diff --git a/doc/src/sgml/backup.sgml b/doc/src/sgml/backup.sgml
index 3c8aaed0b6203..21094c6a9d056 100644
--- a/doc/src/sgml/backup.sgml
+++ b/doc/src/sgml/backup.sgml
@@ -1520,11 +1520,11 @@ tar -rf /var/lib/pgsql/backup.tar /var/lib/pgsql/archive/
If archive storage size is a concern, you can use
gzip to compress the archive files:
-archive_command = 'gzip < %p > /var/lib/pgsql/archive/%f'
+archive_command = 'gzip < %p > /mnt/server/archivedir/%f.gz'
You will then need to use gunzip during recovery:
-restore_command = 'gunzip < /mnt/server/archivedir/%f > %p'
+restore_command = 'gunzip < /mnt/server/archivedir/%f.gz > %p'
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c5048a199886c..08f08322ca51b 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -26238,14 +26238,14 @@ postgres=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup());
- pg_collation_current_version
+ pg_collation_actual_version
- pg_collation_current_version ( oid )
+ pg_collation_actual_version ( oid )
text
- Returns the version of the collation object as reported by the ICU
- library or operating system. null is returned
+ Returns the actual version of the collation object as it is currently
+ installed in the operating system. null is returned
on operating systems where PostgreSQL
doesn't have support for versions.
diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c
index a0453b36cde5b..a276eb020b5dd 100644
--- a/src/backend/access/gin/ginvacuum.c
+++ b/src/backend/access/gin/ginvacuum.c
@@ -231,6 +231,7 @@ ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkn
END_CRIT_SECTION();
+ gvs->result->pages_newly_deleted++;
gvs->result->pages_deleted++;
}
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c
index ddecb8ab18ef4..0663193531a73 100644
--- a/src/backend/access/gist/gistvacuum.c
+++ b/src/backend/access/gist/gistvacuum.c
@@ -133,9 +133,21 @@ gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
MemoryContext oldctx;
/*
- * Reset counts that will be incremented during the scan; needed in case
- * of multiple scans during a single VACUUM command.
+ * Reset fields that track information about the entire index now. This
+ * avoids double-counting in the case where a single VACUUM command
+ * requires multiple scans of the index.
+ *
+ * Avoid resetting the tuples_removed and pages_newly_deleted fields here,
+ * since they track information about the VACUUM command, and so must last
+ * across each call to gistvacuumscan().
+ *
+ * (Note that pages_free is treated as state about the whole index, not
+ * the current VACUUM. This is appropriate because RecordFreeIndexPage()
+ * calls are idempotent, and get repeated for the same deleted pages in
+ * some scenarios. The point for us is to track the number of recyclable
+ * pages in the index at the end of the VACUUM command.)
*/
+ stats->num_pages = 0;
stats->estimated_count = false;
stats->num_index_tuples = 0;
stats->pages_deleted = 0;
@@ -281,8 +293,8 @@ gistvacuumpage(GistVacState *vstate, BlockNumber blkno, BlockNumber orig_blkno)
{
/* Okay to recycle this page */
RecordFreeIndexPage(rel, blkno);
- vstate->stats->pages_free++;
vstate->stats->pages_deleted++;
+ vstate->stats->pages_free++;
}
else if (GistPageIsDeleted(page))
{
@@ -636,6 +648,7 @@ gistdeletepage(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
/* mark the page as deleted */
MarkBufferDirty(leafBuffer);
GistPageSetDeleted(leafPage, txid);
+ stats->pages_newly_deleted++;
stats->pages_deleted++;
/* remove the downlink from the parent */
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 0bb78162f546d..d8f847b0e6673 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -2521,9 +2521,11 @@ lazy_cleanup_index(Relation indrel,
(*stats)->num_index_tuples,
(*stats)->num_pages),
errdetail("%.0f index row versions were removed.\n"
- "%u index pages have been deleted, %u are currently reusable.\n"
+ "%u index pages were newly deleted.\n"
+ "%u index pages are currently deleted, of which %u are currently reusable.\n"
"%s.",
(*stats)->tuples_removed,
+ (*stats)->pages_newly_deleted,
(*stats)->pages_deleted, (*stats)->pages_free,
pg_rusage_show(&ru0))));
}
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index a43805a7b09e1..629a23628ef6c 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -50,7 +50,7 @@ static bool _bt_mark_page_halfdead(Relation rel, Buffer leafbuf,
static bool _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf,
BlockNumber scanblkno,
bool *rightsib_empty,
- uint32 *ndeleted);
+ BTVacState *vstate);
static bool _bt_lock_subtree_parent(Relation rel, BlockNumber child,
BTStack stack,
Buffer *subtreeparent,
@@ -1760,20 +1760,22 @@ _bt_rightsib_halfdeadflag(Relation rel, BlockNumber leafrightsib)
* should never pass a buffer containing an existing deleted page here. The
* lock and pin on caller's buffer will be dropped before we return.
*
- * Returns the number of pages successfully deleted (zero if page cannot
- * be deleted now; could be more than one if parent or right sibling pages
- * were deleted too). Note that this does not include pages that we delete
- * that the btvacuumscan scan has yet to reach; they'll get counted later
- * instead.
+ * Maintains bulk delete stats for caller, which are taken from vstate. We
+ * need to cooperate closely with caller here so that whole VACUUM operation
+ * reliably avoids any double counting of subsidiary-to-leafbuf pages that we
+ * delete in passing. If such pages happen to be from a block number that is
+ * ahead of the current scanblkno position, then caller is expected to count
+ * them directly later on. It's simpler for us to understand caller's
+ * requirements than it would be for caller to understand when or how a
+ * deleted page became deleted after the fact.
*
* NOTE: this leaks memory. Rather than trying to clean up everything
* carefully, it's better to run it in a temp context that can be reset
* frequently.
*/
-uint32
-_bt_pagedel(Relation rel, Buffer leafbuf)
+void
+_bt_pagedel(Relation rel, Buffer leafbuf, BTVacState *vstate)
{
- uint32 ndeleted = 0;
BlockNumber rightsib;
bool rightsib_empty;
Page page;
@@ -1781,7 +1783,8 @@ _bt_pagedel(Relation rel, Buffer leafbuf)
/*
* Save original leafbuf block number from caller. Only deleted blocks
- * that are <= scanblkno get counted in ndeleted return value.
+ * that are <= scanblkno are added to bulk delete stat's pages_deleted
+ * count.
*/
BlockNumber scanblkno = BufferGetBlockNumber(leafbuf);
@@ -1843,7 +1846,7 @@ _bt_pagedel(Relation rel, Buffer leafbuf)
RelationGetRelationName(rel))));
_bt_relbuf(rel, leafbuf);
- return ndeleted;
+ return;
}
/*
@@ -1873,7 +1876,7 @@ _bt_pagedel(Relation rel, Buffer leafbuf)
Assert(!P_ISHALFDEAD(opaque));
_bt_relbuf(rel, leafbuf);
- return ndeleted;
+ return;
}
/*
@@ -1922,8 +1925,7 @@ _bt_pagedel(Relation rel, Buffer leafbuf)
if (_bt_leftsib_splitflag(rel, leftsib, leafblkno))
{
ReleaseBuffer(leafbuf);
- Assert(ndeleted == 0);
- return ndeleted;
+ return;
}
/* we need an insertion scan key for the search, so build one */
@@ -1964,7 +1966,7 @@ _bt_pagedel(Relation rel, Buffer leafbuf)
if (!_bt_mark_page_halfdead(rel, leafbuf, stack))
{
_bt_relbuf(rel, leafbuf);
- return ndeleted;
+ return;
}
}
@@ -1979,7 +1981,7 @@ _bt_pagedel(Relation rel, Buffer leafbuf)
{
/* Check for interrupts in _bt_unlink_halfdead_page */
if (!_bt_unlink_halfdead_page(rel, leafbuf, scanblkno,
- &rightsib_empty, &ndeleted))
+ &rightsib_empty, vstate))
{
/*
* _bt_unlink_halfdead_page should never fail, since we
@@ -1990,7 +1992,7 @@ _bt_pagedel(Relation rel, Buffer leafbuf)
* lock and pin on leafbuf for us.
*/
Assert(false);
- return ndeleted;
+ return;
}
}
@@ -2026,8 +2028,6 @@ _bt_pagedel(Relation rel, Buffer leafbuf)
leafbuf = _bt_getbuf(rel, rightsib, BT_WRITE);
}
-
- return ndeleted;
}
/*
@@ -2262,9 +2262,10 @@ _bt_mark_page_halfdead(Relation rel, Buffer leafbuf, BTStack stack)
*/
static bool
_bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, BlockNumber scanblkno,
- bool *rightsib_empty, uint32 *ndeleted)
+ bool *rightsib_empty, BTVacState *vstate)
{
BlockNumber leafblkno = BufferGetBlockNumber(leafbuf);
+ IndexBulkDeleteResult *stats = vstate->stats;
BlockNumber leafleftsib;
BlockNumber leafrightsib;
BlockNumber target;
@@ -2674,12 +2675,17 @@ _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, BlockNumber scanblkno,
_bt_relbuf(rel, buf);
/*
- * If btvacuumscan won't revisit this page in a future btvacuumpage call
- * and count it as deleted then, we count it as deleted by current
- * btvacuumpage call
+ * Maintain pages_newly_deleted, which is simply the number of pages
+ * deleted by the ongoing VACUUM operation.
+ *
+ * Maintain pages_deleted in a way that takes into account how
+ * btvacuumpage() will count deleted pages that have yet to become
+ * scanblkno -- only count page when it's not going to get that treatment
+ * later on.
*/
+ stats->pages_newly_deleted++;
if (target <= scanblkno)
- (*ndeleted)++;
+ stats->pages_deleted++;
return true;
}
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 3b2e0aa5cb794..504f5bef17a43 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -38,17 +38,6 @@
#include "utils/memutils.h"
-/* Working state needed by btvacuumpage */
-typedef struct
-{
- IndexVacuumInfo *info;
- IndexBulkDeleteResult *stats;
- IndexBulkDeleteCallback callback;
- void *callback_state;
- BTCycleId cycleid;
- MemoryContext pagedelcontext;
-} BTVacState;
-
/*
* BTPARALLEL_NOT_INITIALIZED indicates that the scan has not started.
*
@@ -1016,9 +1005,9 @@ btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
* avoids double-counting in the case where a single VACUUM command
* requires multiple scans of the index.
*
- * Avoid resetting the tuples_removed field here, since it tracks
- * information about the VACUUM command, and so must last across each call
- * to btvacuumscan().
+ * Avoid resetting the tuples_removed and pages_newly_deleted fields here,
+ * since they track information about the VACUUM command, and so must last
+ * across each call to btvacuumscan().
*
* (Note that pages_free is treated as state about the whole index, not
* the current VACUUM. This is appropriate because RecordFreeIndexPage()
@@ -1237,11 +1226,13 @@ btvacuumpage(BTVacState *vstate, BlockNumber scanblkno)
}
else if (P_ISHALFDEAD(opaque))
{
+ /* Half-dead leaf page (from interrupted VACUUM) -- finish deleting */
+ attempt_pagedel = true;
+
/*
- * Half-dead leaf page. Try to delete now. Might update
- * pages_deleted below.
+ * _bt_pagedel() will increment both pages_newly_deleted and
+ * pages_deleted stats in all cases (barring corruption)
*/
- attempt_pagedel = true;
}
else if (P_ISLEAF(opaque))
{
@@ -1451,12 +1442,12 @@ btvacuumpage(BTVacState *vstate, BlockNumber scanblkno)
oldcontext = MemoryContextSwitchTo(vstate->pagedelcontext);
/*
- * We trust the _bt_pagedel return value because it does not include
- * any page that a future call here from btvacuumscan is expected to
- * count. There will be no double-counting.
+ * _bt_pagedel maintains the bulk delete stats on our behalf;
+ * pages_newly_deleted and pages_deleted are likely to be incremented
+ * during call
*/
Assert(blkno == scanblkno);
- stats->pages_deleted += _bt_pagedel(rel, buf);
+ _bt_pagedel(rel, buf, vstate);
MemoryContextSwitchTo(oldcontext);
/* pagedel released buffer, so we shouldn't */
diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c
index 0d02a02222e9e..a9ffca5183bd2 100644
--- a/src/backend/access/spgist/spgvacuum.c
+++ b/src/backend/access/spgist/spgvacuum.c
@@ -891,6 +891,7 @@ spgvacuumscan(spgBulkDeleteState *bds)
/* Report final stats */
bds->stats->num_pages = num_pages;
+ bds->stats->pages_newly_deleted = bds->stats->pages_deleted;
bds->stats->pages_free = bds->stats->pages_deleted;
}
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index 4b76a6051d234..a7ee452e192eb 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -268,7 +268,7 @@ IsThereCollationInNamespace(const char *collname, Oid nspOid)
}
Datum
-pg_collation_current_version(PG_FUNCTION_ARGS)
+pg_collation_actual_version(PG_FUNCTION_ARGS)
{
Oid collid = PG_GETARG_OID(0);
char *version;
diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c
index f4f7041ead09d..f46d63d45131a 100644
--- a/src/backend/parser/parse_cte.c
+++ b/src/backend/parser/parse_cte.c
@@ -730,15 +730,15 @@ makeDependencyGraphWalker(Node *node, CteState *cstate)
* In the non-RECURSIVE case, query names are visible to the
* WITH items after them and to the main query.
*/
- ListCell *cell1;
-
cstate->innerwiths = lcons(NIL, cstate->innerwiths);
- cell1 = list_head(cstate->innerwiths);
foreach(lc, stmt->withClause->ctes)
{
CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
+ ListCell *cell1;
(void) makeDependencyGraphWalker(cte->ctequery, cstate);
+ /* note that recursion could mutate innerwiths list */
+ cell1 = list_head(cstate->innerwiths);
lfirst(cell1) = lappend((List *) lfirst(cell1), cte);
}
(void) raw_expression_tree_walker(node,
@@ -1006,15 +1006,15 @@ checkWellFormedRecursionWalker(Node *node, CteState *cstate)
* In the non-RECURSIVE case, query names are visible to the
* WITH items after them and to the main query.
*/
- ListCell *cell1;
-
cstate->innerwiths = lcons(NIL, cstate->innerwiths);
- cell1 = list_head(cstate->innerwiths);
foreach(lc, stmt->withClause->ctes)
{
CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
+ ListCell *cell1;
(void) checkWellFormedRecursionWalker(cte->ctequery, cstate);
+ /* note that recursion could mutate innerwiths list */
+ cell1 = list_head(cstate->innerwiths);
lfirst(cell1) = lappend((List *) lfirst(cell1), cte);
}
checkWellFormedSelectStmt(stmt, cstate);
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index df1f36132d38a..aa4874163f88e 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -127,8 +127,8 @@ static char *IsoLocaleName(const char *); /* MSVC specific */
static void icu_set_collation_attributes(UCollator *collator, const char *loc);
#endif
-static char *get_collation_current_version(char collprovider,
- const char *collcollate);
+static char *get_collation_actual_version(char collprovider,
+ const char *collcollate);
/*
* pg_perm_setlocale
@@ -1610,7 +1610,7 @@ pg_newlocale_from_collation(Oid collid)
* the operating system/library.
*/
static char *
-get_collation_current_version(char collprovider, const char *collcollate)
+get_collation_actual_version(char collprovider, const char *collcollate)
{
char *collversion = NULL;
@@ -1717,8 +1717,8 @@ get_collation_version_for_oid(Oid oid, bool missing_ok)
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
dbform = (Form_pg_database) GETSTRUCT(tp);
- version = get_collation_current_version(COLLPROVIDER_LIBC,
- NameStr(dbform->datcollate));
+ version = get_collation_actual_version(COLLPROVIDER_LIBC,
+ NameStr(dbform->datcollate));
}
else
{
@@ -1732,8 +1732,8 @@ get_collation_version_for_oid(Oid oid, bool missing_ok)
elog(ERROR, "cache lookup failed for collation %u", oid);
}
collform = (Form_pg_collation) GETSTRUCT(tp);
- version = get_collation_current_version(collform->collprovider,
- NameStr(collform->collcollate));
+ version = get_collation_actual_version(collform->collprovider,
+ NameStr(collform->collcollate));
}
ReleaseSysCache(tp);
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index ffa1a4c80dbe1..4515401869fb4 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -63,8 +63,11 @@ typedef struct IndexVacuumInfo
* of which this is just the first field; this provides a way for ambulkdelete
* to communicate additional private data to amvacuumcleanup.
*
- * Note: pages_deleted and pages_free refer to free space within the index
- * file. Some index AMs may compute num_index_tuples by reference to
+ * Note: pages_newly_deleted is the number of pages in the index that were
+ * deleted by the current vacuum operation. pages_deleted and pages_free
+ * refer to free space within the index file.
+ *
+ * Note: Some index AMs may compute num_index_tuples by reference to
* num_heap_tuples, in which case they should copy the estimated_count field
* from IndexVacuumInfo.
*/
@@ -74,7 +77,8 @@ typedef struct IndexBulkDeleteResult
bool estimated_count; /* num_index_tuples is an estimate */
double num_index_tuples; /* tuples remaining */
double tuples_removed; /* # removed during vacuum operation */
- BlockNumber pages_deleted; /* # unused pages in index */
+ BlockNumber pages_newly_deleted; /* # pages marked deleted by us */
+ BlockNumber pages_deleted; /* # pages marked deleted (could be by us) */
BlockNumber pages_free; /* # pages available for reuse */
} IndexBulkDeleteResult;
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 9ac90d7439836..b56b7b7868eb4 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -312,6 +312,20 @@ BTPageIsRecyclable(Page page)
return false;
}
+/*
+ * BTVacState is private nbtree.c state used during VACUUM. It is exported
+ * for use by page deletion related code in nbtpage.c.
+ */
+typedef struct BTVacState
+{
+ IndexVacuumInfo *info;
+ IndexBulkDeleteResult *stats;
+ IndexBulkDeleteCallback callback;
+ void *callback_state;
+ BTCycleId cycleid;
+ MemoryContext pagedelcontext;
+} BTVacState;
+
/*
* Lehman and Yao's algorithm requires a ``high key'' on every non-rightmost
* page. The high key is not a tuple that is used to visit the heap. It is
@@ -1181,7 +1195,7 @@ extern void _bt_delitems_vacuum(Relation rel, Buffer buf,
extern void _bt_delitems_delete_check(Relation rel, Buffer buf,
Relation heapRel,
TM_IndexDeleteOp *delstate);
-extern uint32 _bt_pagedel(Relation rel, Buffer leafbuf);
+extern void _bt_pagedel(Relation rel, Buffer leafbuf, BTVacState *vstate);
/*
* prototypes for functions in nbtsearch.c
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index e94f97437045a..4cc94de224a85 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202102221
+#define CATALOG_VERSION_NO 202102191
#endif
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 16044125ba465..1487710d59077 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -11321,9 +11321,9 @@
{ oid => '3448',
descr => 'get actual version of collation from operating system',
- proname => 'pg_collation_current_version', procost => '100',
+ proname => 'pg_collation_actual_version', procost => '100',
provolatile => 'v', prorettype => 'text', proargtypes => 'oid',
- prosrc => 'pg_collation_current_version' },
+ prosrc => 'pg_collation_actual_version' },
# system management/monitoring related functions
{ oid => '3353', descr => 'list files in the log directory',
diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out
index 43fbff1de2017..de70cb121274d 100644
--- a/src/test/regress/expected/collate.icu.utf8.out
+++ b/src/test/regress/expected/collate.icu.utf8.out
@@ -2018,7 +2018,7 @@ SELECT objid::regclass::text collate "C", refobjid::regcollation::text collate "
CASE
WHEN refobjid = 'default'::regcollation THEN 'XXX' -- depends on libc version support
WHEN refobjversion IS NULL THEN 'version not tracked'
-WHEN refobjversion = pg_collation_current_version(refobjid) THEN 'up to date'
+WHEN refobjversion = pg_collation_actual_version(refobjid) THEN 'up to date'
ELSE 'out of date'
END AS version
FROM pg_depend d
@@ -2156,14 +2156,14 @@ RESET client_min_messages;
-- leave a collation for pg_upgrade test
CREATE COLLATION coll_icu_upgrade FROM "und-x-icu";
-- Test user-visible function for inspecting versions
-SELECT pg_collation_current_version('"en-x-icu"'::regcollation) is not null;
+SELECT pg_collation_actual_version('"en-x-icu"'::regcollation) is not null;
?column?
----------
t
(1 row)
-- Invalid OIDs are silently ignored
-SELECT pg_collation_current_version(0) is null;
+SELECT pg_collation_actual_version(0) is null;
?column?
----------
t
diff --git a/src/test/regress/expected/copydml.out b/src/test/regress/expected/copydml.out
index 1b533962c6b9c..b5a225628f46e 100644
--- a/src/test/regress/expected/copydml.out
+++ b/src/test/regress/expected/copydml.out
@@ -84,10 +84,10 @@ drop rule qqq on copydml_test;
create function qqq_trig() returns trigger as $$
begin
if tg_op in ('INSERT', 'UPDATE') then
- raise notice '% %', tg_op, new.id;
+ raise notice '% % %', tg_when, tg_op, new.id;
return new;
else
- raise notice '% %', tg_op, old.id;
+ raise notice '% % %', tg_when, tg_op, old.id;
return old;
end if;
end
@@ -97,16 +97,16 @@ create trigger qqqbef before insert or update or delete on copydml_test
create trigger qqqaf after insert or update or delete on copydml_test
for each row execute procedure qqq_trig();
copy (insert into copydml_test (t) values ('f') returning id) to stdout;
-NOTICE: INSERT 8
+NOTICE: BEFORE INSERT 8
8
-NOTICE: INSERT 8
+NOTICE: AFTER INSERT 8
copy (update copydml_test set t = 'g' where t = 'f' returning id) to stdout;
-NOTICE: UPDATE 8
+NOTICE: BEFORE UPDATE 8
8
-NOTICE: UPDATE 8
+NOTICE: AFTER UPDATE 8
copy (delete from copydml_test where t = 'g' returning id) to stdout;
-NOTICE: DELETE 8
+NOTICE: BEFORE DELETE 8
8
-NOTICE: DELETE 8
+NOTICE: AFTER DELETE 8
drop table copydml_test;
drop function qqq_trig();
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out
index c519a61c4fe79..a7a652822c914 100644
--- a/src/test/regress/expected/with.out
+++ b/src/test/regress/expected/with.out
@@ -176,6 +176,65 @@ ERROR: operator does not exist: text + integer
LINE 4: SELECT n+1 FROM t WHERE n < 10
^
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
+-- Deeply nested WITH caused a list-munging problem in v13
+-- Detection of cross-references and self-references
+WITH RECURSIVE w1(c1) AS
+ (WITH w2(c2) AS
+ (WITH w3(c3) AS
+ (WITH w4(c4) AS
+ (WITH w5(c5) AS
+ (WITH RECURSIVE w6(c6) AS
+ (WITH w6(c6) AS
+ (WITH w8(c8) AS
+ (SELECT 1)
+ SELECT * FROM w8)
+ SELECT * FROM w6)
+ SELECT * FROM w6)
+ SELECT * FROM w5)
+ SELECT * FROM w4)
+ SELECT * FROM w3)
+ SELECT * FROM w2)
+SELECT * FROM w1;
+ c1
+----
+ 1
+(1 row)
+
+-- Detection of invalid self-references
+WITH RECURSIVE outermost(x) AS (
+ SELECT 1
+ UNION (WITH innermost1 AS (
+ SELECT 2
+ UNION (WITH innermost2 AS (
+ SELECT 3
+ UNION (WITH innermost3 AS (
+ SELECT 4
+ UNION (WITH innermost4 AS (
+ SELECT 5
+ UNION (WITH innermost5 AS (
+ SELECT 6
+ UNION (WITH innermost6 AS
+ (SELECT 7)
+ SELECT * FROM innermost6))
+ SELECT * FROM innermost5))
+ SELECT * FROM innermost4))
+ SELECT * FROM innermost3))
+ SELECT * FROM innermost2))
+ SELECT * FROM outermost
+ UNION SELECT * FROM innermost1)
+ )
+ SELECT * FROM outermost ORDER BY 1;
+ x
+---
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+(7 rows)
+
--
-- Some examples with a tree
--
diff --git a/src/test/regress/sql/collate.icu.utf8.sql b/src/test/regress/sql/collate.icu.utf8.sql
index 8b341dbb2429a..dd5d208854747 100644
--- a/src/test/regress/sql/collate.icu.utf8.sql
+++ b/src/test/regress/sql/collate.icu.utf8.sql
@@ -820,7 +820,7 @@ SELECT objid::regclass::text collate "C", refobjid::regcollation::text collate "
CASE
WHEN refobjid = 'default'::regcollation THEN 'XXX' -- depends on libc version support
WHEN refobjversion IS NULL THEN 'version not tracked'
-WHEN refobjversion = pg_collation_current_version(refobjid) THEN 'up to date'
+WHEN refobjversion = pg_collation_actual_version(refobjid) THEN 'up to date'
ELSE 'out of date'
END AS version
FROM pg_depend d
@@ -885,6 +885,6 @@ RESET client_min_messages;
CREATE COLLATION coll_icu_upgrade FROM "und-x-icu";
-- Test user-visible function for inspecting versions
-SELECT pg_collation_current_version('"en-x-icu"'::regcollation) is not null;
+SELECT pg_collation_actual_version('"en-x-icu"'::regcollation) is not null;
-- Invalid OIDs are silently ignored
-SELECT pg_collation_current_version(0) is null;
+SELECT pg_collation_actual_version(0) is null;
diff --git a/src/test/regress/sql/copydml.sql b/src/test/regress/sql/copydml.sql
index 9a29f9c9acc7e..4578342253b62 100644
--- a/src/test/regress/sql/copydml.sql
+++ b/src/test/regress/sql/copydml.sql
@@ -70,10 +70,10 @@ drop rule qqq on copydml_test;
create function qqq_trig() returns trigger as $$
begin
if tg_op in ('INSERT', 'UPDATE') then
- raise notice '% %', tg_op, new.id;
+ raise notice '% % %', tg_when, tg_op, new.id;
return new;
else
- raise notice '% %', tg_op, old.id;
+ raise notice '% % %', tg_when, tg_op, old.id;
return old;
end if;
end
diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql
index f4ba0d8e39942..85a671c6300a2 100644
--- a/src/test/regress/sql/with.sql
+++ b/src/test/regress/sql/with.sql
@@ -95,6 +95,50 @@ UNION ALL
)
SELECT n, pg_typeof(n) FROM t;
+-- Deeply nested WITH caused a list-munging problem in v13
+-- Detection of cross-references and self-references
+WITH RECURSIVE w1(c1) AS
+ (WITH w2(c2) AS
+ (WITH w3(c3) AS
+ (WITH w4(c4) AS
+ (WITH w5(c5) AS
+ (WITH RECURSIVE w6(c6) AS
+ (WITH w6(c6) AS
+ (WITH w8(c8) AS
+ (SELECT 1)
+ SELECT * FROM w8)
+ SELECT * FROM w6)
+ SELECT * FROM w6)
+ SELECT * FROM w5)
+ SELECT * FROM w4)
+ SELECT * FROM w3)
+ SELECT * FROM w2)
+SELECT * FROM w1;
+-- Detection of invalid self-references
+WITH RECURSIVE outermost(x) AS (
+ SELECT 1
+ UNION (WITH innermost1 AS (
+ SELECT 2
+ UNION (WITH innermost2 AS (
+ SELECT 3
+ UNION (WITH innermost3 AS (
+ SELECT 4
+ UNION (WITH innermost4 AS (
+ SELECT 5
+ UNION (WITH innermost5 AS (
+ SELECT 6
+ UNION (WITH innermost6 AS
+ (SELECT 7)
+ SELECT * FROM innermost6))
+ SELECT * FROM innermost5))
+ SELECT * FROM innermost4))
+ SELECT * FROM innermost3))
+ SELECT * FROM innermost2))
+ SELECT * FROM outermost
+ UNION SELECT * FROM innermost1)
+ )
+ SELECT * FROM outermost ORDER BY 1;
+
--
-- Some examples with a tree
--