diff --git a/doc/src/sgml/brin.sgml b/doc/src/sgml/brin.sgml index 4ee8908b65a..71697155d7c 100644 --- a/doc/src/sgml/brin.sgml +++ b/doc/src/sgml/brin.sgml @@ -75,7 +75,7 @@ summarized will cause the summary information to be updated with data from the new tuples. When a new page is created that does not fall within the last - summarized range, the range that the new page belongs into + summarized range, the range that the new page belongs to does not automatically acquire a summary tuple; those tuples remain unsummarized until a summarization run is invoked later, creating the initial summary for that range. diff --git a/doc/src/sgml/ref/drop_extension.sgml b/doc/src/sgml/ref/drop_extension.sgml index c01ddace84c..dcc52c2ced0 100644 --- a/doc/src/sgml/ref/drop_extension.sgml +++ b/doc/src/sgml/ref/drop_extension.sgml @@ -32,7 +32,8 @@ DROP EXTENSION [ IF EXISTS ] name [ DROP EXTENSION removes extensions from the database. Dropping an extension causes its component objects, and other explicitly dependent routines (see , - the depends on extension action), to be dropped as well. + the DEPENDS ON EXTENSION extension_name + action), to be dropped as well. @@ -80,8 +81,8 @@ DROP EXTENSION [ IF EXISTS ] name [ This option prevents the specified extensions from being dropped - if there exists non-extension-member objects that depends on any - the extensions. This is the default. + if there exist non-extension-member objects that depend on any + of the extensions. This is the default. diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 8e2b9230cb3..6c45d349524 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -754,6 +754,12 @@ expression_returns_set_walker(Node *node, void *context) /* else fall through to check args */ } + /* + * If you add any more cases that return sets, also fix + * expression_returns_set_rows() in clauses.c and IS_SRF_CALL() in + * tlist.c. + */ + /* Avoid recursion for some cases that parser checks not to return a set */ if (IsA(node, Aggref)) return false; diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 547b2550917..6a941c662c7 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -1005,7 +1005,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel, * one are effectively checking properties of targetexpr, so there's * no point in asking whether some other EC member would be better.) */ - if (IS_SRF_CALL((Node *) em->em_expr)) + if (expression_returns_set((Node *) em->em_expr)) continue; /* @@ -1033,7 +1033,7 @@ relation_can_be_sorted_early(PlannerInfo *root, RelOptInfo *rel, * member in this case; since SRFs can't appear in WHERE, they cannot * belong to multi-member ECs.) */ - if (IS_SRF_CALL((Node *) em->em_expr)) + if (expression_returns_set((Node *) em->em_expr)) return false; return true; diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index 9d0f3274dbe..bb16a8d2c38 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -32,6 +32,17 @@ typedef struct maxSortGroupRef_context static bool maxSortGroupRef_walker(Node *node, maxSortGroupRef_context *cxt); +/* + * Test if an expression node represents a SRF call. Beware multiple eval! + * + * Please note that this is only meant for use in split_pathtarget_at_srfs(); + * if you use it anywhere else, your code is almost certainly wrong for SRFs + * nested within expressions. Use expression_returns_set() instead. + */ +#define IS_SRF_CALL(node) \ + ((IsA(node, FuncExpr) && ((FuncExpr *) (node))->funcretset) || \ + (IsA(node, OpExpr) && ((OpExpr *) (node))->opretset)) + /* * Data structures for split_pathtarget_at_srfs(). To preserve the identity * of sortgroupref items even if they are textually equal(), what we track is diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 26cd7458961..6d00be72b16 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -6802,7 +6802,7 @@ trim_array(PG_FUNCTION_ARGS) { ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); int n = PG_GETARG_INT32(1); - int array_length = ARR_DIMS(v)[0]; + int array_length = (ARR_NDIM(v) > 0) ? ARR_DIMS(v)[0] : 0; int16 elmlen; bool elmbyval; char elmalign; @@ -6822,8 +6822,11 @@ trim_array(PG_FUNCTION_ARGS) /* Set all the bounds as unprovided except the first upper bound */ memset(lowerProvided, false, sizeof(lowerProvided)); memset(upperProvided, false, sizeof(upperProvided)); - upper[0] = ARR_LBOUND(v)[0] + array_length - n - 1; - upperProvided[0] = true; + if (ARR_NDIM(v) > 0) + { + upper[0] = ARR_LBOUND(v)[0] + array_length - n - 1; + upperProvided[0] = true; + } /* Fetch the needed information about the element type */ get_typlenbyvalalign(ARR_ELEMTYPE(v), &elmlen, &elmbyval, &elmalign); diff --git a/src/include/optimizer/optimizer.h b/src/include/optimizer/optimizer.h index f8400206288..c2553891186 100644 --- a/src/include/optimizer/optimizer.h +++ b/src/include/optimizer/optimizer.h @@ -25,11 +25,6 @@ #include "nodes/parsenodes.h" #include "nodes/plannodes.h" -/* Test if an expression node represents a SRF call. Beware multiple eval! */ -#define IS_SRF_CALL(node) \ - ((IsA(node, FuncExpr) && ((FuncExpr *) (node))->funcretset) || \ - (IsA(node, OpExpr) && ((OpExpr *) (node))->opretset)) - /* * We don't want to include nodes/pathnodes.h here, because non-planner * code should generally treat PlannerInfo as an opaque typedef. diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out index a8686405408..d6a8f8f8545 100644 --- a/src/test/regress/expected/arrays.out +++ b/src/test/regress/expected/arrays.out @@ -2586,3 +2586,5 @@ SELECT trim_array(ARRAY[1, 2, 3], -1); -- fail ERROR: number of elements to trim must be between 0 and 3 SELECT trim_array(ARRAY[1, 2, 3], 10); -- fail ERROR: number of elements to trim must be between 0 and 3 +SELECT trim_array(ARRAY[]::int[], 1); -- fail +ERROR: number of elements to trim must be between 0 and 0 diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out index 4de01f4f632..62c4b4f99c2 100644 --- a/src/test/regress/expected/select_parallel.out +++ b/src/test/regress/expected/select_parallel.out @@ -1217,6 +1217,30 @@ SELECT generate_series(1, two), array(select generate_series(1, two)) Optimizer: Postgres query optimizer (18 rows) +-- must disallow pushing sort below gather when pathkey contains an SRF +EXPLAIN (VERBOSE, COSTS OFF) +SELECT unnest(ARRAY[]::integer[]) + 1 AS pathkey + FROM tenk1 t1 JOIN tenk1 t2 ON TRUE + ORDER BY pathkey; + QUERY PLAN +----------------------------------------------------------------------------------------------------- + Sort + Output: (((unnest('{}'::integer[])) + 1)) + Sort Key: (((unnest('{}'::integer[])) + 1)) + -> Result + Output: ((unnest('{}'::integer[])) + 1) + -> ProjectSet + Output: unnest('{}'::integer[]) + -> Nested Loop + -> Gather + Workers Planned: 4 + -> Parallel Index Only Scan using tenk1_hundred on public.tenk1 t1 + -> Materialize + -> Gather + Workers Planned: 4 + -> Parallel Index Only Scan using tenk1_hundred on public.tenk1 t2 +(15 rows) + -- test passing expanded-value representations to workers CREATE FUNCTION make_some_array(int,int) returns int[] as $$declare x int[]; diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql index d10278e32f0..cefe5b05b3b 100644 --- a/src/test/regress/sql/arrays.sql +++ b/src/test/regress/sql/arrays.sql @@ -875,3 +875,4 @@ FROM SELECT trim_array(ARRAY[1, 2, 3], -1); -- fail SELECT trim_array(ARRAY[1, 2, 3], 10); -- fail +SELECT trim_array(ARRAY[]::int[], 1); -- fail diff --git a/src/test/regress/sql/incremental_sort.sql b/src/test/regress/sql/incremental_sort.sql index afd1dab2045..648eced7e14 100644 --- a/src/test/regress/sql/incremental_sort.sql +++ b/src/test/regress/sql/incremental_sort.sql @@ -294,5 +294,3 @@ from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub; explain (costs off) select sub.unique1, stringu1 || random()::text from tenk1, lateral (select tenk1.unique1 from generate_series(1, 1000)) as sub order by 1, 2; --- Disallow pushing down sort when pathkey is an SRF. -explain (costs off) select unique1 from tenk1 order by unnest('{1,2}'::int[]); diff --git a/src/test/regress/sql/select_parallel.sql b/src/test/regress/sql/select_parallel.sql index 846066fad05..4a22a001ef9 100644 --- a/src/test/regress/sql/select_parallel.sql +++ b/src/test/regress/sql/select_parallel.sql @@ -443,6 +443,12 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT generate_series(1, two), array(select generate_series(1, two)) FROM tenk1 ORDER BY tenthous; +-- must disallow pushing sort below gather when pathkey contains an SRF +EXPLAIN (VERBOSE, COSTS OFF) +SELECT unnest(ARRAY[]::integer[]) + 1 AS pathkey + FROM tenk1 t1 JOIN tenk1 t2 ON TRUE + ORDER BY pathkey; + -- test passing expanded-value representations to workers CREATE FUNCTION make_some_array(int,int) returns int[] as $$declare x int[];