mirror of https://github.com/citusdata/citus.git
RECORD: Add support for more expression types
- OpExpr - NullIfExpr - MinMaxExpr - CoalesceExpr - CaseExpr Also fix case where ARRAY[(1,2), NULL] was rejectedpull/3237/head
parent
6d14f63f81
commit
0d04ff1692
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "access/htup_details.h"
|
||||
#include "catalog/pg_class.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "distributed/citus_nodefuncs.h"
|
||||
#include "distributed/citus_nodes.h"
|
||||
|
@ -90,6 +91,7 @@ static PlannedStmt * FinalizeNonRouterPlan(PlannedStmt *localPlan,
|
|||
DistributedPlan *distributedPlan,
|
||||
CustomScan *customScan);
|
||||
static PlannedStmt * FinalizeRouterPlan(PlannedStmt *localPlan, CustomScan *customScan);
|
||||
static int32 BlessRecordExpressionList(List *exprs);
|
||||
static void CheckNodeIsDumpable(Node *node);
|
||||
static Node * CheckNodeCopyAndSerialization(Node *node);
|
||||
static void AdjustReadIntermediateResultCost(RangeTblEntry *rangeTableEntry,
|
||||
|
@ -1224,7 +1226,7 @@ BlessRecordExpression(Expr *expr)
|
|||
{
|
||||
int32 typeMod = -1;
|
||||
|
||||
if (IsA(expr, FuncExpr))
|
||||
if (IsA(expr, FuncExpr) || IsA(expr, OpExpr))
|
||||
{
|
||||
/*
|
||||
* Handle functions that return records on the target
|
||||
|
@ -1236,6 +1238,7 @@ BlessRecordExpression(Expr *expr)
|
|||
/* get_expr_result_type blesses the tuple descriptor */
|
||||
TypeFuncClass typeClass = get_expr_result_type((Node *) expr, &resultTypeId,
|
||||
&resultTupleDesc);
|
||||
|
||||
if (typeClass == TYPEFUNC_COMPOSITE)
|
||||
{
|
||||
typeMod = resultTupleDesc->tdtypmod;
|
||||
|
@ -1285,34 +1288,87 @@ BlessRecordExpression(Expr *expr)
|
|||
{
|
||||
/*
|
||||
* Handle row array expressions, e.g. SELECT ARRAY[(1,2)];
|
||||
* Postgres allows ARRAY[(1,2),(1,2,3)]. We do not.
|
||||
*/
|
||||
ArrayExpr *arrayExpr = (ArrayExpr *) expr;
|
||||
ListCell *elemCell = NULL;
|
||||
|
||||
foreach(elemCell, arrayExpr->elements)
|
||||
typeMod = BlessRecordExpressionList(arrayExpr->elements);
|
||||
}
|
||||
else if (IsA(expr, NullIfExpr))
|
||||
{
|
||||
NullIfExpr *nullIfExpr = (NullIfExpr *) expr;
|
||||
|
||||
typeMod = BlessRecordExpressionList(nullIfExpr->args);
|
||||
}
|
||||
else if (IsA(expr, MinMaxExpr))
|
||||
{
|
||||
MinMaxExpr *minMaxExpr = (MinMaxExpr *) expr;
|
||||
|
||||
typeMod = BlessRecordExpressionList(minMaxExpr->args);
|
||||
}
|
||||
else if (IsA(expr, CoalesceExpr))
|
||||
{
|
||||
CoalesceExpr *coalesceExpr = (CoalesceExpr *) expr;
|
||||
|
||||
typeMod = BlessRecordExpressionList(coalesceExpr->args);
|
||||
}
|
||||
else if (IsA(expr, CaseExpr))
|
||||
{
|
||||
CaseExpr *caseExpr = (CaseExpr *) expr;
|
||||
List *results = NIL;
|
||||
ListCell *whenCell = NULL;
|
||||
|
||||
foreach(whenCell, caseExpr->args)
|
||||
{
|
||||
Node *elemArg = (Node *) lfirst(elemCell);
|
||||
int32 arrayTypeMod = BlessRecordExpression((Expr *) elemArg);
|
||||
CaseWhen *whenArg = (CaseWhen *) lfirst(whenCell);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
results = lappend(results, whenArg->result);
|
||||
}
|
||||
|
||||
if (caseExpr->defresult != NULL)
|
||||
{
|
||||
results = lappend(results, caseExpr->defresult);
|
||||
}
|
||||
|
||||
typeMod = BlessRecordExpressionList(results);
|
||||
}
|
||||
|
||||
return typeMod;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* BlessRecordExpressionList maps BlessRecordExpression over a list.
|
||||
* Returns typmod of all expressions, or -1 if they are not all the same.
|
||||
* Ignores expressions with a typmod of -1.
|
||||
*/
|
||||
static int32
|
||||
BlessRecordExpressionList(List *exprs)
|
||||
{
|
||||
int32 finalTypeMod = -1;
|
||||
ListCell *exprCell = NULL;
|
||||
foreach(exprCell, exprs)
|
||||
{
|
||||
Node *exprArg = (Node *) lfirst(exprCell);
|
||||
int32 exprTypeMod = BlessRecordExpression((Expr *) exprArg);
|
||||
|
||||
if (exprTypeMod == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (finalTypeMod == -1)
|
||||
{
|
||||
finalTypeMod = exprTypeMod;
|
||||
}
|
||||
else if (finalTypeMod != exprTypeMod)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return finalTypeMod;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RemoteScanRangeTableEntry creates a range table entry from given column name
|
||||
* list to represent a remote scan.
|
||||
|
|
|
@ -34,6 +34,19 @@ SELECT create_distributed_function('record_returner(int)');
|
|||
|
||||
(1 row)
|
||||
|
||||
CREATE OR REPLACE FUNCTION identity_returner(x anyelement)
|
||||
RETURNS anyelement
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN x;
|
||||
END;
|
||||
$$ language plpgsql;
|
||||
SELECT create_distributed_function('identity_returner(anyelement)');
|
||||
create_distributed_function
|
||||
-----------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
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;
|
||||
|
@ -54,13 +67,13 @@ 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)\")"}
|
||||
SELECT ARRAY[NULL,(x,(y,x)),NULL,(y,(x,y))] FROM test ORDER BY x, y;
|
||||
array
|
||||
---------------------------------------------
|
||||
{NULL,"(1,\"(2,1)\")",NULL,"(2,\"(1,2)\")"}
|
||||
{NULL,"(1,\"(3,1)\")",NULL,"(3,\"(1,3)\")"}
|
||||
{NULL,"(2,\"(2,2)\")",NULL,"(2,\"(2,2)\")"}
|
||||
{NULL,"(2,\"(3,2)\")",NULL,"(3,\"(2,3)\")"}
|
||||
(4 rows)
|
||||
|
||||
SELECT ARRAY[[(x,(y,x))],[(x,(x,y))]] FROM test ORDER BY x, y;
|
||||
|
@ -99,11 +112,72 @@ SELECT record_returner(x) FROM test ORDER BY x, y;
|
|||
(3,returned)
|
||||
(4 rows)
|
||||
|
||||
-- RECORD[] with varying shape unsupported
|
||||
SELECT NULLIF((x, y), (y, x)) FROM test ORDER BY x, y;
|
||||
nullif
|
||||
--------
|
||||
(1,2)
|
||||
(1,3)
|
||||
|
||||
(2,3)
|
||||
(4 rows)
|
||||
|
||||
SELECT LEAST((x, y), (y, x)) FROM test ORDER BY x, y;
|
||||
least
|
||||
-------
|
||||
(1,2)
|
||||
(1,3)
|
||||
(2,2)
|
||||
(2,3)
|
||||
(4 rows)
|
||||
|
||||
SELECT GREATEST((x, y), (y, x)) FROM test ORDER BY x, y;
|
||||
greatest
|
||||
----------
|
||||
(2,1)
|
||||
(3,1)
|
||||
(2,2)
|
||||
(3,2)
|
||||
(4 rows)
|
||||
|
||||
SELECT COALESCE(NULL, (x, y), (y, x)) FROM test ORDER BY x, y;
|
||||
coalesce
|
||||
----------
|
||||
(1,2)
|
||||
(1,3)
|
||||
(2,2)
|
||||
(2,3)
|
||||
(4 rows)
|
||||
|
||||
SELECT CASE x WHEN 2 THEN (x, y) ELSE (y, x) END FROM test ORDER BY x, y;
|
||||
row
|
||||
-------
|
||||
(2,1)
|
||||
(3,1)
|
||||
(2,2)
|
||||
(2,3)
|
||||
(4 rows)
|
||||
|
||||
SELECT CASE x WHEN 2 THEN (x, y) END FROM test ORDER BY x, y;
|
||||
case
|
||||
-------
|
||||
|
||||
|
||||
(2,2)
|
||||
(2,3)
|
||||
(4 rows)
|
||||
|
||||
-- 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
|
||||
SELECT CASE x WHEN 2 THEN (x, y, x) ELSE (y, x) END FROM test ORDER BY x, y;
|
||||
ERROR: input of anonymous composite types is not implemented
|
||||
-- RECORD from polymorphic types unsupported
|
||||
SELECT identity_returner((x, y)) FROM test ORDER BY x, y;
|
||||
ERROR: input of anonymous composite types is not implemented
|
||||
SELECT array_agg((x, y)) FROM test;
|
||||
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
|
||||
|
@ -119,11 +193,11 @@ 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)\")"}
|
||||
SELECT ARRAY[NULL,(x,(y,x)),NULL,(y,(x,y))] FROM test WHERE x = 1 ORDER BY x, y;
|
||||
array
|
||||
---------------------------------------------
|
||||
{NULL,"(1,\"(2,1)\")",NULL,"(2,\"(1,2)\")"}
|
||||
{NULL,"(1,\"(3,1)\")",NULL,"(3,\"(1,3)\")"}
|
||||
(2 rows)
|
||||
|
||||
SELECT ARRAY[[(x,(y,x))],[(x,(x,y))]] FROM test WHERE x = 1 ORDER BY x, y;
|
||||
|
@ -154,11 +228,60 @@ SELECT record_returner(x) FROM test WHERE x = 1 ORDER BY x, y;
|
|||
(2,returned)
|
||||
(2 rows)
|
||||
|
||||
-- RECORD[] with varying shape unsupported
|
||||
SELECT NULLIF((x, y), (y, x)) FROM test WHERE x = 1 ORDER BY x, y;
|
||||
nullif
|
||||
--------
|
||||
(1,2)
|
||||
(1,3)
|
||||
(2 rows)
|
||||
|
||||
SELECT LEAST((x, y), (y, x)) FROM test WHERE x = 1 ORDER BY x, y;
|
||||
least
|
||||
-------
|
||||
(1,2)
|
||||
(1,3)
|
||||
(2 rows)
|
||||
|
||||
SELECT GREATEST((x, y), (y, x)) FROM test WHERE x = 1 ORDER BY x, y;
|
||||
greatest
|
||||
----------
|
||||
(2,1)
|
||||
(3,1)
|
||||
(2 rows)
|
||||
|
||||
SELECT COALESCE(NULL, (x, y), (y, x)) FROM test WHERE x = 1 ORDER BY x, y;
|
||||
coalesce
|
||||
----------
|
||||
(1,2)
|
||||
(1,3)
|
||||
(2 rows)
|
||||
|
||||
SELECT CASE x WHEN 2 THEN (x, y) ELSE (y, x) END FROM test WHERE x = 1 ORDER BY x, y;
|
||||
row
|
||||
-------
|
||||
(2,1)
|
||||
(3,1)
|
||||
(2 rows)
|
||||
|
||||
SELECT CASE x WHEN 2 THEN (x, y) END FROM test WHERE x = 1 ORDER BY x, y;
|
||||
case
|
||||
------
|
||||
|
||||
|
||||
(2 rows)
|
||||
|
||||
-- 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
|
||||
SELECT CASE x WHEN 2 THEN (x, y, x) ELSE (y, x) END FROM test WHERE x = 1 ORDER BY x, y;
|
||||
ERROR: input of anonymous composite types is not implemented
|
||||
-- RECORD from polymorphic types unsupported
|
||||
SELECT identity_returner((x, y)) FROM test WHERE x = 1 ORDER BY x, y;
|
||||
ERROR: input of anonymous composite types is not implemented
|
||||
SELECT array_agg((x, y)) FROM test WHERE x = 1;
|
||||
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
|
||||
|
@ -221,8 +344,5 @@ EXECUTE rec(1);
|
|||
(1,3)
|
||||
(2 rows)
|
||||
|
||||
SET client_min_messages TO error;
|
||||
DROP SCHEMA row_types CASCADE;
|
||||
NOTICE: drop cascades to 3 other objects
|
||||
DETAIL: drop cascades to table test
|
||||
drop cascades to function table_returner(integer)
|
||||
drop cascades to function record_returner(integer)
|
||||
|
|
|
@ -23,32 +23,61 @@ END;
|
|||
$$ language plpgsql;
|
||||
SELECT create_distributed_function('record_returner(int)');
|
||||
|
||||
CREATE OR REPLACE FUNCTION identity_returner(x anyelement)
|
||||
RETURNS anyelement
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN x;
|
||||
END;
|
||||
$$ language plpgsql;
|
||||
SELECT create_distributed_function('identity_returner(anyelement)');
|
||||
|
||||
|
||||
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[NULL,(x,(y,x)),NULL,(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 NULLIF((x, y), (y, x)) FROM test ORDER BY x, y;
|
||||
SELECT LEAST((x, y), (y, x)) FROM test ORDER BY x, y;
|
||||
SELECT GREATEST((x, y), (y, x)) FROM test ORDER BY x, y;
|
||||
SELECT COALESCE(NULL, (x, y), (y, x)) FROM test ORDER BY x, y;
|
||||
SELECT CASE x WHEN 2 THEN (x, y) ELSE (y, x) END FROM test ORDER BY x, y;
|
||||
SELECT CASE x WHEN 2 THEN (x, y) END FROM test ORDER BY x, y;
|
||||
-- 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;
|
||||
SELECT CASE x WHEN 2 THEN (x, y, x) ELSE (y, x) END FROM test ORDER BY x, y;
|
||||
-- RECORD from polymorphic types unsupported
|
||||
SELECT identity_returner((x, y)) FROM test ORDER BY x, y;
|
||||
SELECT array_agg((x, y)) FROM test;
|
||||
|
||||
-- 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[NULL,(x,(y,x)),NULL,(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 NULLIF((x, y), (y, x)) FROM test WHERE x = 1 ORDER BY x, y;
|
||||
SELECT LEAST((x, y), (y, x)) FROM test WHERE x = 1 ORDER BY x, y;
|
||||
SELECT GREATEST((x, y), (y, x)) FROM test WHERE x = 1 ORDER BY x, y;
|
||||
SELECT COALESCE(NULL, (x, y), (y, x)) FROM test WHERE x = 1 ORDER BY x, y;
|
||||
SELECT CASE x WHEN 2 THEN (x, y) ELSE (y, x) END FROM test WHERE x = 1 ORDER BY x, y;
|
||||
SELECT CASE x WHEN 2 THEN (x, y) END FROM test WHERE x = 1 ORDER BY x, y;
|
||||
-- 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;
|
||||
SELECT CASE x WHEN 2 THEN (x, y, x) ELSE (y, x) END FROM test WHERE x = 1 ORDER BY x, y;
|
||||
-- RECORD from polymorphic types unsupported
|
||||
SELECT identity_returner((x, y)) FROM test WHERE x = 1 ORDER BY x, y;
|
||||
SELECT array_agg((x, y)) FROM test WHERE x = 1;
|
||||
|
||||
-- nested row expressions
|
||||
SELECT (x,(x,y)) AS foo FROM test WHERE x = 1 ORDER BY x, y;
|
||||
|
@ -66,4 +95,5 @@ EXECUTE rec(1);
|
|||
EXECUTE rec(1);
|
||||
EXECUTE rec(1);
|
||||
|
||||
SET client_min_messages TO error;
|
||||
DROP SCHEMA row_types CASCADE;
|
||||
|
|
Loading…
Reference in New Issue