summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/executor/nodeAgg.c2
-rw-r--r--src/backend/executor/nodeWindowAgg.c19
-rw-r--r--src/backend/optimizer/path/pathkeys.c213
-rw-r--r--src/backend/utils/adt/pg_locale.c21
-rw-r--r--src/backend/utils/adt/pg_locale_builtin.c7
-rw-r--r--src/backend/utils/adt/pg_locale_icu.c7
-rw-r--r--src/backend/utils/adt/pg_locale_libc.c23
-rw-r--r--src/bin/pg_dump/pg_dump.c8
-rw-r--r--src/bin/pg_dump/pg_dump_sort.c3
-rw-r--r--src/include/lib/simplehash.h4
-rw-r--r--src/include/nodes/memnodes.h2
-rw-r--r--src/include/utils/pg_locale.h3
-rw-r--r--src/test/regress/expected/constraints.out4
-rw-r--r--src/test/regress/sql/constraints.sql5
14 files changed, 178 insertions, 143 deletions
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index a4f3d30f307..64643c3943a 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -2911,7 +2911,7 @@ agg_retrieve_hash_table_in_memory(AggState *aggstate)
perhash = &aggstate->perhash[aggstate->current_set];
- ResetTupleHashIterator(hashtable, &perhash->hashiter);
+ ResetTupleHashIterator(perhash->hashtable, &perhash->hashiter);
continue;
}
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 47e00be7b49..aa145d4e1a9 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -3270,8 +3270,9 @@ window_gettupleslot(WindowObject winobj, int64 pos, TupleTableSlot *slot)
return true;
}
-/*
- * get tuple and evaluate in partition
+/* gettuple_eval_partition
+ * get tuple in a patition and evaluate the window function's argument
+ * expression on it.
*/
static Datum
gettuple_eval_partition(WindowObject winobj, int argno,
@@ -3790,9 +3791,15 @@ WinGetFuncArgInPartition(WindowObject winobj, int argno,
continue; /* keep on moving forward or backward */
else /* need to check NULL or not */
{
- /* get tuple and evaluate in partition */
- datum = gettuple_eval_partition(winobj, argno,
- abs_pos, isnull, &myisout);
+ /*
+ * NOT NULL info does not exist yet. Get tuple and evaluate func
+ * arg in partition. We ignore the return value from
+ * gettuple_eval_partition because we are just interested in
+ * whether we are inside or outside of partition, NULL or NOT
+ * NULL.
+ */
+ (void) gettuple_eval_partition(winobj, argno,
+ abs_pos, isnull, &myisout);
if (myisout) /* out of partition? */
break;
if (!*isnull)
@@ -3802,7 +3809,7 @@ WinGetFuncArgInPartition(WindowObject winobj, int argno,
}
} while (notnull_offset < notnull_relpos);
- /* get tuple and evaluate in partition */
+ /* get tuple and evaluate func arg in partition */
datum = gettuple_eval_partition(winobj, argno,
abs_pos, isnull, &myisout);
if (!myisout && set_mark)
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 879dcb4608e..139fa1f875a 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -2147,174 +2147,126 @@ right_merge_direction(PlannerInfo *root, PathKey *pathkey)
}
/*
- * pathkeys_useful_for_ordering
- * Count the number of pathkeys that are useful for meeting the
- * query's requested output ordering.
- *
- * Because we the have the possibility of incremental sort, a prefix list of
- * keys is potentially useful for improving the performance of the requested
- * ordering. Thus we return 0, if no valuable keys are found, or the number
- * of leading keys shared by the list and the requested ordering.
+ * count_common_leading_pathkeys_ordered
+ * Returns the number of leading pathkeys which both lists have in common
*/
static int
-pathkeys_useful_for_ordering(PlannerInfo *root, List *pathkeys)
+count_common_leading_pathkeys_ordered(List *keys1, List *keys2)
{
- int n_common_pathkeys;
+ int ncommon;
- (void) pathkeys_count_contained_in(root->sort_pathkeys, pathkeys,
- &n_common_pathkeys);
+ (void) pathkeys_count_contained_in(keys1, keys2, &ncommon);
- return n_common_pathkeys;
+ return ncommon;
}
/*
- * pathkeys_useful_for_windowing
- * Count the number of pathkeys that are useful for meeting the
- * query's desired sort order for window function evaluation.
+ * count_common_leading_pathkeys_unordered
+ * Returns the number of leading PathKeys in 'keys2' which exist in
+ * 'keys1'.
*/
static int
-pathkeys_useful_for_windowing(PlannerInfo *root, List *pathkeys)
+count_common_leading_pathkeys_unordered(List *keys1, List *keys2)
{
- int n_common_pathkeys;
+ int ncommon = 0;
- (void) pathkeys_count_contained_in(root->window_pathkeys,
- pathkeys,
- &n_common_pathkeys);
-
- return n_common_pathkeys;
-}
-
-/*
- * pathkeys_useful_for_grouping
- * Count the number of pathkeys that are useful for grouping (instead of
- * explicit sort)
- *
- * Group pathkeys could be reordered to benefit from the ordering. The
- * ordering may not be "complete" and may require incremental sort, but that's
- * fine. So we simply count prefix pathkeys with a matching group key, and
- * stop once we find the first pathkey without a match.
- *
- * So e.g. with pathkeys (a,b,c) and group keys (a,b,e) this determines (a,b)
- * pathkeys are useful for grouping, and we might do incremental sort to get
- * path ordered by (a,b,e).
- *
- * This logic is necessary to retain paths with ordering not matching grouping
- * keys directly, without the reordering.
- *
- * Returns the length of pathkey prefix with matching group keys.
- */
-static int
-pathkeys_useful_for_grouping(PlannerInfo *root, List *pathkeys)
-{
- ListCell *key;
- int n = 0;
-
- /* no special ordering requested for grouping */
- if (root->group_pathkeys == NIL)
+ /* No point in searching keys2 when keys1 is empty */
+ if (keys1 == NIL)
return 0;
- /* walk the pathkeys and search for matching group key */
- foreach(key, pathkeys)
+ /* walk keys2 and search for matching PathKeys in keys1 */
+ foreach_node(PathKey, pathkey, keys2)
{
- PathKey *pathkey = (PathKey *) lfirst(key);
-
- /* no matching group key, we're done */
- if (!list_member_ptr(root->group_pathkeys, pathkey))
+ /*
+ * return the number of matches so far as soon as keys1 doesn't
+ * contain the given keys2 key.
+ */
+ if (!list_member_ptr(keys1, pathkey))
break;
- n++;
+ ncommon++;
}
- return n;
+ return ncommon;
}
/*
- * pathkeys_useful_for_distinct
- * Count the number of pathkeys that are useful for DISTINCT or DISTINCT
- * ON clause.
- *
- * DISTINCT keys could be reordered to benefit from the given pathkey list. As
- * with pathkeys_useful_for_grouping, we return the number of leading keys in
- * the list that are shared by the distinctClause pathkeys.
+ * truncate_useless_pathkeys
+ * Shorten the given PathKey List to just the useful PathKeys. If all
+ * PathKeys are useful, return the input List, otherwise return a new
+ * List containing only the useful PathKeys.
*/
-static int
-pathkeys_useful_for_distinct(PlannerInfo *root, List *pathkeys)
+List *
+truncate_useless_pathkeys(PlannerInfo *root,
+ RelOptInfo *rel,
+ List *pathkeys)
{
- int n_common_pathkeys;
+ int nuseful;
+ int nuseful2;
+ int ntotal = list_length(pathkeys);
/*
- * distinct_pathkeys may have become empty if all of the pathkeys were
- * determined to be redundant. Return 0 in this case.
+ * Here we determine how many items in 'pathkeys' might be useful for
+ * various Path sort ordering requirements the planner has. Operations
+ * such as ORDER BY require a Path's pathkeys to match the PathKeys of the
+ * ORDER BY in the same order, however operations such as GROUP BY and
+ * DISTINCT are less critical as a Unique or GroupAggregate only need to
+ * care that all PathKeys exist in their subpath, and don't need to care
+ * if they're in the same order as the clause in the query.
*/
- if (root->distinct_pathkeys == NIL)
- return 0;
+ nuseful = count_common_leading_pathkeys_ordered(root->sort_pathkeys,
+ pathkeys);
- /* walk the pathkeys and search for matching DISTINCT key */
- n_common_pathkeys = 0;
- foreach_node(PathKey, pathkey, pathkeys)
- {
- /* no matching DISTINCT key, we're done */
- if (!list_member_ptr(root->distinct_pathkeys, pathkey))
- break;
+ /* Short-circuit at any point we discover *all* pathkeys are useful */
+ if (nuseful == ntotal)
+ return pathkeys;
- n_common_pathkeys++;
- }
+ nuseful2 = count_common_leading_pathkeys_ordered(root->window_pathkeys,
+ pathkeys);
+ if (nuseful2 == ntotal)
+ return pathkeys;
- return n_common_pathkeys;
-}
+ nuseful = Max(nuseful, nuseful2);
+ nuseful2 = count_common_leading_pathkeys_ordered(root->setop_pathkeys,
+ pathkeys);
+ if (nuseful2 == ntotal)
+ return pathkeys;
-/*
- * pathkeys_useful_for_setop
- * Count the number of leading common pathkeys root's 'setop_pathkeys' in
- * 'pathkeys'.
- */
-static int
-pathkeys_useful_for_setop(PlannerInfo *root, List *pathkeys)
-{
- int n_common_pathkeys;
+ nuseful = Max(nuseful, nuseful2);
- (void) pathkeys_count_contained_in(root->setop_pathkeys, pathkeys,
- &n_common_pathkeys);
+ /*
+ * Check if these pathkeys are useful for GROUP BY or DISTINCT. The order
+ * of the pathkeys does not matter here as Unique and GroupAggregate for
+ * these operations can take advantage of Paths presorted by any of the
+ * GROUP BY/DISTINCT pathkeys.
+ */
+ nuseful2 = count_common_leading_pathkeys_unordered(root->group_pathkeys,
+ pathkeys);
+ if (nuseful2 == ntotal)
+ return pathkeys;
- return n_common_pathkeys;
-}
+ nuseful = Max(nuseful, nuseful2);
+ nuseful2 = count_common_leading_pathkeys_unordered(root->distinct_pathkeys,
+ pathkeys);
-/*
- * truncate_useless_pathkeys
- * Shorten the given pathkey list to just the useful pathkeys.
- */
-List *
-truncate_useless_pathkeys(PlannerInfo *root,
- RelOptInfo *rel,
- List *pathkeys)
-{
- int nuseful;
- int nuseful2;
+ if (nuseful2 == ntotal)
+ return pathkeys;
- nuseful = pathkeys_useful_for_merging(root, rel, pathkeys);
- nuseful2 = pathkeys_useful_for_ordering(root, pathkeys);
- if (nuseful2 > nuseful)
- nuseful = nuseful2;
- nuseful2 = pathkeys_useful_for_windowing(root, pathkeys);
- if (nuseful2 > nuseful)
- nuseful = nuseful2;
- nuseful2 = pathkeys_useful_for_grouping(root, pathkeys);
- if (nuseful2 > nuseful)
- nuseful = nuseful2;
- nuseful2 = pathkeys_useful_for_distinct(root, pathkeys);
- if (nuseful2 > nuseful)
- nuseful = nuseful2;
- nuseful2 = pathkeys_useful_for_setop(root, pathkeys);
- if (nuseful2 > nuseful)
- nuseful = nuseful2;
+ nuseful = Max(nuseful, nuseful2);
+
+ /*
+ * Finally, check how many PathKeys might be useful for Merge Joins. This
+ * is a bit more expensive, so do it last and only if we've not figured
+ * out that all the pathkeys are useful already.
+ */
+ nuseful2 = pathkeys_useful_for_merging(root, rel, pathkeys);
+ nuseful = Max(nuseful, nuseful2);
/*
* Note: not safe to modify input list destructively, but we can avoid
* copying the list if we're not actually going to change it
*/
- if (nuseful == 0)
- return NIL;
- else if (nuseful == list_length(pathkeys))
+ if (nuseful == ntotal)
return pathkeys;
else
return list_copy_head(pathkeys, nuseful);
@@ -2340,9 +2292,8 @@ has_useful_pathkeys(PlannerInfo *root, RelOptInfo *rel)
{
if (rel->joininfo != NIL || rel->has_eclass_joins)
return true; /* might be able to use pathkeys for merging */
- if (root->group_pathkeys != NIL)
- return true; /* might be able to use pathkeys for grouping */
if (root->query_pathkeys != NIL)
- return true; /* might be able to use them for ordering */
+ return true; /* the upper planner might need them */
+
return false; /* definitely useless */
}
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 50b25445f7a..00d1e031472 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -1163,6 +1163,15 @@ init_database_collation(void)
}
/*
+ * Get database default locale.
+ */
+pg_locale_t
+pg_database_locale(void)
+{
+ return pg_newlocale_from_collation(DEFAULT_COLLATION_OID);
+}
+
+/*
* Create a pg_locale_t from a collation OID. Results are cached for the
* lifetime of the backend. Thus, do not free the result with freelocale().
*
@@ -1493,6 +1502,18 @@ pg_iswspace(pg_wchar wc, pg_locale_t locale)
return locale->ctype->wc_isspace(wc, locale);
}
+bool
+pg_iswxdigit(pg_wchar wc, pg_locale_t locale)
+{
+ if (locale->ctype == NULL)
+ return (wc <= (pg_wchar) 127 &&
+ ((pg_char_properties[wc] & PG_ISDIGIT) ||
+ ((wc >= 'A' && wc <= 'F') ||
+ (wc >= 'a' && wc <= 'f'))));
+ else
+ return locale->ctype->wc_isxdigit(wc, locale);
+}
+
pg_wchar
pg_towupper(pg_wchar wc, pg_locale_t locale)
{
diff --git a/src/backend/utils/adt/pg_locale_builtin.c b/src/backend/utils/adt/pg_locale_builtin.c
index 526ab3c6711..3dc611b50e1 100644
--- a/src/backend/utils/adt/pg_locale_builtin.c
+++ b/src/backend/utils/adt/pg_locale_builtin.c
@@ -164,6 +164,12 @@ wc_isspace_builtin(pg_wchar wc, pg_locale_t locale)
}
static bool
+wc_isxdigit_builtin(pg_wchar wc, pg_locale_t locale)
+{
+ return pg_u_isxdigit(wc, !locale->builtin.casemap_full);
+}
+
+static bool
char_is_cased_builtin(char ch, pg_locale_t locale)
{
return IS_HIGHBIT_SET(ch) ||
@@ -196,6 +202,7 @@ static const struct ctype_methods ctype_methods_builtin = {
.wc_isprint = wc_isprint_builtin,
.wc_ispunct = wc_ispunct_builtin,
.wc_isspace = wc_isspace_builtin,
+ .wc_isxdigit = wc_isxdigit_builtin,
.char_is_cased = char_is_cased_builtin,
.wc_tolower = wc_tolower_builtin,
.wc_toupper = wc_toupper_builtin,
diff --git a/src/backend/utils/adt/pg_locale_icu.c b/src/backend/utils/adt/pg_locale_icu.c
index 9f0b4eead73..05bad202669 100644
--- a/src/backend/utils/adt/pg_locale_icu.c
+++ b/src/backend/utils/adt/pg_locale_icu.c
@@ -212,6 +212,12 @@ wc_isspace_icu(pg_wchar wc, pg_locale_t locale)
return u_isspace(wc);
}
+static bool
+wc_isxdigit_icu(pg_wchar wc, pg_locale_t locale)
+{
+ return u_isxdigit(wc);
+}
+
static const struct ctype_methods ctype_methods_icu = {
.strlower = strlower_icu,
.strtitle = strtitle_icu,
@@ -226,6 +232,7 @@ static const struct ctype_methods ctype_methods_icu = {
.wc_isprint = wc_isprint_icu,
.wc_ispunct = wc_ispunct_icu,
.wc_isspace = wc_isspace_icu,
+ .wc_isxdigit = wc_isxdigit_icu,
.char_is_cased = char_is_cased_icu,
.wc_toupper = toupper_icu,
.wc_tolower = tolower_icu,
diff --git a/src/backend/utils/adt/pg_locale_libc.c b/src/backend/utils/adt/pg_locale_libc.c
index f56b5dbdd37..34865ccf00e 100644
--- a/src/backend/utils/adt/pg_locale_libc.c
+++ b/src/backend/utils/adt/pg_locale_libc.c
@@ -173,6 +173,16 @@ wc_isspace_libc_sb(pg_wchar wc, pg_locale_t locale)
}
static bool
+wc_isxdigit_libc_sb(pg_wchar wc, pg_locale_t locale)
+{
+#ifndef WIN32
+ return isxdigit_l((unsigned char) wc, locale->lt);
+#else
+ return _isxdigit_l((unsigned char) wc, locale->lt);
+#endif
+}
+
+static bool
wc_isdigit_libc_mb(pg_wchar wc, pg_locale_t locale)
{
return iswdigit_l((wint_t) wc, locale->lt);
@@ -226,6 +236,16 @@ wc_isspace_libc_mb(pg_wchar wc, pg_locale_t locale)
return iswspace_l((wint_t) wc, locale->lt);
}
+static bool
+wc_isxdigit_libc_mb(pg_wchar wc, pg_locale_t locale)
+{
+#ifndef WIN32
+ return iswxdigit_l((wint_t) wc, locale->lt);
+#else
+ return _iswxdigit_l((wint_t) wc, locale->lt);
+#endif
+}
+
static char
char_tolower_libc(unsigned char ch, pg_locale_t locale)
{
@@ -313,6 +333,7 @@ static const struct ctype_methods ctype_methods_libc_sb = {
.wc_isprint = wc_isprint_libc_sb,
.wc_ispunct = wc_ispunct_libc_sb,
.wc_isspace = wc_isspace_libc_sb,
+ .wc_isxdigit = wc_isxdigit_libc_sb,
.char_is_cased = char_is_cased_libc,
.char_tolower = char_tolower_libc,
.wc_toupper = toupper_libc_sb,
@@ -337,6 +358,7 @@ static const struct ctype_methods ctype_methods_libc_other_mb = {
.wc_isprint = wc_isprint_libc_sb,
.wc_ispunct = wc_ispunct_libc_sb,
.wc_isspace = wc_isspace_libc_sb,
+ .wc_isxdigit = wc_isxdigit_libc_sb,
.char_is_cased = char_is_cased_libc,
.char_tolower = char_tolower_libc,
.wc_toupper = toupper_libc_sb,
@@ -357,6 +379,7 @@ static const struct ctype_methods ctype_methods_libc_utf8 = {
.wc_isprint = wc_isprint_libc_mb,
.wc_ispunct = wc_ispunct_libc_mb,
.wc_isspace = wc_isspace_libc_mb,
+ .wc_isxdigit = wc_isxdigit_libc_mb,
.char_is_cased = char_is_cased_libc,
.char_tolower = char_tolower_libc,
.wc_toupper = toupper_libc_mb,
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 890db7b08c2..4b8cd49df09 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -9351,8 +9351,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
*
* We track in notnull_islocal whether the constraint was defined directly
* in this table or via an ancestor, for binary upgrade. flagInhAttrs
- * might modify this later; that routine is also in charge of determining
- * the correct inhcount.
+ * might modify this later.
*/
if (fout->remoteVersion >= 180000)
appendPQExpBufferStr(q,
@@ -9369,7 +9368,10 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
"NULL AS notnull_comment,\n"
"NULL AS notnull_invalidoid,\n"
"false AS notnull_noinherit,\n"
- "a.attislocal AS notnull_islocal,\n");
+ "CASE WHEN a.attislocal THEN true\n"
+ " WHEN a.attnotnull AND NOT a.attislocal THEN true\n"
+ " ELSE false\n"
+ "END AS notnull_islocal,\n");
if (fout->remoteVersion >= 140000)
appendPQExpBufferStr(q,
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 2d02456664b..164c76e0864 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -385,7 +385,8 @@ DOTypeNameCompare(const void *p1, const void *p2)
if (cmpval != 0)
return cmpval;
}
- else if (obj1->objType == DO_CONSTRAINT)
+ else if (obj1->objType == DO_CONSTRAINT ||
+ obj1->objType == DO_FK_CONSTRAINT)
{
ConstraintInfo *robj1 = *(ConstraintInfo *const *) p1;
ConstraintInfo *robj2 = *(ConstraintInfo *const *) p2;
diff --git a/src/include/lib/simplehash.h b/src/include/lib/simplehash.h
index 327274c2340..9622131ede6 100644
--- a/src/include/lib/simplehash.h
+++ b/src/include/lib/simplehash.h
@@ -1044,6 +1044,10 @@ SH_START_ITERATE_AT(SH_TYPE * tb, SH_ITERATOR * iter, uint32 at)
SH_SCOPE SH_ELEMENT_TYPE *
SH_ITERATE(SH_TYPE * tb, SH_ITERATOR * iter)
{
+ /* validate sanity of the given iterator */
+ Assert(iter->cur < tb->size);
+ Assert(iter->end < tb->size);
+
while (!iter->done)
{
SH_ELEMENT_TYPE *elem;
diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h
index 5807ef805bd..f8a78095eb9 100644
--- a/src/include/nodes/memnodes.h
+++ b/src/include/nodes/memnodes.h
@@ -120,7 +120,7 @@ typedef struct MemoryContextData
NodeTag type; /* identifies exact kind of context */
/* these two fields are placed here to minimize alignment wastage: */
- bool isReset; /* T = no space alloced since last reset */
+ bool isReset; /* T = no space allocated since last reset */
bool allowInCritSection; /* allow palloc in critical section */
Size mem_allocated; /* track memory allocated for this context */
const MemoryContextMethods *methods; /* virtual function table */
diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h
index 009f5334a87..86c48c34f26 100644
--- a/src/include/utils/pg_locale.h
+++ b/src/include/utils/pg_locale.h
@@ -110,6 +110,7 @@ struct ctype_methods
bool (*wc_isprint) (pg_wchar wc, pg_locale_t locale);
bool (*wc_ispunct) (pg_wchar wc, pg_locale_t locale);
bool (*wc_isspace) (pg_wchar wc, pg_locale_t locale);
+ bool (*wc_isxdigit) (pg_wchar wc, pg_locale_t locale);
pg_wchar (*wc_toupper) (pg_wchar wc, pg_locale_t locale);
pg_wchar (*wc_tolower) (pg_wchar wc, pg_locale_t locale);
@@ -175,6 +176,7 @@ struct pg_locale_struct
};
extern void init_database_collation(void);
+extern pg_locale_t pg_database_locale(void);
extern pg_locale_t pg_newlocale_from_collation(Oid collid);
extern char *get_collation_actual_version(char collprovider, const char *collcollate);
@@ -217,6 +219,7 @@ extern bool pg_iswgraph(pg_wchar wc, pg_locale_t locale);
extern bool pg_iswprint(pg_wchar wc, pg_locale_t locale);
extern bool pg_iswpunct(pg_wchar wc, pg_locale_t locale);
extern bool pg_iswspace(pg_wchar wc, pg_locale_t locale);
+extern bool pg_iswxdigit(pg_wchar wc, pg_locale_t locale);
extern pg_wchar pg_towupper(pg_wchar wc, pg_locale_t locale);
extern pg_wchar pg_towlower(pg_wchar wc, pg_locale_t locale);
diff --git a/src/test/regress/expected/constraints.out b/src/test/regress/expected/constraints.out
index 3590d3274f0..dda67798cb3 100644
--- a/src/test/regress/expected/constraints.out
+++ b/src/test/regress/expected/constraints.out
@@ -1705,3 +1705,7 @@ DROP ROLE regress_constraint_comments_noaccess;
CREATE DOMAIN constraint_comments_dom AS int;
ALTER DOMAIN constraint_comments_dom ADD CONSTRAINT inv_ck CHECK (value > 0) NOT VALID;
COMMENT ON CONSTRAINT inv_ck ON DOMAIN constraint_comments_dom IS 'comment on invalid constraint';
+-- Create a table that exercises pg_upgrade
+CREATE TABLE regress_notnull1 (a integer);
+CREATE TABLE regress_notnull2 () INHERITS (regress_notnull1);
+ALTER TABLE ONLY regress_notnull2 ALTER COLUMN a SET NOT NULL;
diff --git a/src/test/regress/sql/constraints.sql b/src/test/regress/sql/constraints.sql
index 1f6dc8fd69f..0a6290bc571 100644
--- a/src/test/regress/sql/constraints.sql
+++ b/src/test/regress/sql/constraints.sql
@@ -1049,3 +1049,8 @@ CREATE DOMAIN constraint_comments_dom AS int;
ALTER DOMAIN constraint_comments_dom ADD CONSTRAINT inv_ck CHECK (value > 0) NOT VALID;
COMMENT ON CONSTRAINT inv_ck ON DOMAIN constraint_comments_dom IS 'comment on invalid constraint';
+
+-- Create a table that exercises pg_upgrade
+CREATE TABLE regress_notnull1 (a integer);
+CREATE TABLE regress_notnull2 () INHERITS (regress_notnull1);
+ALTER TABLE ONLY regress_notnull2 ALTER COLUMN a SET NOT NULL;