mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Support for deparsing of ArrayCoerceExpr node in contrib/postgres_fdw
When using a prepared statement to select data from a PostgreSQL foreign table (postgres_fdw) with the "field = ANY($1)" expression, the operation is not pushed down when an implicit type case is applied, and a generic plan is used. This commit resolves the issue by supporting the push-down of ArrayCoerceExpr, which is used in this case. The support is quite straightforward and similar to other nods, such as RelabelType. Discussion: https://postgr.es/m/4f0cea802476d23c6e799512ffd17aff%40postgrespro.ru Author: Alexander Pyhalov <a.pyhalov@postgrespro.ru> Reviewed-by: Maxim Orlov <orlovmg@gmail.com> Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
This commit is contained in:
@ -161,6 +161,7 @@ static void deparseDistinctExpr(DistinctExpr *node, deparse_expr_cxt *context);
|
|||||||
static void deparseScalarArrayOpExpr(ScalarArrayOpExpr *node,
|
static void deparseScalarArrayOpExpr(ScalarArrayOpExpr *node,
|
||||||
deparse_expr_cxt *context);
|
deparse_expr_cxt *context);
|
||||||
static void deparseRelabelType(RelabelType *node, deparse_expr_cxt *context);
|
static void deparseRelabelType(RelabelType *node, deparse_expr_cxt *context);
|
||||||
|
static void deparseArrayCoerceExpr(ArrayCoerceExpr *node, deparse_expr_cxt *context);
|
||||||
static void deparseBoolExpr(BoolExpr *node, deparse_expr_cxt *context);
|
static void deparseBoolExpr(BoolExpr *node, deparse_expr_cxt *context);
|
||||||
static void deparseNullTest(NullTest *node, deparse_expr_cxt *context);
|
static void deparseNullTest(NullTest *node, deparse_expr_cxt *context);
|
||||||
static void deparseCaseExpr(CaseExpr *node, deparse_expr_cxt *context);
|
static void deparseCaseExpr(CaseExpr *node, deparse_expr_cxt *context);
|
||||||
@ -702,6 +703,34 @@ foreign_expr_walker(Node *node,
|
|||||||
state = FDW_COLLATE_UNSAFE;
|
state = FDW_COLLATE_UNSAFE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case T_ArrayCoerceExpr:
|
||||||
|
{
|
||||||
|
ArrayCoerceExpr *e = (ArrayCoerceExpr *) node;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Recurse to input subexpression.
|
||||||
|
*/
|
||||||
|
if (!foreign_expr_walker((Node *) e->arg,
|
||||||
|
glob_cxt, &inner_cxt, case_arg_cxt))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* T_ArrayCoerceExpr must not introduce a collation not
|
||||||
|
* derived from an input foreign Var (same logic as for a
|
||||||
|
* function).
|
||||||
|
*/
|
||||||
|
collation = e->resultcollid;
|
||||||
|
if (collation == InvalidOid)
|
||||||
|
state = FDW_COLLATE_NONE;
|
||||||
|
else if (inner_cxt.state == FDW_COLLATE_SAFE &&
|
||||||
|
collation == inner_cxt.collation)
|
||||||
|
state = FDW_COLLATE_SAFE;
|
||||||
|
else if (collation == DEFAULT_COLLATION_OID)
|
||||||
|
state = FDW_COLLATE_NONE;
|
||||||
|
else
|
||||||
|
state = FDW_COLLATE_UNSAFE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case T_BoolExpr:
|
case T_BoolExpr:
|
||||||
{
|
{
|
||||||
BoolExpr *b = (BoolExpr *) node;
|
BoolExpr *b = (BoolExpr *) node;
|
||||||
@ -2919,6 +2948,9 @@ deparseExpr(Expr *node, deparse_expr_cxt *context)
|
|||||||
case T_RelabelType:
|
case T_RelabelType:
|
||||||
deparseRelabelType((RelabelType *) node, context);
|
deparseRelabelType((RelabelType *) node, context);
|
||||||
break;
|
break;
|
||||||
|
case T_ArrayCoerceExpr:
|
||||||
|
deparseArrayCoerceExpr((ArrayCoerceExpr *) node, context);
|
||||||
|
break;
|
||||||
case T_BoolExpr:
|
case T_BoolExpr:
|
||||||
deparseBoolExpr((BoolExpr *) node, context);
|
deparseBoolExpr((BoolExpr *) node, context);
|
||||||
break;
|
break;
|
||||||
@ -3507,6 +3539,24 @@ deparseRelabelType(RelabelType *node, deparse_expr_cxt *context)
|
|||||||
node->resulttypmod));
|
node->resulttypmod));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Deparse a ArrayCoerceExpr (array-type conversion) node.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
deparseArrayCoerceExpr(ArrayCoerceExpr *node, deparse_expr_cxt *context)
|
||||||
|
{
|
||||||
|
deparseExpr(node->arg, context);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* No difference how to deparse explicit cast, but if we omit implicit
|
||||||
|
* cast in the query, it'll be more user-friendly
|
||||||
|
*/
|
||||||
|
if (node->coerceformat != COERCE_IMPLICIT_CAST)
|
||||||
|
appendStringInfo(context->buf, "::%s",
|
||||||
|
deparse_type_name(node->resulttype,
|
||||||
|
node->resulttypmod));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deparse a BoolExpr node.
|
* Deparse a BoolExpr node.
|
||||||
*/
|
*/
|
||||||
|
@ -1180,6 +1180,27 @@ SELECT * FROM ft1 WHERE CASE c3 COLLATE "C" WHEN c6 THEN true ELSE c3 < 'bar' EN
|
|||||||
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1"
|
Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1"
|
||||||
(4 rows)
|
(4 rows)
|
||||||
|
|
||||||
|
-- Test array type conversion pushdown
|
||||||
|
SET plan_cache_mode = force_generic_plan;
|
||||||
|
PREPARE s(varchar[]) AS SELECT count(*) FROM ft2 WHERE c6 = ANY ($1);
|
||||||
|
EXPLAIN (VERBOSE, COSTS OFF)
|
||||||
|
EXECUTE s(ARRAY['1','2']);
|
||||||
|
QUERY PLAN
|
||||||
|
---------------------------------------------------------------------------------------------
|
||||||
|
Foreign Scan
|
||||||
|
Output: (count(*))
|
||||||
|
Relations: Aggregate on (public.ft2)
|
||||||
|
Remote SQL: SELECT count(*) FROM "S 1"."T 1" WHERE ((c6 = ANY ($1::character varying[])))
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
EXECUTE s(ARRAY['1','2']);
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
200
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DEALLOCATE s;
|
||||||
|
RESET plan_cache_mode;
|
||||||
-- a regconfig constant referring to this text search configuration
|
-- a regconfig constant referring to this text search configuration
|
||||||
-- is initially unshippable
|
-- is initially unshippable
|
||||||
CREATE TEXT SEARCH CONFIGURATION public.custom_search
|
CREATE TEXT SEARCH CONFIGURATION public.custom_search
|
||||||
|
@ -458,6 +458,15 @@ SELECT * FROM ft1 WHERE CASE c3 WHEN c6 THEN true ELSE c3 < 'bar' END;
|
|||||||
EXPLAIN (VERBOSE, COSTS OFF)
|
EXPLAIN (VERBOSE, COSTS OFF)
|
||||||
SELECT * FROM ft1 WHERE CASE c3 COLLATE "C" WHEN c6 THEN true ELSE c3 < 'bar' END;
|
SELECT * FROM ft1 WHERE CASE c3 COLLATE "C" WHEN c6 THEN true ELSE c3 < 'bar' END;
|
||||||
|
|
||||||
|
-- Test array type conversion pushdown
|
||||||
|
SET plan_cache_mode = force_generic_plan;
|
||||||
|
PREPARE s(varchar[]) AS SELECT count(*) FROM ft2 WHERE c6 = ANY ($1);
|
||||||
|
EXPLAIN (VERBOSE, COSTS OFF)
|
||||||
|
EXECUTE s(ARRAY['1','2']);
|
||||||
|
EXECUTE s(ARRAY['1','2']);
|
||||||
|
DEALLOCATE s;
|
||||||
|
RESET plan_cache_mode;
|
||||||
|
|
||||||
-- a regconfig constant referring to this text search configuration
|
-- a regconfig constant referring to this text search configuration
|
||||||
-- is initially unshippable
|
-- is initially unshippable
|
||||||
CREATE TEXT SEARCH CONFIGURATION public.custom_search
|
CREATE TEXT SEARCH CONFIGURATION public.custom_search
|
||||||
|
Reference in New Issue
Block a user