From 168e11cc9b3b2f46566cae61e6647ca148d1f024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= Date: Wed, 20 Nov 2019 23:42:50 +0000 Subject: [PATCH] Implement support for RECORD[] where we support RECORD Support for ARRAY[] expressions is limited to having a consistent shape, eg ARRAY[(int,text),(int,text)] as opposed to ARRAY[(int,text),(float,text)] or ARRAY[(int,text),(int,text,float)] --- .../distributed/planner/distributed_planner.c | 31 +++++++++++- .../planner/multi_logical_optimizer.c | 2 +- .../planner/multi_master_planner.c | 2 +- src/test/regress/expected/row_types.out | 48 +++++++++++++++++-- src/test/regress/sql/row_types.sql | 16 +++++-- 5 files changed, 89 insertions(+), 10 deletions(-) diff --git a/src/backend/distributed/planner/distributed_planner.c b/src/backend/distributed/planner/distributed_planner.c index 822a9954e..5d3d8a36f 100644 --- a/src/backend/distributed/planner/distributed_planner.c +++ b/src/backend/distributed/planner/distributed_planner.c @@ -1169,7 +1169,7 @@ FinalizeRouterPlan(PlannedStmt *localPlan, CustomScan *customScan) /* build target entry pointing to remote scan range table entry */ Var *newVar = makeVarFromTargetEntry(customScanRangeTableIndex, targetEntry); - if (newVar->vartype == RECORDOID) + if (newVar->vartype == RECORDOID || newVar->vartype == RECORDARRAYOID) { /* * Add the anonymous composite type to the type cache and store @@ -1263,7 +1263,7 @@ BlessRecordExpression(Expr *expr) Oid rowArgTypeId = exprType(rowArg); int rowArgTypeMod = exprTypmod(rowArg); - if (rowArgTypeId == RECORDOID) + if (rowArgTypeId == RECORDOID || rowArgTypeId == RECORDARRAYOID) { /* ensure nested rows are blessed as well */ rowArgTypeMod = BlessRecordExpression((Expr *) rowArg); @@ -1281,6 +1281,33 @@ BlessRecordExpression(Expr *expr) typeMod = rowTupleDesc->tdtypmod; } + else if (IsA(expr, ArrayExpr)) + { + /* + * Handle row array expressions, e.g. SELECT ARRAY[(1,2)]; + */ + ArrayExpr *arrayExpr = (ArrayExpr *) expr; + ListCell *elemCell = NULL; + + foreach(elemCell, arrayExpr->elements) + { + Node *elemArg = (Node *) lfirst(elemCell); + int32 arrayTypeMod = BlessRecordExpression((Expr *) elemArg); + + /* + * Postgres allows ARRAY[(1,2),(1,2,3)]. We do not. + * If multiple RECORD shapes exist in an ArrayExpr, bail out. + */ + if (typeMod == -1) + { + typeMod = arrayTypeMod; + } + else if (arrayTypeMod != typeMod) + { + return -1; + } + } + } return typeMod; } diff --git a/src/backend/distributed/planner/multi_logical_optimizer.c b/src/backend/distributed/planner/multi_logical_optimizer.c index 56404556e..74a580510 100644 --- a/src/backend/distributed/planner/multi_logical_optimizer.c +++ b/src/backend/distributed/planner/multi_logical_optimizer.c @@ -1402,7 +1402,7 @@ MasterExtendedOpNode(MultiExtendedOp *originalOpNode, column->varoattno = walkerContext->columnId; walkerContext->columnId++; - if (column->vartype == RECORDOID) + if (column->vartype == RECORDOID || column->vartype == RECORDARRAYOID) { column->vartypmod = BlessRecordExpression(originalTargetEntry->expr); } diff --git a/src/backend/distributed/planner/multi_master_planner.c b/src/backend/distributed/planner/multi_master_planner.c index 852584c6d..74874648c 100644 --- a/src/backend/distributed/planner/multi_master_planner.c +++ b/src/backend/distributed/planner/multi_master_planner.c @@ -110,7 +110,7 @@ MasterTargetList(List *workerTargetList) masterColumn->varoattno = columnId; columnId++; - if (masterColumn->vartype == RECORDOID) + if (masterColumn->vartype == RECORDOID || masterColumn->vartype == RECORDARRAYOID) { masterColumn->vartypmod = BlessRecordExpression(workerTargetEntry->expr); } diff --git a/src/test/regress/expected/row_types.out b/src/test/regress/expected/row_types.out index d8b257a33..a9e31c866 100644 --- a/src/test/regress/expected/row_types.out +++ b/src/test/regress/expected/row_types.out @@ -11,7 +11,7 @@ SELECT create_distributed_table('test','x'); CREATE OR REPLACE FUNCTION table_returner(INT) RETURNS TABLE(name text, id INT) AS $$ BEGIN - RETURN QUERY SELECT $1::text, $1; + RETURN QUERY SELECT $1::text, $1; END; $$ language plpgsql; SELECT create_distributed_function('table_returner(int)'); @@ -24,8 +24,8 @@ CREATE OR REPLACE FUNCTION record_returner(INOUT id int, OUT name text) RETURNS record AS $$ BEGIN - id := id + 1; - name := 'returned'; + id := id + 1; + name := 'returned'; END; $$ language plpgsql; SELECT create_distributed_function('record_returner(int)'); @@ -54,6 +54,24 @@ SELECT (x,y) FROM test GROUP BY x, y ORDER BY x, y; (2,3) (4 rows) +SELECT ARRAY[(x,(y,x)),(y,(x,y))] FROM test ORDER BY x, y; + array +----------------------------------- + {"(1,\"(2,1)\")","(2,\"(1,2)\")"} + {"(1,\"(3,1)\")","(3,\"(1,3)\")"} + {"(2,\"(2,2)\")","(2,\"(2,2)\")"} + {"(2,\"(3,2)\")","(3,\"(2,3)\")"} +(4 rows) + +SELECT ARRAY[[(x,(y,x))],[(x,(x,y))]] FROM test ORDER BY x, y; + array +--------------------------------------- + {{"(1,\"(2,1)\")"},{"(1,\"(1,2)\")"}} + {{"(1,\"(3,1)\")"},{"(1,\"(1,3)\")"}} + {{"(2,\"(2,2)\")"},{"(2,\"(2,2)\")"}} + {{"(2,\"(3,2)\")"},{"(2,\"(2,3)\")"}} +(4 rows) + select distinct (x,y) AS foo, x, y FROM test ORDER BY x, y; foo | x | y -------+---+--- @@ -81,6 +99,11 @@ SELECT record_returner(x) FROM test ORDER BY x, y; (3,returned) (4 rows) +-- RECORD[] with varying shape unsupported +SELECT ARRAY[(x,(y,x),y),(y,(x,y))] FROM test ORDER BY x, y; +ERROR: input of anonymous composite types is not implemented +SELECT ARRAY[[(x,(y,x))],[((x,x),y)]] FROM test ORDER BY x, y; +ERROR: input of anonymous composite types is not implemented -- router queries support row types SELECT (x,y) FROM test WHERE x = 1 ORDER BY x, y; row @@ -96,6 +119,20 @@ SELECT (x,y) AS foo FROM test WHERE x = 1 ORDER BY x, y; (1,3) (2 rows) +SELECT ARRAY[(x,(y,x)),(y,(x,y))] FROM test WHERE x = 1 ORDER BY x, y; + array +----------------------------------- + {"(1,\"(2,1)\")","(2,\"(1,2)\")"} + {"(1,\"(3,1)\")","(3,\"(1,3)\")"} +(2 rows) + +SELECT ARRAY[[(x,(y,x))],[(x,(x,y))]] FROM test WHERE x = 1 ORDER BY x, y; + array +--------------------------------------- + {{"(1,\"(2,1)\")"},{"(1,\"(1,2)\")"}} + {{"(1,\"(3,1)\")"},{"(1,\"(1,3)\")"}} +(2 rows) + select distinct (x,y) AS foo, x, y FROM test WHERE x = 1 ORDER BY x, y; foo | x | y -------+---+--- @@ -117,6 +154,11 @@ SELECT record_returner(x) FROM test WHERE x = 1 ORDER BY x, y; (2,returned) (2 rows) +-- RECORD[] with varying shape unsupported +SELECT ARRAY[(x,(y,x),y),(y,(x,y))] FROM test WHERE x = 1 ORDER BY x, y; +ERROR: input of anonymous composite types is not implemented +SELECT ARRAY[[(x,(y,x))],[((x,x),y)]] FROM test WHERE x = 1 ORDER BY x, y; +ERROR: input of anonymous composite types is not implemented -- nested row expressions SELECT (x,(x,y)) AS foo FROM test WHERE x = 1 ORDER BY x, y; foo diff --git a/src/test/regress/sql/row_types.sql b/src/test/regress/sql/row_types.sql index 43fd2a9e5..59899f54f 100644 --- a/src/test/regress/sql/row_types.sql +++ b/src/test/regress/sql/row_types.sql @@ -8,7 +8,7 @@ SELECT create_distributed_table('test','x'); CREATE OR REPLACE FUNCTION table_returner(INT) RETURNS TABLE(name text, id INT) AS $$ BEGIN - RETURN QUERY SELECT $1::text, $1; + RETURN QUERY SELECT $1::text, $1; END; $$ language plpgsql; SELECT create_distributed_function('table_returner(int)'); @@ -17,8 +17,8 @@ CREATE OR REPLACE FUNCTION record_returner(INOUT id int, OUT name text) RETURNS record AS $$ BEGIN - id := id + 1; - name := 'returned'; + id := id + 1; + name := 'returned'; END; $$ language plpgsql; SELECT create_distributed_function('record_returner(int)'); @@ -29,16 +29,26 @@ INSERT INTO test VALUES (1,2), (1,3), (2,2), (2,3); -- multi-shard queries support row types SELECT (x,y) FROM test ORDER BY x, y; SELECT (x,y) FROM test GROUP BY x, y ORDER BY x, y; +SELECT ARRAY[(x,(y,x)),(y,(x,y))] FROM test ORDER BY x, y; +SELECT ARRAY[[(x,(y,x))],[(x,(x,y))]] FROM test ORDER BY x, y; select distinct (x,y) AS foo, x, y FROM test ORDER BY x, y; SELECT table_returner(x) FROM test ORDER BY x, y; SELECT record_returner(x) FROM test ORDER BY x, y; +-- RECORD[] with varying shape unsupported +SELECT ARRAY[(x,(y,x),y),(y,(x,y))] FROM test ORDER BY x, y; +SELECT ARRAY[[(x,(y,x))],[((x,x),y)]] FROM test ORDER BY x, y; -- router queries support row types SELECT (x,y) FROM test WHERE x = 1 ORDER BY x, y; SELECT (x,y) AS foo FROM test WHERE x = 1 ORDER BY x, y; +SELECT ARRAY[(x,(y,x)),(y,(x,y))] FROM test WHERE x = 1 ORDER BY x, y; +SELECT ARRAY[[(x,(y,x))],[(x,(x,y))]] FROM test WHERE x = 1 ORDER BY x, y; select distinct (x,y) AS foo, x, y FROM test WHERE x = 1 ORDER BY x, y; SELECT table_returner(x) FROM test WHERE x = 1 ORDER BY x, y; SELECT record_returner(x) FROM test WHERE x = 1 ORDER BY x, y; +-- RECORD[] with varying shape unsupported +SELECT ARRAY[(x,(y,x),y),(y,(x,y))] FROM test WHERE x = 1 ORDER BY x, y; +SELECT ARRAY[[(x,(y,x))],[((x,x),y)]] FROM test WHERE x = 1 ORDER BY x, y; -- nested row expressions SELECT (x,(x,y)) AS foo FROM test WHERE x = 1 ORDER BY x, y;