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[];