From 1cb944c99ddc4bff1062b5bc1e814ed8ce3a6731 Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Wed, 7 Oct 2026 18:22:58 +1100 Subject: [PATCH v20261007] Add helpers for schema-qualified relation names in LR messages Logical replication code has accumulated many different ways of constructing a schema-qualified relation name for use in error and log messages: 1. Some paths report only the bare relation name (which is ambiguous when the same name exists in more than one schema, as it commonly does with per-schema publications) 2. Others manually concatenate get_namespace_name()/get_rel_name() 3. Others call get_qualified_objname(), which internally delegates to quote_qualified_identifier() and causes problematic nested quoting. ~ Per the error message style guide [1], functions that already quote their own output should not have further quotes wrapped around them. Existing helpers such as get_qualified_objname(), added in a49b9cfd, are not fit here because they intentionally quote via quote_qualified_identifier() for correctness in SQL contexts. This patch adds unquoted "schema.relation" formatting helpers intended specifically for "%s" substitution into message text, and converts all logical replication messages to use them. No attempt is made to escape a quote character embedded in a schema or relation name for this formatting; the caller's message format string already provides the delimiting quotes, embedded quotes in identifiers are rare, and unescaped quotes already appear elsewhere in core message text. Since these values are for display only, exact quote-matching is not required. The patch also collapses two-argument "%s.%s" format strings and their extra get_namespace_name()/get_rel_name() call pairs down to a single "%s" substitution, simplifying the calling code at each site. Some translation churn is acknowledged, but this one-time change is necessary to make all the message formatting consistent. Index names in logical replication messages are also now schema-qualified. Logical replication code now consistently uses fully-qualified relation name substitution, and always via these 3 helper functions: 1. MsgQualifiedRelName(nspname, relname) - return the fully-qualified name formatted as "nspname.relname" for substitution to "%s" 2. MsgQualifiedRelNameByRel(rel) - given relation find rel.nspname and rel.relname and return MsgQualifiedRelName 3. MsgQualifiedRelNameByRelId(relid) - given relid find rel.nspname and rel.relname and return MsgQualifiedRelName ~~~ Expected test results are updated. Most changes are trivial -- just changing to expect fully-qualified names. There were also some tests which needed to be modified to cope with dynamic temporary schema names like pg_temp_N, where N varies with each test run. ~~~ Author: Peter Smith Reviewed-by: Ajin Cherian [1] https://www.postgresql.org/docs/current/error-style-guide.html#ERROR-STYLE-GUIDE-QUOTES Discussion: TBA --- .../postgres_fdw/expected/postgres_fdw.out | 2 +- doc/src/sgml/logical-replication.sgml | 2 +- src/backend/catalog/pg_publication.c | 17 +-- src/backend/catalog/pg_subscription.c | 2 +- src/backend/commands/publicationcmds.c | 26 ++-- src/backend/commands/subscriptioncmds.c | 27 ++-- src/backend/commands/tablecmds.c | 2 +- src/backend/executor/execReplication.c | 24 +-- src/backend/replication/logical/conflict.c | 24 +-- src/backend/replication/logical/relation.c | 22 ++- .../replication/logical/reorderbuffer.c | 2 +- .../replication/logical/sequencesync.c | 18 +-- src/backend/replication/logical/syncutils.c | 2 +- src/backend/replication/logical/tablesync.c | 34 ++--- src/backend/replication/logical/worker.c | 35 +++-- src/backend/replication/pgoutput/pgoutput.c | 15 +- src/backend/utils/cache/relcache.c | 64 ++++++++ src/include/utils/relcache.h | 8 + src/test/regress/expected/publication.out | 144 +++++++++--------- src/test/regress/sql/publication.sql | 5 + src/test/subscription/t/027_nosuperuser.pl | 4 +- src/test/subscription/t/029_on_error.pl | 2 +- src/test/subscription/t/034_temporal.pl | 20 +-- src/test/subscription/t/035_conflicts.pl | 16 +- 24 files changed, 290 insertions(+), 227 deletions(-) diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 115ebc7925f..a562a1ae907 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -266,7 +266,7 @@ DROP SUBSCRIPTION regress_pgfdw_subscription; -- test error case for create publication on foreign table -- =================================================================== CREATE PUBLICATION testpub_ftbl FOR TABLE ft1; -- should fail -ERROR: cannot add relation "ft1" to publication +ERROR: cannot add relation "public.ft1" to publication DETAIL: This operation is not supported for foreign tables. -- =================================================================== -- simple queries diff --git a/doc/src/sgml/logical-replication.sgml b/doc/src/sgml/logical-replication.sgml index 4701a3d9d18..c8d077388c7 100644 --- a/doc/src/sgml/logical-replication.sgml +++ b/doc/src/sgml/logical-replication.sgml @@ -2287,7 +2287,7 @@ DETAIL: detailed_explanation[: ERROR: conflict detected on relation "public.test": conflict=insert_exists DETAIL: Could not apply remote change: remote row (1, 'remote'). -Key already exists in unique index "test_pkey", modified locally in transaction 800 at 2026-01-16 18:15:25.652759+09: key (c)=(1), local row (1, 'local'). +Key already exists in unique index "public.test_pkey", modified locally in transaction 800 at 2026-01-16 18:15:25.652759+09: key (c)=(1), local row (1, 'local'). CONTEXT: processing remote data for replication origin "pg_16395" during "INSERT" for replication target relation "public.test" in transaction 725 finished at 0/014C0378 The LSN of the transaction that contains the change violating the constraint and diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c index f11ea8839ca..ae97033a620 100644 --- a/src/backend/catalog/pg_publication.c +++ b/src/backend/catalog/pg_publication.c @@ -60,22 +60,11 @@ check_publication_add_relation(PublicationRelInfo *pri) const char *relname; const char *errormsg; + relname = MsgQualifiedRelNameByRel(targetrel); if (pri->except) - { - /* - * The name parts must not be quoted here, because the message already - * encloses the whole name in double quotes. - */ - relname = psprintf("%s.%s", - get_namespace_name(RelationGetNamespace(targetrel)), - RelationGetRelationName(targetrel)); errormsg = gettext_noop("cannot specify relation \"%s\" in the publication EXCEPT clause"); - } else - { - relname = RelationGetRelationName(targetrel); errormsg = gettext_noop("cannot add relation \"%s\" to publication"); - } /* If in EXCEPT clause, must be root partitioned table */ if (pri->except && targetrel->rd_rel->relispartition) @@ -582,7 +571,7 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri, ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("relation \"%s\" is already a member of publication \"%s\"", - RelationGetRelationName(targetrel), pub->name))); + MsgQualifiedRelNameByRel(targetrel), pub->name))); } check_publication_add_relation(pri); @@ -716,7 +705,7 @@ pub_collist_validate(Relation targetrel, List *columns) ereport(ERROR, errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", - colname, RelationGetRelationName(targetrel))); + colname, MsgQualifiedRelNameByRel(targetrel))); if (!AttrNumberIsForUserDefinedAttr(attnum)) ereport(ERROR, diff --git a/src/backend/catalog/pg_subscription.c b/src/backend/catalog/pg_subscription.c index 60c15b22194..483278db4aa 100644 --- a/src/backend/catalog/pg_subscription.c +++ b/src/backend/catalog/pg_subscription.c @@ -571,7 +571,7 @@ RemoveSubscriptionRel(Oid subid, Oid relid) errmsg("could not drop relation mapping for subscription \"%s\"", get_subscription_name(subrel->srsubid, false)), errdetail("Table synchronization for relation \"%s\" is in progress and is in state \"%c\".", - get_rel_name(relid), subrel->srsubstate), + MsgQualifiedRelNameByRelId(relid), subrel->srsubstate), /* * translator: first %s is a SQL ALTER command and second %s is a diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c index 301050104dd..417551087a4 100644 --- a/src/backend/commands/publicationcmds.c +++ b/src/backend/commands/publicationcmds.c @@ -733,7 +733,7 @@ TransformPubWhereClauses(List *tables, const char *queryString, ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot use publication WHERE clause for relation \"%s\"", - RelationGetRelationName(pri->relation)), + MsgQualifiedRelNameByRel(pri->relation)), errdetail("WHERE clause cannot be used for a partitioned table when %s is false.", "publish_via_partition_root"))); @@ -807,9 +807,9 @@ CheckPubRelationColumnList(char *pubname, List *tables, if (publish_schema) ereport(ERROR, errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot use column list for relation \"%s.%s\" in publication \"%s\"", - get_namespace_name(RelationGetNamespace(pri->relation)), - RelationGetRelationName(pri->relation), pubname), + errmsg("cannot use column list for relation \"%s\" in publication \"%s\"", + MsgQualifiedRelNameByRel(pri->relation), + pubname), errdetail("Column lists cannot be specified in publications containing FOR TABLES IN SCHEMA elements.")); /* @@ -821,9 +821,9 @@ CheckPubRelationColumnList(char *pubname, List *tables, pri->relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("cannot use column list for relation \"%s.%s\" in publication \"%s\"", - get_namespace_name(RelationGetNamespace(pri->relation)), - RelationGetRelationName(pri->relation), pubname), + errmsg("cannot use column list for relation \"%s\" in publication \"%s\"", + MsgQualifiedRelNameByRel(pri->relation), + pubname), errdetail("Column lists cannot be specified for partitioned tables when %s is false.", "publish_via_partition_root"))); } @@ -1093,7 +1093,7 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt, ReleaseSysCache(rftuple); continue; } - relname = get_rel_name(relid); + relname = MsgQualifiedRelNameByRelId(relid); if (relname == NULL) /* table concurrently dropped */ { ReleaseSysCache(rftuple); @@ -1879,14 +1879,14 @@ OpenTableList(List *tables) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("conflicting or redundant WHERE clauses for table \"%s\"", - RelationGetRelationName(rel)))); + MsgQualifiedRelNameByRel(rel)))); /* Disallow duplicate tables if there are any with column lists. */ if (t->columns || list_member_oid(relids_with_collist, myrelid)) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("conflicting or redundant column lists for table \"%s\"", - RelationGetRelationName(rel)))); + MsgQualifiedRelNameByRel(rel)))); table_close(rel, ShareUpdateExclusiveLock); continue; @@ -1943,7 +1943,7 @@ OpenTableList(List *tables) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("conflicting or redundant WHERE clauses for table \"%s\"", - RelationGetRelationName(rel)))); + MsgQualifiedRelNameByRel(rel)))); /* * We don't allow to specify column list for both parent @@ -1955,7 +1955,7 @@ OpenTableList(List *tables) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("conflicting or redundant column lists for table \"%s\"", - RelationGetRelationName(rel)))); + MsgQualifiedRelNameByRel(rel)))); continue; } @@ -2097,7 +2097,7 @@ PublicationDropTables(Oid pubid, List *rels, bool missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("relation \"%s\" is not part of the publication", - RelationGetRelationName(rel)))); + MsgQualifiedRelNameByRel(rel)))); } if (pubrel->whereClause) diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 36bb20a1f6e..f64bf30588d 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -1238,9 +1238,10 @@ AlterSubscription_refresh(Subscription *sub, bool copy_data, copy_data ? SUBREL_STATE_INIT : SUBREL_STATE_READY, InvalidXLogRecPtr, true); ereport(DEBUG1, - errmsg_internal("%s \"%s.%s\" added to subscription \"%s\"", + errmsg_internal("%s \"%s\" added to subscription \"%s\"", relkind == RELKIND_SEQUENCE ? "sequence" : "table", - rv->schemaname, rv->relname, sub->name)); + MsgQualifiedRelName(rv->schemaname, rv->relname), + sub->name)); } } @@ -1314,9 +1315,8 @@ AlterSubscription_refresh(Subscription *sub, bool copy_data, } ereport(DEBUG1, - (errmsg_internal("table \"%s.%s\" removed from subscription \"%s\"", - get_namespace_name(get_rel_namespace(relid)), - get_rel_name(relid), + (errmsg_internal("table \"%s\" removed from subscription \"%s\"", + MsgQualifiedRelNameByRelId(relid), sub->name))); } } @@ -1348,9 +1348,8 @@ AlterSubscription_refresh(Subscription *sub, bool copy_data, * subscription and skips it, see copy_sequence(). */ ereport(DEBUG1, - errmsg_internal("sequence \"%s.%s\" removed from subscription \"%s\"", - get_namespace_name(get_rel_namespace(relid)), - get_rel_name(relid), + errmsg_internal("sequence \"%s\" removed from subscription \"%s\"", + MsgQualifiedRelNameByRelId(relid), sub->name)); } } @@ -1487,9 +1486,8 @@ AlterSubscription_refresh_seq(Subscription *sub, char *conninfo) UpdateSubscriptionRelState(sub->oid, relid, SUBREL_STATE_INIT, InvalidXLogRecPtr, false); ereport(DEBUG1, - errmsg_internal("sequence \"%s.%s\" of subscription \"%s\" set to INIT state", - get_namespace_name(get_rel_namespace(relid)), - get_rel_name(relid), + errmsg_internal("sequence \"%s\" of subscription \"%s\" set to INIT state", + MsgQualifiedRelNameByRelId(relid), sub->name)); } } @@ -2595,7 +2593,8 @@ drop_sub_conflict_log_table(Oid subid, char *subname, Oid subconflictlogrelid) ereport(NOTICE, errmsg("dropped conflict log table \"%s\" for subscription \"%s\"", - get_qualified_objname(PG_CONFLICT_NAMESPACE, conflictrelname), + MsgQualifiedRelName(get_namespace_name(PG_CONFLICT_NAMESPACE), + conflictrelname), subname)); } } @@ -3658,8 +3657,8 @@ fetch_relation_list(WalReceiverConn *wrconn, List *publications) list_member_rangevar(relationlist, relinfo->rv)) ereport(ERROR, errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot use different column lists for table \"%s.%s\" in different publications", - nspname, relname)); + errmsg("cannot use different column lists for table \"%s\" in different publications", + MsgQualifiedRelName(nspname, relname))); else relationlist = lappend(relationlist, relinfo); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 56e06e4c324..84108e2ebef 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8734,7 +8734,7 @@ ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"), errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.", - colName, RelationGetRelationName(rel)))); + colName, MsgQualifiedRelNameByRel(rel)))); rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED); diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c index 18da4fe08f6..5234296a5fd 100644 --- a/src/backend/executor/execReplication.c +++ b/src/backend/executor/execReplication.c @@ -1090,37 +1090,37 @@ CheckCmdReplicaIdentity(Relation rel, CmdType cmd) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("cannot update table \"%s\"", - RelationGetRelationName(rel)), + MsgQualifiedRelNameByRel(rel)), errdetail("Column used in the publication WHERE expression is not part of the replica identity."))); else if (cmd == CMD_UPDATE && !pubdesc.cols_valid_for_update) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("cannot update table \"%s\"", - RelationGetRelationName(rel)), + MsgQualifiedRelNameByRel(rel)), errdetail("Column list used by the publication does not cover the replica identity."))); else if (cmd == CMD_UPDATE && !pubdesc.gencols_valid_for_update) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("cannot update table \"%s\"", - RelationGetRelationName(rel)), + MsgQualifiedRelNameByRel(rel)), errdetail("Replica identity must not contain unpublished generated columns."))); else if (cmd == CMD_DELETE && !pubdesc.rf_valid_for_delete) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("cannot delete from table \"%s\"", - RelationGetRelationName(rel)), + MsgQualifiedRelNameByRel(rel)), errdetail("Column used in the publication WHERE expression is not part of the replica identity."))); else if (cmd == CMD_DELETE && !pubdesc.cols_valid_for_delete) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("cannot delete from table \"%s\"", - RelationGetRelationName(rel)), + MsgQualifiedRelNameByRel(rel)), errdetail("Column list used by the publication does not cover the replica identity."))); else if (cmd == CMD_DELETE && !pubdesc.gencols_valid_for_delete) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("cannot delete from table \"%s\"", - RelationGetRelationName(rel)), + MsgQualifiedRelNameByRel(rel)), errdetail("Replica identity must not contain unpublished generated columns."))); /* If relation has replica identity we are always good. */ @@ -1140,13 +1140,13 @@ CheckCmdReplicaIdentity(Relation rel, CmdType cmd) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("cannot update table \"%s\" because it does not have a replica identity and publishes updates", - RelationGetRelationName(rel)), + MsgQualifiedRelNameByRel(rel)), errhint("To enable updating the table, set REPLICA IDENTITY using ALTER TABLE."))); else if (cmd == CMD_DELETE && pubdesc.pubactions.pubdelete) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("cannot delete from table \"%s\" because it does not have a replica identity and publishes deletes", - RelationGetRelationName(rel)), + MsgQualifiedRelNameByRel(rel)), errhint("To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE."))); } @@ -1166,8 +1166,8 @@ CheckSubscriptionRelkind(char localrelkind, char remoterelkind, localrelkind != RELKIND_SEQUENCE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot use relation \"%s.%s\" as logical replication target", - nspname, relname), + errmsg("cannot use relation \"%s\" as logical replication target", + MsgQualifiedRelName(nspname, relname)), errdetail_relkind_not_supported(localrelkind))); /* @@ -1180,8 +1180,8 @@ CheckSubscriptionRelkind(char localrelkind, char remoterelkind, ereport(ERROR, errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), /* translator: 3rd and 4th %s are "sequence" or "table" */ - errmsg("relation \"%s.%s\" type mismatch: source \"%s\", target \"%s\"", - nspname, relname, + errmsg("relation \"%s\" type mismatch: source \"%s\", target \"%s\"", + MsgQualifiedRelName(nspname, relname), remoterelkind == RELKIND_SEQUENCE ? "sequence" : "table", localrelkind == RELKIND_SEQUENCE ? "sequence" : "table")); } diff --git a/src/backend/replication/logical/conflict.c b/src/backend/replication/logical/conflict.c index 90431ffce98..2bdca6c4ac6 100644 --- a/src/backend/replication/logical/conflict.c +++ b/src/backend/replication/logical/conflict.c @@ -196,7 +196,8 @@ create_conflict_log_table(Oid subid, char *subname, Oid subowner) ereport(NOTICE, (errmsg("created conflict log table \"%s\" for subscription \"%s\"", - get_qualified_objname(PG_CONFLICT_NAMESPACE, relname), + MsgQualifiedRelName(get_namespace_name(PG_CONFLICT_NAMESPACE), + relname), subname))); return relid; @@ -297,9 +298,8 @@ ReportApplyConflict(EState *estate, ResultRelInfo *relinfo, int elevel, ereport(elevel, errcode_apply_conflict(type), - errmsg("conflict detected on relation \"%s.%s\": conflict=%s", - get_namespace_name(RelationGetNamespace(localrel)), - RelationGetRelationName(localrel), + errmsg("conflict detected on relation \"%s\": conflict=%s", + MsgQualifiedRelNameByRel(localrel), ConflictTypeNames[type]), errdetail_internal("%s", err_detail.data)); } @@ -458,24 +458,24 @@ errdetail_apply_conflict(EState *estate, ResultRelInfo *relinfo, { if (tuple_buf.len) appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified locally in transaction %u at %s: %s."), - get_rel_name(indexoid), + MsgQualifiedRelNameByRelId(indexoid), localxmin, timestamptz_to_str(localts), tuple_buf.data); else appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified locally in transaction %u at %s."), - get_rel_name(indexoid), + MsgQualifiedRelNameByRelId(indexoid), localxmin, timestamptz_to_str(localts)); } else if (replorigin_by_oid(localorigin, true, &origin_name)) { if (tuple_buf.len) appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified by origin \"%s\" in transaction %u at %s: %s."), - get_rel_name(indexoid), origin_name, + MsgQualifiedRelNameByRelId(indexoid), origin_name, localxmin, timestamptz_to_str(localts), tuple_buf.data); else appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified by origin \"%s\" in transaction %u at %s."), - get_rel_name(indexoid), origin_name, + MsgQualifiedRelNameByRelId(indexoid), origin_name, localxmin, timestamptz_to_str(localts)); } @@ -490,12 +490,12 @@ errdetail_apply_conflict(EState *estate, ResultRelInfo *relinfo, { if (tuple_buf.len) appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified by a non-existent origin in transaction %u at %s: %s."), - get_rel_name(indexoid), + MsgQualifiedRelNameByRelId(indexoid), localxmin, timestamptz_to_str(localts), tuple_buf.data); else appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified by a non-existent origin in transaction %u at %s."), - get_rel_name(indexoid), + MsgQualifiedRelNameByRelId(indexoid), localxmin, timestamptz_to_str(localts)); } } @@ -503,11 +503,11 @@ errdetail_apply_conflict(EState *estate, ResultRelInfo *relinfo, { if (tuple_buf.len) appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified in transaction %u: %s."), - get_rel_name(indexoid), localxmin, + MsgQualifiedRelNameByRelId(indexoid), localxmin, tuple_buf.data); else appendStringInfo(&err_detail, _("Key already exists in unique index \"%s\", modified in transaction %u."), - get_rel_name(indexoid), localxmin); + MsgQualifiedRelNameByRelId(indexoid), localxmin); } break; diff --git a/src/backend/replication/logical/relation.c b/src/backend/replication/logical/relation.c index 6242ce70ad2..70746b1d9ec 100644 --- a/src/backend/replication/logical/relation.c +++ b/src/backend/replication/logical/relation.c @@ -271,22 +271,20 @@ logicalrep_report_missing_or_gen_attrs(LogicalRepRelation *remoterel, if (!bms_is_empty(missingatts)) ereport(ERROR, errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg_plural("logical replication target relation \"%s.%s\" is missing replicated column: %s", - "logical replication target relation \"%s.%s\" is missing replicated columns: %s", + errmsg_plural("logical replication target relation \"%s\" is missing replicated column: %s", + "logical replication target relation \"%s\" is missing replicated columns: %s", bms_num_members(missingatts), - remoterel->nspname, - remoterel->relname, + MsgQualifiedRelName(remoterel->nspname, remoterel->relname), logicalrep_get_attrs_str(remoterel, missingatts))); if (!bms_is_empty(generatedatts)) ereport(ERROR, errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg_plural("logical replication target relation \"%s.%s\" has incompatible generated column: %s", - "logical replication target relation \"%s.%s\" has incompatible generated columns: %s", + errmsg_plural("logical replication target relation \"%s\" has incompatible generated column: %s", + "logical replication target relation \"%s\" has incompatible generated columns: %s", bms_num_members(generatedatts), - remoterel->nspname, - remoterel->relname, + MsgQualifiedRelName(remoterel->nspname, remoterel->relname), logicalrep_get_attrs_str(remoterel, generatedatts))); } @@ -337,9 +335,9 @@ logicalrep_rel_mark_updatable(LogicalRepRelMapEntry *entry) if (!AttrNumberIsForUserDefinedAttr(attnum)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("logical replication target relation \"%s.%s\" uses " + errmsg("logical replication target relation \"%s\" uses " "system columns in REPLICA IDENTITY index", - remoterel->nspname, remoterel->relname))); + MsgQualifiedRelName(remoterel->nspname, remoterel->relname)))); attnum = AttrNumberGetAttrOffset(attnum); @@ -430,8 +428,8 @@ logicalrep_rel_open(LogicalRepRelId remoteid, LOCKMODE lockmode) if (!OidIsValid(relid)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("logical replication target relation \"%s.%s\" does not exist", - remoterel->nspname, remoterel->relname))); + errmsg("logical replication target relation \"%s\" does not exist", + MsgQualifiedRelName(remoterel->nspname, remoterel->relname)))); entry->localrel = table_open(relid, NoLock); entry->localreloid = relid; diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index 94112c30501..ec65a0108ee 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -5155,7 +5155,7 @@ ReorderBufferToastReplace(ReorderBuffer *rb, ReorderBufferTXN *txn, toast_rel = RelationIdGetRelation(relation->rd_rel->reltoastrelid); if (!RelationIsValid(toast_rel)) elog(ERROR, "could not open toast relation with OID %u (base relation \"%s\")", - relation->rd_rel->reltoastrelid, RelationGetRelationName(relation)); + relation->rd_rel->reltoastrelid, MsgQualifiedRelNameByRel(relation)); toast_desc = RelationGetDescr(toast_rel); diff --git a/src/backend/replication/logical/sequencesync.c b/src/backend/replication/logical/sequencesync.c index 8d6b0cfef19..b33df0f5b8d 100644 --- a/src/backend/replication/logical/sequencesync.c +++ b/src/backend/replication/logical/sequencesync.c @@ -159,7 +159,8 @@ get_sequences_string(List *seqindexes, StringInfo buf) if (buf->len > 0) appendStringInfoString(buf, ", "); - appendStringInfo(buf, "\"%s.%s\"", seqinfo->nspname, seqinfo->seqname); + appendStringInfo(buf, "\"%s\"", + MsgQualifiedRelName(seqinfo->nspname, seqinfo->seqname)); } } @@ -643,9 +644,9 @@ copy_sequences(WalReceiverConn *conn) { case COPYSEQ_SUCCESS: elog(DEBUG1, - "logical replication synchronization for subscription \"%s\", sequence \"%s.%s\" has finished", - MySubscription->name, seqinfo->nspname, - seqinfo->seqname); + "logical replication synchronization for subscription \"%s\", sequence \"%s\" has finished", + MySubscription->name, + MsgQualifiedRelName(seqinfo->nspname, seqinfo->seqname)); batch_succeeded_count++; break; case COPYSEQ_MISMATCH: @@ -697,9 +698,8 @@ copy_sequences(WalReceiverConn *conn) if (seqinfo->found_on_pub) { ereport(LOG, - errmsg("skip synchronization of sequence \"%s.%s\" because it has been dropped concurrently", - seqinfo->nspname, - seqinfo->seqname)); + errmsg("skip synchronization of sequence \"%s\" because it has been dropped concurrently", + MsgQualifiedRelName(seqinfo->nspname, seqinfo->seqname))); batch_skipped_count++; } break; @@ -711,8 +711,8 @@ copy_sequences(WalReceiverConn *conn) * and it must not be treated as an error. */ ereport(LOG, - errmsg("skip synchronization of sequence \"%s.%s\" because it is no longer part of subscription \"%s\"", - seqinfo->nspname, seqinfo->seqname, + errmsg("skip synchronization of sequence \"%s\" because it is no longer part of subscription \"%s\"", + MsgQualifiedRelName(seqinfo->nspname, seqinfo->seqname), MySubscription->name)); batch_skipped_count++; break; diff --git a/src/backend/replication/logical/syncutils.c b/src/backend/replication/logical/syncutils.c index ef61ca0437d..cdf2390d262 100644 --- a/src/backend/replication/logical/syncutils.c +++ b/src/backend/replication/logical/syncutils.c @@ -82,7 +82,7 @@ FinishSyncWorker(void) ereport(LOG, errmsg("logical replication table synchronization worker for subscription \"%s\", table \"%s\" has finished", MySubscription->name, - get_rel_name(MyLogicalRepWorker->relid))); + MsgQualifiedRelNameByRelId(MyLogicalRepWorker->relid))); CommitTransactionCommand(); /* Find the leader apply worker and signal it. */ diff --git a/src/backend/replication/logical/tablesync.c b/src/backend/replication/logical/tablesync.c index 4015e861a64..8cbc15905b3 100644 --- a/src/backend/replication/logical/tablesync.c +++ b/src/backend/replication/logical/tablesync.c @@ -756,15 +756,15 @@ fetch_remote_table_info(char *nspname, char *relname, LogicalRepRelation *lrel, if (res->status != WALRCV_OK_TUPLES) ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), - errmsg("could not fetch table info for table \"%s.%s\" from publisher: %s", - nspname, relname, res->err))); + errmsg("could not fetch table info for table \"%s\" from publisher: %s", + MsgQualifiedRelName(nspname, relname), res->err))); slot = MakeSingleTupleTableSlot(res->tupledesc, &TTSOpsMinimalTuple); if (!tuplestore_gettupleslot(res->tuplestore, true, false, slot)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("table \"%s.%s\" not found on publisher", - nspname, relname))); + errmsg("table \"%s\" not found on publisher", + MsgQualifiedRelName(nspname, relname)))); lrel->remoteid = DatumGetObjectId(slot_getattr(slot, 1, &isnull)); Assert(!isnull); @@ -834,8 +834,8 @@ fetch_remote_table_info(char *nspname, char *relname, LogicalRepRelation *lrel, if (pubres->status != WALRCV_OK_TUPLES) ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), - errmsg("could not fetch column list info for table \"%s.%s\" from publisher: %s", - nspname, relname, pubres->err))); + errmsg("could not fetch column list info for table \"%s\" from publisher: %s", + MsgQualifiedRelName(nspname, relname), pubres->err))); /* * We don't support the case where the column list is different for @@ -848,8 +848,8 @@ fetch_remote_table_info(char *nspname, char *relname, LogicalRepRelation *lrel, if (tuplestore_tuple_count(pubres->tuplestore) > 1) ereport(ERROR, errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot use different column lists for table \"%s.%s\" in different publications", - nspname, relname)); + errmsg("cannot use different column lists for table \"%s\" in different publications", + MsgQualifiedRelName(nspname, relname))); /* * Get the column list and build a single bitmap with the attnums. @@ -915,8 +915,8 @@ fetch_remote_table_info(char *nspname, char *relname, LogicalRepRelation *lrel, if (res->status != WALRCV_OK_TUPLES) ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), - errmsg("could not fetch table info for table \"%s.%s\" from publisher: %s", - nspname, relname, res->err))); + errmsg("could not fetch table info for table \"%s\" from publisher: %s", + MsgQualifiedRelName(nspname, relname), res->err))); /* We don't know the number of rows coming, so allocate enough space. */ lrel->attnames = palloc0_array(char *, MaxTupleAttributeNumber); @@ -963,8 +963,8 @@ fetch_remote_table_info(char *nspname, char *relname, LogicalRepRelation *lrel, /* Should never happen. */ if (++natt >= MaxTupleAttributeNumber) - elog(ERROR, "too many columns in remote table \"%s.%s\"", - nspname, relname); + elog(ERROR, "too many columns in remote table \"%s\"", + MsgQualifiedRelName(nspname, relname)); ExecClearTuple(slot); } @@ -1027,8 +1027,8 @@ fetch_remote_table_info(char *nspname, char *relname, LogicalRepRelation *lrel, if (res->status != WALRCV_OK_TUPLES) ereport(ERROR, - (errmsg("could not fetch table WHERE clause info for table \"%s.%s\" from publisher: %s", - nspname, relname, res->err))); + (errmsg("could not fetch table WHERE clause info for table \"%s\" from publisher: %s", + MsgQualifiedRelName(nspname, relname), res->err))); /* * Multiple row filter expressions for the same table will be combined @@ -1200,8 +1200,8 @@ copy_table(Relation rel) if (res->status != WALRCV_OK_COPY_OUT) ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), - errmsg("could not start initial contents copy for table \"%s.%s\": %s", - lrel.nspname, lrel.relname, res->err))); + errmsg("could not start initial contents copy for table \"%s\": %s", + MsgQualifiedRelName(lrel.nspname, lrel.relname), res->err))); walrcv_clear_result(res); copybuf = makeStringInfo(); @@ -1478,7 +1478,7 @@ LogicalRepSyncTableStart(XLogRecPtr *origin_startpos) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("user \"%s\" cannot replicate into relation with row-level security enabled: \"%s\"", GetUserNameFromId(GetUserId(), true), - RelationGetRelationName(rel)))); + MsgQualifiedRelNameByRel(rel)))); /* Now do the initial data copy */ PushActiveSnapshot(GetTransactionSnapshot()); diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c index 44d735cdc55..a9ab3927984 100644 --- a/src/backend/replication/logical/worker.c +++ b/src/backend/replication/logical/worker.c @@ -2662,7 +2662,7 @@ TargetPrivilegesCheck(Relation rel, AclMode mode) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("user \"%s\" cannot replicate into relation with row-level security enabled: \"%s\"", GetUserNameFromId(GetUserId(), true), - RelationGetRelationName(rel)))); + MsgQualifiedRelNameByRel(rel)))); } /* @@ -2805,17 +2805,17 @@ check_relation_updatable(LogicalRepRelMapEntry *rel) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("publisher did not send replica identity column " - "expected by the logical replication target relation \"%s.%s\"", - rel->remoterel.nspname, rel->remoterel.relname))); + "expected by the logical replication target relation \"%s\"", + MsgQualifiedRelName(rel->remoterel.nspname, rel->remoterel.relname)))); } ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("logical replication target relation \"%s.%s\" has " + errmsg("logical replication target relation \"%s\" has " "neither REPLICA IDENTITY index nor PRIMARY " "KEY and published relation does not have " "REPLICA IDENTITY FULL", - rel->remoterel.nspname, rel->remoterel.relname))); + MsgQualifiedRelName(rel->remoterel.nspname, rel->remoterel.relname)))); } /* @@ -6010,7 +6010,7 @@ InitializeLogRepWorker(void) ereport(LOG, errmsg("logical replication table synchronization worker for subscription \"%s\", table \"%s\" has started", MySubscription->name, - get_rel_name(MyLogicalRepWorker->relid))); + MsgQualifiedRelNameByRelId(MyLogicalRepWorker->relid))); else if (am_sequencesync_worker()) ereport(LOG, errmsg("logical replication sequence synchronization worker for subscription \"%s\" has started", @@ -6363,40 +6363,39 @@ apply_error_callback(void *arg) } else { + const char *target_relname = MsgQualifiedRelName(ctx->rel->remoterel.nspname, + ctx->rel->remoterel.relname); + if (ctx->remote_attnum < 0) { if (!XLogRecPtrIsValid(ctx->finish_lsn)) - errcontext("processing remote data for replication origin \"%s\" during message type \"%s\" for replication target relation \"%s.%s\" in transaction %u", + errcontext("processing remote data for replication origin \"%s\" during message type \"%s\" for replication target relation \"%s\" in transaction %u", ctx->origin_name, logicalrep_message_type(ctx->command), - ctx->rel->remoterel.nspname, - ctx->rel->remoterel.relname, + target_relname, ctx->remote_xid); else - errcontext("processing remote data for replication origin \"%s\" during message type \"%s\" for replication target relation \"%s.%s\" in transaction %u, finished at %X/%08X", + errcontext("processing remote data for replication origin \"%s\" during message type \"%s\" for replication target relation \"%s\" in transaction %u, finished at %X/%08X", ctx->origin_name, logicalrep_message_type(ctx->command), - ctx->rel->remoterel.nspname, - ctx->rel->remoterel.relname, + target_relname, ctx->remote_xid, LSN_FORMAT_ARGS(ctx->finish_lsn)); } else { if (!XLogRecPtrIsValid(ctx->finish_lsn)) - errcontext("processing remote data for replication origin \"%s\" during message type \"%s\" for replication target relation \"%s.%s\" column \"%s\" in transaction %u", + errcontext("processing remote data for replication origin \"%s\" during message type \"%s\" for replication target relation \"%s\" column \"%s\" in transaction %u", ctx->origin_name, logicalrep_message_type(ctx->command), - ctx->rel->remoterel.nspname, - ctx->rel->remoterel.relname, + target_relname, ctx->rel->remoterel.attnames[ctx->remote_attnum], ctx->remote_xid); else - errcontext("processing remote data for replication origin \"%s\" during message type \"%s\" for replication target relation \"%s.%s\" column \"%s\" in transaction %u, finished at %X/%08X", + errcontext("processing remote data for replication origin \"%s\" during message type \"%s\" for replication target relation \"%s\" column \"%s\" in transaction %u, finished at %X/%08X", ctx->origin_name, logicalrep_message_type(ctx->command), - ctx->rel->remoterel.nspname, - ctx->rel->remoterel.relname, + target_relname, ctx->rel->remoterel.attnames[ctx->remote_attnum], ctx->remote_xid, LSN_FORMAT_ARGS(ctx->finish_lsn)); diff --git a/src/backend/replication/pgoutput/pgoutput.c b/src/backend/replication/pgoutput/pgoutput.c index b481d5bb388..13d639fa6f9 100644 --- a/src/backend/replication/pgoutput/pgoutput.c +++ b/src/backend/replication/pgoutput/pgoutput.c @@ -1112,9 +1112,8 @@ check_and_init_gencol(PGOutputData *data, List *publications, else if (entry->include_gencols_type != pub->pubgencols_type) ereport(ERROR, errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot use different values of publish_generated_columns for table \"%s.%s\" in different publications", - get_namespace_name(RelationGetNamespace(relation)), - RelationGetRelationName(relation))); + errmsg("cannot use different values of publish_generated_columns for table \"%s\" in different publications", + MsgQualifiedRelNameByRel(relation))); } } @@ -1189,9 +1188,8 @@ pgoutput_column_list_init(PGOutputData *data, List *publications, else if (!bms_equal(entry->columns, cols)) ereport(ERROR, errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot use different column lists for table \"%s.%s\" in different publications", - get_namespace_name(RelationGetNamespace(relation)), - RelationGetRelationName(relation))); + errmsg("cannot use different column lists for table \"%s\" in different publications", + MsgQualifiedRelNameByRel(relation))); } /* loop all subscribed publications */ /* @@ -1338,9 +1336,8 @@ pgoutput_row_filter(Relation relation, TupleTableSlot *old_slot, if (!filter_exprstate) return true; - elog(DEBUG3, "table \"%s.%s\" has row filter", - get_namespace_name(RelationGetNamespace(relation)), - RelationGetRelationName(relation)); + elog(DEBUG3, "table \"%s\" has row filter", + MsgQualifiedRelNameByRel(relation)); ResetPerTupleExprContext(entry->estate); diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index d8f04a05309..12011a97ff9 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -2140,6 +2140,70 @@ RelationIdGetRelation(Oid relationId) return rd; } +/* + * MsgQualifiedRelName + * + * Returns a schema-qualified name of the relation in the format + * "nspname.relname" suitable for substitution to a log message that already + * has enclosing quotes \"%s\" + */ +char * +MsgQualifiedRelName(const char *nspname, const char *relname) +{ + Assert(nspname); + Assert(relname); + + return psprintf("%s.%s", nspname, relname); +} + +/* + * MsgQualifiedRelNameByRel + * + * Returns a schema-qualified name of the specified `rel` suitable for + * substitution to a log message. Calls MsgQualifiedRelName. + */ +char * +MsgQualifiedRelNameByRel(Relation rel) +{ + const char *nspname; + const char *relname; + + Assert(rel); + nspname = get_namespace_name(RelationGetNamespace(rel)); + relname = RelationGetRelationName(rel); + + return MsgQualifiedRelName(nspname, relname); +} + +/* + * MsgQualifiedRelNameByRelId + * + * Returns a schema-qualified name of the relation with the specified `relid` + * suitable for substitution to a log message. Calls MsgQualifiedRelName. + * + * Returns NULL if no relation found for this relid. + */ +char * +MsgQualifiedRelNameByRelId(Oid relid) +{ + HeapTuple tp; + + tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_class reltup = (Form_pg_class) GETSTRUCT(tp); + const char *nspname = get_namespace_name(reltup->relnamespace); + const char *relname = NameStr(reltup->relname); + char *result; + + result = MsgQualifiedRelName(nspname, relname); + ReleaseSysCache(tp); + return result; + } + else + return NULL; +} + /* ---------------------------------------------------------------- * cache invalidation support routines * ---------------------------------------------------------------- diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h index 3e97da6a538..baf34968962 100644 --- a/src/include/utils/relcache.h +++ b/src/include/utils/relcache.h @@ -101,6 +101,14 @@ extern int errtablecol(Relation rel, int attnum); extern int errtablecolname(Relation rel, const char *colname); extern int errtableconstraint(Relation rel, const char *conname); +/* + * Routines to get a schema-qualified name of a relation suitable for + * substitution to a log message. + */ +extern char *MsgQualifiedRelName(const char *nspname, const char *relname); +extern char *MsgQualifiedRelNameByRel(Relation rel); +extern char *MsgQualifiedRelNameByRelId(Oid relid); + /* * Routines for backend startup */ diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index b55da39fde5..281765b995d 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -194,7 +194,7 @@ Tables from schemas: -- fail - can't drop a table from the schema publication which isn't in the -- publication ALTER PUBLICATION testpub_forschema DROP TABLE pub_test.testpub_nopk; -ERROR: relation "testpub_nopk" is not part of the publication +ERROR: relation "pub_test.testpub_nopk" is not part of the publication -- should be able to set table to schema publication ALTER PUBLICATION testpub_forschema SET TABLE pub_test.testpub_nopk; \dRp+ testpub_forschema @@ -635,7 +635,7 @@ Tables: UPDATE testpub_parted SET a = 1 WHERE false; -- should now fail, because parent's publication replicates updates UPDATE testpub_parted1 SET a = 1; -ERROR: cannot update table "testpub_parted1" because it does not have a replica identity and publishes updates +ERROR: cannot update table "public.testpub_parted1" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. ALTER TABLE testpub_parted DETACH PARTITION testpub_parted1; -- works again, because parent's publication is no longer considered @@ -651,7 +651,7 @@ Tables: -- still fail, because parent's publication replicates updates UPDATE testpub_parted2 SET a = 2; -ERROR: cannot update table "testpub_parted2" because it does not have a replica identity and publishes updates +ERROR: cannot update table "public.testpub_parted2" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. ALTER PUBLICATION testpub_forparted DROP TABLE testpub_parted; -- works again, because update is no longer replicated @@ -792,9 +792,9 @@ RESET client_min_messages; -- fail - duplicate tables are not allowed if that table has any WHERE clause SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpub_dups FOR TABLE testpub_rf_tbl1 WHERE (a = 1), testpub_rf_tbl1 WITH (publish = 'insert'); -ERROR: conflicting or redundant WHERE clauses for table "testpub_rf_tbl1" +ERROR: conflicting or redundant WHERE clauses for table "public.testpub_rf_tbl1" CREATE PUBLICATION testpub_dups FOR TABLE testpub_rf_tbl1, testpub_rf_tbl1 WHERE (a = 2) WITH (publish = 'insert'); -ERROR: conflicting or redundant WHERE clauses for table "testpub_rf_tbl1" +ERROR: conflicting or redundant WHERE clauses for table "public.testpub_rf_tbl1" RESET client_min_messages; -- fail - publication WHERE clause must be boolean ALTER PUBLICATION testpub5 SET TABLE testpub_rf_tbl3 WHERE (1234); @@ -921,7 +921,7 @@ CREATE TABLE testpub_rf_tbl7 (id int PRIMARY KEY, x int, y int GENERATED ALWAYS CREATE PUBLICATION testpub8 FOR TABLE testpub_rf_tbl7 WHERE (y > 100); ALTER TABLE testpub_rf_tbl7 ALTER COLUMN y SET EXPRESSION AS (x * testpub_rf_func2()); ERROR: ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication -DETAIL: Column "y" of relation "testpub_rf_tbl7" is a virtual generated column. +DETAIL: Column "y" of relation "public.testpub_rf_tbl7" is a virtual generated column. RESET client_min_messages; DROP TABLE testpub_rf_tbl1; DROP TABLE testpub_rf_tbl2; @@ -961,18 +961,18 @@ UPDATE rf_tbl_abcd_pk SET a = 1; ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_pk WHERE (c > 99); -- fail - "c" is not part of the PK UPDATE rf_tbl_abcd_pk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_pk" +ERROR: cannot update table "public.rf_tbl_abcd_pk" DETAIL: Column used in the publication WHERE expression is not part of the replica identity. ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_pk WHERE (d > 99); -- fail - "d" is not part of the PK UPDATE rf_tbl_abcd_pk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_pk" +ERROR: cannot update table "public.rf_tbl_abcd_pk" DETAIL: Column used in the publication WHERE expression is not part of the replica identity. -- 1b. REPLICA IDENTITY is DEFAULT and table has no PK ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_nopk WHERE (a > 99); -- fail - "a" is not part of REPLICA IDENTITY UPDATE rf_tbl_abcd_nopk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_nopk" +ERROR: cannot update table "public.rf_tbl_abcd_nopk" DETAIL: Column used in the publication WHERE expression is not part of the replica identity. -- Case 2. REPLICA IDENTITY FULL ALTER TABLE rf_tbl_abcd_pk REPLICA IDENTITY FULL; @@ -989,17 +989,17 @@ ALTER TABLE rf_tbl_abcd_nopk REPLICA IDENTITY NOTHING; ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_pk WHERE (a > 99); -- fail - "a" is in PK but it is not part of REPLICA IDENTITY NOTHING UPDATE rf_tbl_abcd_pk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_pk" +ERROR: cannot update table "public.rf_tbl_abcd_pk" DETAIL: Column used in the publication WHERE expression is not part of the replica identity. ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_pk WHERE (c > 99); -- fail - "c" is not in PK and not in REPLICA IDENTITY NOTHING UPDATE rf_tbl_abcd_pk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_pk" +ERROR: cannot update table "public.rf_tbl_abcd_pk" DETAIL: Column used in the publication WHERE expression is not part of the replica identity. ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_nopk WHERE (a > 99); -- fail - "a" is not in REPLICA IDENTITY NOTHING UPDATE rf_tbl_abcd_nopk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_nopk" +ERROR: cannot update table "public.rf_tbl_abcd_nopk" DETAIL: Column used in the publication WHERE expression is not part of the replica identity. -- Case 4. REPLICA IDENTITY INDEX ALTER TABLE rf_tbl_abcd_pk ALTER COLUMN c SET NOT NULL; @@ -1011,7 +1011,7 @@ ALTER TABLE rf_tbl_abcd_nopk REPLICA IDENTITY USING INDEX idx_abcd_nopk_c; ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_pk WHERE (a > 99); -- fail - "a" is in PK but it is not part of REPLICA IDENTITY INDEX UPDATE rf_tbl_abcd_pk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_pk" +ERROR: cannot update table "public.rf_tbl_abcd_pk" DETAIL: Column used in the publication WHERE expression is not part of the replica identity. ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_pk WHERE (c > 99); -- ok - "c" is not in PK but it is part of REPLICA IDENTITY INDEX @@ -1019,7 +1019,7 @@ UPDATE rf_tbl_abcd_pk SET a = 1; ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_nopk WHERE (a > 99); -- fail - "a" is not in REPLICA IDENTITY INDEX UPDATE rf_tbl_abcd_nopk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_nopk" +ERROR: cannot update table "public.rf_tbl_abcd_nopk" DETAIL: Column used in the publication WHERE expression is not part of the replica identity. ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_nopk WHERE (c > 99); -- ok - "c" is part of REPLICA IDENTITY INDEX @@ -1030,7 +1030,7 @@ UPDATE rf_tbl_abcd_nopk SET a = 1; ALTER PUBLICATION testpub6 SET (PUBLISH_VIA_PARTITION_ROOT=0); -- fail - cannot use row filter for partitioned table ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_part_pk WHERE (a > 99); -ERROR: cannot use publication WHERE clause for relation "rf_tbl_abcd_part_pk" +ERROR: cannot use publication WHERE clause for relation "public.rf_tbl_abcd_part_pk" DETAIL: WHERE clause cannot be used for a partitioned table when publish_via_partition_root is false. -- ok - can use row filter for partition ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_part_pk_1 WHERE (a > 99); @@ -1047,7 +1047,7 @@ UPDATE rf_tbl_abcd_part_pk SET a = 1; -- used for partitioned table ALTER PUBLICATION testpub6 SET (PUBLISH_VIA_PARTITION_ROOT=0); ERROR: cannot set parameter "publish_via_partition_root" to false for publication "testpub6" -DETAIL: The publication contains a WHERE clause for partitioned table "rf_tbl_abcd_part_pk", which is not allowed when "publish_via_partition_root" is false. +DETAIL: The publication contains a WHERE clause for partitioned table "public.rf_tbl_abcd_part_pk", which is not allowed when "publish_via_partition_root" is false. -- remove partitioned table's row filter ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_part_pk; -- ok - we don't have row filter for partitioned table. @@ -1059,7 +1059,7 @@ ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_part_pk_1 WHERE (b > 99); ALTER PUBLICATION testpub6 SET (PUBLISH_VIA_PARTITION_ROOT=0); -- fail - "b" is not in REPLICA IDENTITY INDEX UPDATE rf_tbl_abcd_part_pk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_part_pk_1" +ERROR: cannot update table "public.rf_tbl_abcd_part_pk_1" DETAIL: Column used in the publication WHERE expression is not part of the replica identity. -- set PUBLISH_VIA_PARTITION_ROOT to true -- can use row filter for partitioned table @@ -1068,7 +1068,7 @@ ALTER PUBLICATION testpub6 SET (PUBLISH_VIA_PARTITION_ROOT=1); ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_part_pk WHERE (b > 99); -- fail - "b" is not in REPLICA IDENTITY INDEX UPDATE rf_tbl_abcd_part_pk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_part_pk_1" +ERROR: cannot update table "public.rf_tbl_abcd_part_pk_1" DETAIL: Column used in the publication WHERE expression is not part of the replica identity. DROP PUBLICATION testpub6; DROP TABLE rf_tbl_abcd_pk; @@ -1086,13 +1086,13 @@ ALTER TABLE testpub_gencol REPLICA IDENTITY USING index testpub_gencol_idx; -- part of the REPLICA IDENTITY index. CREATE PUBLICATION pub_gencol FOR TABLE testpub_gencol; UPDATE testpub_gencol SET a = 100 WHERE a = 1; -ERROR: cannot update table "testpub_gencol" +ERROR: cannot update table "public.testpub_gencol" DETAIL: Replica identity must not contain unpublished generated columns. -- error - generated column "b" must be published explicitly as it is -- part of the REPLICA IDENTITY. ALTER TABLE testpub_gencol REPLICA IDENTITY FULL; UPDATE testpub_gencol SET a = 100 WHERE a = 1; -ERROR: cannot update table "testpub_gencol" +ERROR: cannot update table "public.testpub_gencol" DETAIL: Replica identity must not contain unpublished generated columns. DROP PUBLICATION pub_gencol; -- ok - generated column "b" is published explicitly @@ -1107,13 +1107,13 @@ CREATE PUBLICATION pub_gencol FOR TABLE testpub_gencol; -- part of the REPLICA IDENTITY. ALTER TABLE testpub_gencol REPLICA IDENTITY FULL; UPDATE testpub_gencol SET a = 100 WHERE a = 1; -ERROR: cannot update table "testpub_gencol" +ERROR: cannot update table "public.testpub_gencol" DETAIL: Replica identity must not contain unpublished generated columns. DROP PUBLICATION pub_gencol; -- error - "stored" setting does not affect virtual column CREATE PUBLICATION pub_gencol FOR TABLE testpub_gencol with (publish_generated_columns = stored); UPDATE testpub_gencol SET a = 100 WHERE a = 1; -ERROR: cannot update table "testpub_gencol" +ERROR: cannot update table "public.testpub_gencol" DETAIL: Replica identity must not contain unpublished generated columns. DROP PUBLICATION pub_gencol; DROP TABLE testpub_gencol; @@ -1122,9 +1122,9 @@ RESET client_min_messages; -- fail - duplicate tables are not allowed if that table has any column lists SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpub_dups FOR TABLE testpub_tbl1 (a), testpub_tbl1 WITH (publish = 'insert'); -ERROR: conflicting or redundant column lists for table "testpub_tbl1" +ERROR: conflicting or redundant column lists for table "public.testpub_tbl1" CREATE PUBLICATION testpub_dups FOR TABLE testpub_tbl1, testpub_tbl1 (a) WITH (publish = 'insert'); -ERROR: conflicting or redundant column lists for table "testpub_tbl1" +ERROR: conflicting or redundant column lists for table "public.testpub_tbl1" RESET client_min_messages; -- test for column lists SET client_min_messages = 'ERROR'; @@ -1137,11 +1137,11 @@ CREATE TABLE testpub_tbl5 (a int PRIMARY KEY, b text, c text, ); -- error: column "x" does not exist ALTER PUBLICATION testpub_fortable ADD TABLE testpub_tbl5 (a, x); -ERROR: column "x" of relation "testpub_tbl5" does not exist +ERROR: column "x" of relation "public.testpub_tbl5" does not exist -- error: replica identity "a" not included in the column list ALTER PUBLICATION testpub_fortable ADD TABLE testpub_tbl5 (b, c); UPDATE testpub_tbl5 SET a = 1; -ERROR: cannot update table "testpub_tbl5" +ERROR: cannot update table "public.testpub_tbl5" DETAIL: Column list used by the publication does not cover the replica identity. ALTER PUBLICATION testpub_fortable DROP TABLE testpub_tbl5; -- error: system attributes "ctid" not allowed in column list @@ -1168,7 +1168,7 @@ ALTER TABLE testpub_tbl5 ALTER b SET NOT NULL, ALTER c SET NOT NULL; ALTER TABLE testpub_tbl5 REPLICA IDENTITY USING INDEX testpub_tbl5_b_key; -- error: replica identity (b,c) is not covered by column list (a, c) UPDATE testpub_tbl5 SET a = 1; -ERROR: cannot update table "testpub_tbl5" +ERROR: cannot update table "public.testpub_tbl5" DETAIL: Column list used by the publication does not cover the replica identity. ALTER PUBLICATION testpub_fortable DROP TABLE testpub_tbl5; -- ok: stored generated column "d" can be in the list too @@ -1182,7 +1182,7 @@ ERROR: cannot use virtual generated column "e" in publication column list ALTER TABLE testpub_tbl5 REPLICA IDENTITY USING INDEX testpub_tbl5_b_key; ALTER PUBLICATION testpub_fortable ADD TABLE testpub_tbl5 (a, c); UPDATE testpub_tbl5 SET a = 1; -ERROR: cannot update table "testpub_tbl5" +ERROR: cannot update table "public.testpub_tbl5" DETAIL: Column list used by the publication does not cover the replica identity. /* But if upd/del are not published, it works OK */ SET client_min_messages = 'ERROR'; @@ -1201,7 +1201,7 @@ Tables: CREATE TABLE testpub_tbl5d (a int PRIMARY KEY DEFERRABLE); ALTER PUBLICATION testpub_fortable ADD TABLE testpub_tbl5d; UPDATE testpub_tbl5d SET a = 1; -ERROR: cannot update table "testpub_tbl5d" because it does not have a replica identity and publishes updates +ERROR: cannot update table "public.testpub_tbl5d" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. /* but works fine with FULL replica identity */ ALTER TABLE testpub_tbl5d REPLICA IDENTITY FULL; @@ -1212,7 +1212,7 @@ CREATE TABLE testpub_tbl6 (a int, b text, c text); ALTER TABLE testpub_tbl6 REPLICA IDENTITY FULL; ALTER PUBLICATION testpub_fortable ADD TABLE testpub_tbl6 (a, b, c); UPDATE testpub_tbl6 SET a = 1; -ERROR: cannot update table "testpub_tbl6" +ERROR: cannot update table "public.testpub_tbl6" DETAIL: Column list used by the publication does not cover the replica identity. ALTER PUBLICATION testpub_fortable DROP TABLE testpub_tbl6; ALTER PUBLICATION testpub_fortable ADD TABLE testpub_tbl6; -- ok @@ -1289,14 +1289,14 @@ UPDATE testpub_tbl8 SET a = 1; ALTER PUBLICATION testpub_col_list DROP TABLE testpub_tbl8; ALTER PUBLICATION testpub_col_list ADD TABLE testpub_tbl8 (a, c); UPDATE testpub_tbl8 SET a = 1; -ERROR: cannot update table "testpub_tbl8_1" +ERROR: cannot update table "public.testpub_tbl8_1" DETAIL: Column list used by the publication does not cover the replica identity. ALTER PUBLICATION testpub_col_list DROP TABLE testpub_tbl8; -- failure: one of the partitions has REPLICA IDENTITY FULL ALTER TABLE testpub_tbl8_1 REPLICA IDENTITY FULL; ALTER PUBLICATION testpub_col_list ADD TABLE testpub_tbl8 (a, c); UPDATE testpub_tbl8 SET a = 1; -ERROR: cannot update table "testpub_tbl8_1" +ERROR: cannot update table "public.testpub_tbl8_1" DETAIL: Column list used by the publication does not cover the replica identity. ALTER PUBLICATION testpub_col_list DROP TABLE testpub_tbl8; -- add table and then try changing replica identity @@ -1305,14 +1305,14 @@ ALTER PUBLICATION testpub_col_list ADD TABLE testpub_tbl8 (a, b); -- failure: replica identity full can't be used with a column list ALTER TABLE testpub_tbl8_1 REPLICA IDENTITY FULL; UPDATE testpub_tbl8 SET a = 1; -ERROR: cannot update table "testpub_tbl8_1" +ERROR: cannot update table "public.testpub_tbl8_1" DETAIL: Column list used by the publication does not cover the replica identity. -- failure: replica identity has to be covered by the column list ALTER TABLE testpub_tbl8_1 DROP CONSTRAINT testpub_tbl8_1_pkey; ALTER TABLE testpub_tbl8_1 ADD PRIMARY KEY (c); ALTER TABLE testpub_tbl8_1 REPLICA IDENTITY USING INDEX testpub_tbl8_1_pkey; UPDATE testpub_tbl8 SET a = 1; -ERROR: cannot update table "testpub_tbl8_1" +ERROR: cannot update table "public.testpub_tbl8_1" DETAIL: Column list used by the publication does not cover the replica identity. DROP TABLE testpub_tbl8; -- column list for partitioned tables has to cover replica identities for @@ -1332,13 +1332,13 @@ ALTER TABLE testpub_tbl8 ATTACH PARTITION testpub_tbl8_0 FOR VALUES WITH (modulu -- failure: second partition has replica identity (c), which si not in column list ALTER TABLE testpub_tbl8 ATTACH PARTITION testpub_tbl8_1 FOR VALUES WITH (modulus 2, remainder 1); UPDATE testpub_tbl8 SET a = 1; -ERROR: cannot update table "testpub_tbl8_1" +ERROR: cannot update table "public.testpub_tbl8_1" DETAIL: Column list used by the publication does not cover the replica identity. -- failure: changing replica identity to FULL for partition fails, because -- of the column list on the parent ALTER TABLE testpub_tbl8_0 REPLICA IDENTITY FULL; UPDATE testpub_tbl8 SET a = 1; -ERROR: cannot update table "testpub_tbl8_0" +ERROR: cannot update table "public.testpub_tbl8_0" DETAIL: Column list used by the publication does not cover the replica identity. -- test that using column list for table is disallowed if any schemas are -- part of the publication @@ -1427,19 +1427,19 @@ UPDATE rf_tbl_abcd_pk SET a = 1; ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_pk (a); -- fail - "b" is missing from the column list UPDATE rf_tbl_abcd_pk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_pk" +ERROR: cannot update table "public.rf_tbl_abcd_pk" DETAIL: Column list used by the publication does not cover the replica identity. ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_pk (b); -- fail - "a" is missing from the column list UPDATE rf_tbl_abcd_pk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_pk" +ERROR: cannot update table "public.rf_tbl_abcd_pk" DETAIL: Column list used by the publication does not cover the replica identity. -- 1b. REPLICA IDENTITY is DEFAULT and table has no PK ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_nopk (a); -- ok - there's no replica identity, so any column list works -- note: it fails anyway, just a bit later because UPDATE requires RI UPDATE rf_tbl_abcd_nopk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_nopk" because it does not have a replica identity and publishes updates +ERROR: cannot update table "public.rf_tbl_abcd_nopk" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. -- Case 2. REPLICA IDENTITY FULL ALTER TABLE rf_tbl_abcd_pk REPLICA IDENTITY FULL; @@ -1447,12 +1447,12 @@ ALTER TABLE rf_tbl_abcd_nopk REPLICA IDENTITY FULL; ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_pk (c); -- fail - with REPLICA IDENTITY FULL no column list is allowed UPDATE rf_tbl_abcd_pk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_pk" +ERROR: cannot update table "public.rf_tbl_abcd_pk" DETAIL: Column list used by the publication does not cover the replica identity. ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_nopk (a, b, c, d); -- fail - with REPLICA IDENTITY FULL no column list is allowed UPDATE rf_tbl_abcd_nopk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_nopk" +ERROR: cannot update table "public.rf_tbl_abcd_nopk" DETAIL: Column list used by the publication does not cover the replica identity. -- Case 3. REPLICA IDENTITY NOTHING ALTER TABLE rf_tbl_abcd_pk REPLICA IDENTITY NOTHING; @@ -1461,19 +1461,19 @@ ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_pk (a); -- ok - REPLICA IDENTITY NOTHING means all column lists are valid -- it still fails later because without RI we can't replicate updates UPDATE rf_tbl_abcd_pk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_pk" because it does not have a replica identity and publishes updates +ERROR: cannot update table "public.rf_tbl_abcd_pk" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_pk (a, b, c, d); -- ok - REPLICA IDENTITY NOTHING means all column lists are valid -- it still fails later because without RI we can't replicate updates UPDATE rf_tbl_abcd_pk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_pk" because it does not have a replica identity and publishes updates +ERROR: cannot update table "public.rf_tbl_abcd_pk" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_nopk (d); -- ok - REPLICA IDENTITY NOTHING means all column lists are valid -- it still fails later because without RI we can't replicate updates UPDATE rf_tbl_abcd_nopk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_nopk" because it does not have a replica identity and publishes updates +ERROR: cannot update table "public.rf_tbl_abcd_nopk" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. -- Case 4. REPLICA IDENTITY INDEX ALTER TABLE rf_tbl_abcd_pk ALTER COLUMN c SET NOT NULL; @@ -1485,7 +1485,7 @@ ALTER TABLE rf_tbl_abcd_nopk REPLICA IDENTITY USING INDEX idx_abcd_nopk_c; ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_pk (a); -- fail - column list "a" does not cover the REPLICA IDENTITY INDEX on "c" UPDATE rf_tbl_abcd_pk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_pk" +ERROR: cannot update table "public.rf_tbl_abcd_pk" DETAIL: Column list used by the publication does not cover the replica identity. ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_pk (c); -- ok - column list "c" does cover the REPLICA IDENTITY INDEX on "c" @@ -1493,7 +1493,7 @@ UPDATE rf_tbl_abcd_pk SET a = 1; ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_nopk (a); -- fail - column list "a" does not cover the REPLICA IDENTITY INDEX on "c" UPDATE rf_tbl_abcd_nopk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_nopk" +ERROR: cannot update table "public.rf_tbl_abcd_nopk" DETAIL: Column list used by the publication does not cover the replica identity. ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_nopk (c); -- ok - column list "c" does cover the REPLICA IDENTITY INDEX on "c" @@ -1521,7 +1521,7 @@ UPDATE rf_tbl_abcd_part_pk SET a = 1; -- used for partitioned table ALTER PUBLICATION testpub6 SET (PUBLISH_VIA_PARTITION_ROOT=0); ERROR: cannot set parameter "publish_via_partition_root" to false for publication "testpub6" -DETAIL: The publication contains a column list for partitioned table "rf_tbl_abcd_part_pk", which is not allowed when "publish_via_partition_root" is false. +DETAIL: The publication contains a column list for partitioned table "public.rf_tbl_abcd_part_pk", which is not allowed when "publish_via_partition_root" is false. -- remove partitioned table's column list ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_part_pk; -- ok - we don't have column list for partitioned table. @@ -1533,7 +1533,7 @@ ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_part_pk_1 (b); ALTER PUBLICATION testpub6 SET (PUBLISH_VIA_PARTITION_ROOT=0); -- fail - "b" is not in REPLICA IDENTITY INDEX UPDATE rf_tbl_abcd_part_pk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_part_pk_1" +ERROR: cannot update table "public.rf_tbl_abcd_part_pk_1" DETAIL: Column list used by the publication does not cover the replica identity. -- set PUBLISH_VIA_PARTITION_ROOT to true -- can use column list for partitioned table @@ -1542,7 +1542,7 @@ ALTER PUBLICATION testpub6 SET (PUBLISH_VIA_PARTITION_ROOT=1); ALTER PUBLICATION testpub6 SET TABLE rf_tbl_abcd_part_pk (b); -- fail - "b" is not in REPLICA IDENTITY INDEX UPDATE rf_tbl_abcd_part_pk SET a = 1; -ERROR: cannot update table "rf_tbl_abcd_part_pk_1" +ERROR: cannot update table "public.rf_tbl_abcd_part_pk_1" DETAIL: Column list used by the publication does not cover the replica identity. DROP PUBLICATION testpub6; DROP TABLE rf_tbl_abcd_pk; @@ -1558,7 +1558,7 @@ CREATE PUBLICATION testpub_foralltables FOR ALL TABLES; RESET client_min_messages; -- fail missing REPLICA IDENTITY UPDATE testpub_tbl4 set a = 3; -ERROR: cannot update table "testpub_tbl4" because it does not have a replica identity and publishes updates +ERROR: cannot update table "public.testpub_tbl4" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. DROP PUBLICATION testpub_foralltables; -- should pass after dropping the publication @@ -1566,30 +1566,34 @@ UPDATE testpub_tbl4 set a = 3; DROP TABLE testpub_tbl4; -- fail - view CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_view; -ERROR: cannot add relation "testpub_view" to publication +ERROR: cannot add relation "public.testpub_view" to publication DETAIL: This operation is not supported for views. CREATE TEMPORARY TABLE testpub_temptbl(a int); -- fail - temporary table +-- The temporary schema in the error message is "pg_temp_N", but N varies with +-- each run, so show only the SQLSTATE. +-- (22023 means invalid_parameter_value) +\set VERBOSITY sqlstate CREATE PUBLICATION testpub_fortemptbl FOR TABLE testpub_temptbl; -ERROR: cannot add relation "testpub_temptbl" to publication -DETAIL: This operation is not supported for temporary tables. +ERROR: 22023 +\set VERBOSITY default DROP TABLE testpub_temptbl; CREATE UNLOGGED TABLE testpub_unloggedtbl(a int); -- fail - unlogged table CREATE PUBLICATION testpub_forunloggedtbl FOR TABLE testpub_unloggedtbl; -ERROR: cannot add relation "testpub_unloggedtbl" to publication +ERROR: cannot add relation "public.testpub_unloggedtbl" to publication DETAIL: This operation is not supported for unlogged tables. DROP TABLE testpub_unloggedtbl; -- fail - system table CREATE PUBLICATION testpub_forsystemtbl FOR TABLE pg_publication; -ERROR: cannot add relation "pg_publication" to publication +ERROR: cannot add relation "pg_catalog.pg_publication" to publication DETAIL: This operation is not supported for system tables. SET client_min_messages = 'ERROR'; CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_tbl1, pub_test.testpub_nopk; RESET client_min_messages; -- fail - already added ALTER PUBLICATION testpub_fortbl ADD TABLE testpub_tbl1; -ERROR: relation "testpub_tbl1" is already a member of publication "testpub_fortbl" +ERROR: relation "public.testpub_tbl1" is already a member of publication "testpub_fortbl" -- fail - already added CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_tbl1; ERROR: publication "testpub_fortbl" already exists @@ -1604,7 +1608,7 @@ Tables: -- fail - view ALTER PUBLICATION testpub_default ADD TABLE testpub_view; -ERROR: cannot add relation "testpub_view" to publication +ERROR: cannot add relation "public.testpub_view" to publication DETAIL: This operation is not supported for views. ALTER PUBLICATION testpub_default ADD TABLE testpub_tbl1; ALTER PUBLICATION testpub_default SET TABLE testpub_tbl1; @@ -1648,7 +1652,7 @@ Tables: ALTER PUBLICATION testpub_default DROP TABLE testpub_tbl1, pub_test.testpub_nopk; -- fail - nonexistent ALTER PUBLICATION testpub_default DROP TABLE pub_test.testpub_nopk; -ERROR: relation "testpub_nopk" is not part of the publication +ERROR: relation "pub_test.testpub_nopk" is not part of the publication \d+ testpub_tbl1 Table "public.testpub_tbl1" Column | Type | Collation | Nullable | Default | Storage | Stats target | Description @@ -1671,7 +1675,7 @@ INSERT INTO pub_test.testpub_addpk VALUES(1, 11); CREATE UNIQUE INDEX testpub_addpk_id_idx ON pub_test.testpub_addpk(id); -- fail: UPDATE pub_test.testpub_addpk SET id = 2; -ERROR: cannot update table "testpub_addpk" because it does not have a replica identity and publishes updates +ERROR: cannot update table "pub_test.testpub_addpk" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. ALTER TABLE pub_test.testpub_addpk ADD PRIMARY KEY USING INDEX testpub_addpk_id_idx; -- now it should work: @@ -2033,7 +2037,7 @@ NOTICE: drop cascades to table "CURRENT_SCHEMA"."CURRENT_SCHEMA" INSERT INTO pub_test1.tbl VALUES(1, 'test'); -- fail UPDATE pub_test1.tbl SET id = 2; -ERROR: cannot update table "tbl" because it does not have a replica identity and publishes updates +ERROR: cannot update table "pub_test1.tbl" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. ALTER PUBLICATION testpub1_forschema DROP TABLES IN SCHEMA pub_test1; -- success @@ -2041,7 +2045,7 @@ UPDATE pub_test1.tbl SET id = 2; ALTER PUBLICATION testpub1_forschema SET TABLES IN SCHEMA pub_test1; -- fail UPDATE pub_test1.tbl SET id = 2; -ERROR: cannot update table "tbl" because it does not have a replica identity and publishes updates +ERROR: cannot update table "pub_test1.tbl" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. -- verify invalidation of partition table having parent and child tables in -- different schema @@ -2056,10 +2060,10 @@ CREATE PUBLICATION testpubpart_forschema FOR TABLES IN SCHEMA pub_testpart1; RESET client_min_messages; -- fail UPDATE pub_testpart1.parent1 set a = 1; -ERROR: cannot update table "child_parent1" because it does not have a replica identity and publishes updates +ERROR: cannot update table "pub_testpart2.child_parent1" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. UPDATE pub_testpart2.child_parent1 set a = 1; -ERROR: cannot update table "child_parent1" because it does not have a replica identity and publishes updates +ERROR: cannot update table "pub_testpart2.child_parent1" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. DROP PUBLICATION testpubpart_forschema; -- verify invalidation of partition tables for schema publication that has @@ -2073,13 +2077,13 @@ CREATE PUBLICATION testpubpart_forschema FOR TABLES IN SCHEMA pub_testpart2; RESET client_min_messages; -- fail UPDATE pub_testpart2.child_parent1 set a = 1; -ERROR: cannot update table "child_parent1" because it does not have a replica identity and publishes updates +ERROR: cannot update table "pub_testpart2.child_parent1" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. UPDATE pub_testpart2.parent2 set a = 1; -ERROR: cannot update table "child_parent2" because it does not have a replica identity and publishes updates +ERROR: cannot update table "pub_testpart1.child_parent2" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. UPDATE pub_testpart1.child_parent2 set a = 1; -ERROR: cannot update table "child_parent2" because it does not have a replica identity and publishes updates +ERROR: cannot update table "pub_testpart1.child_parent2" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. -- alter publication set 'TABLES IN SCHEMA' on an empty publication. SET client_min_messages = 'ERROR'; @@ -2324,13 +2328,13 @@ CREATE PUBLICATION pub1 FOR ALL TABLES; RESET client_min_messages; -- fail - missing REPLICA IDENTITY INSERT INTO testpub_insert_onconfl_no_ri VALUES (1, 1) ON CONFLICT (a) DO UPDATE SET b = 2; -ERROR: cannot update table "testpub_insert_onconfl_no_ri" because it does not have a replica identity and publishes updates +ERROR: cannot update table "public.testpub_insert_onconfl_no_ri" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. -- ok - no updates INSERT INTO testpub_insert_onconfl_no_ri VALUES (1, 1) ON CONFLICT DO NOTHING; -- fail - missing REPLICA IDENTITY in partition testpub_insert_onconfl_no_ri INSERT INTO testpub_insert_onconfl_parted VALUES (1, 1) ON CONFLICT (a) DO UPDATE SET b = 2; -ERROR: cannot update table "testpub_insert_onconfl_part_no_ri" because it does not have a replica identity and publishes updates +ERROR: cannot update table "public.testpub_insert_onconfl_part_no_ri" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. -- ok - no updates INSERT INTO testpub_insert_onconfl_parted VALUES (1, 1) ON CONFLICT DO NOTHING; @@ -2347,12 +2351,12 @@ RESET client_min_messages; -- fail - missing REPLICA IDENTITY MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1 WHEN MATCHED THEN UPDATE SET b = s.b; -ERROR: cannot update table "testpub_merge_no_ri" because it does not have a replica identity and publishes updates +ERROR: cannot update table "public.testpub_merge_no_ri" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE. -- fail - missing REPLICA IDENTITY MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1 WHEN MATCHED THEN DELETE; -ERROR: cannot delete from table "testpub_merge_no_ri" because it does not have a replica identity and publishes deletes +ERROR: cannot delete from table "public.testpub_merge_no_ri" because it does not have a replica identity and publishes deletes HINT: To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE. -- ok - insert and do nothing are not restricted MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1 diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index e3dbb2bc57c..4a718af3d70 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -997,7 +997,12 @@ CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_view; CREATE TEMPORARY TABLE testpub_temptbl(a int); -- fail - temporary table +-- The temporary schema in the error message is "pg_temp_N", but N varies with +-- each run, so show only the SQLSTATE. +-- (22023 means invalid_parameter_value) +\set VERBOSITY sqlstate CREATE PUBLICATION testpub_fortemptbl FOR TABLE testpub_temptbl; +\set VERBOSITY default DROP TABLE testpub_temptbl; CREATE UNLOGGED TABLE testpub_unloggedtbl(a int); diff --git a/src/test/subscription/t/027_nosuperuser.pl b/src/test/subscription/t/027_nosuperuser.pl index 322f5b4cc6a..949938b77ea 100644 --- a/src/test/subscription/t/027_nosuperuser.pl +++ b/src/test/subscription/t/027_nosuperuser.pl @@ -215,7 +215,7 @@ expect_failure( 2, 11, 13, - qr/ERROR: ( [A-Z0-9]+:)? user "regress_alice" cannot replicate into relation with row-level security enabled: "unpartitioned\w*"/msi, + qr/ERROR: ( [A-Z0-9]+:)? user "regress_alice" cannot replicate into relation with row-level security enabled: "alice.unpartitioned\w*"/msi, "replication of insert into table with forced rls fails"); # Since replication acts as the table owner, replication will succeed if we don't force it. @@ -236,7 +236,7 @@ expect_failure( 3, 11, 15, - qr/ERROR: ( [A-Z0-9]+:)? user "regress_alice" cannot replicate into relation with row-level security enabled: "unpartitioned\w*"/msi, + qr/ERROR: ( [A-Z0-9]+:)? user "regress_alice" cannot replicate into relation with row-level security enabled: "alice.unpartitioned\w*"/msi, "replication of update into table with forced rls fails"); $node_subscriber->safe_psql( 'postgres', qq( diff --git a/src/test/subscription/t/029_on_error.pl b/src/test/subscription/t/029_on_error.pl index 7d68759b6cd..0812b12cbe7 100644 --- a/src/test/subscription/t/029_on_error.pl +++ b/src/test/subscription/t/029_on_error.pl @@ -30,7 +30,7 @@ sub test_skip_lsn # ERROR with its CONTEXT when retrieving this information. my $contents = slurp_file($node_subscriber->logfile, $offset); $contents =~ - qr/conflict detected on relation "public.tbl".*\n.*DETAIL:.* Could not apply remote change.*\n.*Key already exists in unique index "tbl_pkey", modified by .*origin.* in transaction \d+ at .*: key .*, local row .*\n.*CONTEXT:.* for replication target relation "public.tbl" in transaction \d+, finished at ([[:xdigit:]]+\/[[:xdigit:]]+)/m + qr/conflict detected on relation "public.tbl".*\n.*DETAIL:.* Could not apply remote change.*\n.*Key already exists in unique index "public.tbl_pkey", modified by .*origin.* in transaction \d+ at .*: key .*, local row .*\n.*CONTEXT:.* for replication target relation "public.tbl" in transaction \d+, finished at ([[:xdigit:]]+\/[[:xdigit:]]+)/m or die "could not get error-LSN"; my $lsn = $1; diff --git a/src/test/subscription/t/034_temporal.pl b/src/test/subscription/t/034_temporal.pl index 66955e1b799..de6c782f7e2 100644 --- a/src/test/subscription/t/034_temporal.pl +++ b/src/test/subscription/t/034_temporal.pl @@ -134,14 +134,14 @@ $node_publisher->safe_psql( ($result, $stdout, $stderr) = $node_publisher->psql('postgres', "UPDATE temporal_no_key SET a = 'b' WHERE id = '[2,3)'"); is( $stderr, - qq(psql::1: ERROR: cannot update table "temporal_no_key" because it does not have a replica identity and publishes updates + qq(psql::1: ERROR: cannot update table "public.temporal_no_key" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.), "can't UPDATE temporal_no_key DEFAULT"); ($result, $stdout, $stderr) = $node_publisher->psql('postgres', "DELETE FROM temporal_no_key WHERE id = '[3,4)'"); is( $stderr, - qq(psql::1: ERROR: cannot delete from table "temporal_no_key" because it does not have a replica identity and publishes deletes + qq(psql::1: ERROR: cannot delete from table "public.temporal_no_key" because it does not have a replica identity and publishes deletes HINT: To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE.), "can't DELETE temporal_no_key DEFAULT"); @@ -189,14 +189,14 @@ $node_publisher->safe_psql( ($result, $stdout, $stderr) = $node_publisher->psql('postgres', "UPDATE temporal_unique SET a = 'b' WHERE id = '[2,3)'"); is( $stderr, - qq(psql::1: ERROR: cannot update table "temporal_unique" because it does not have a replica identity and publishes updates + qq(psql::1: ERROR: cannot update table "public.temporal_unique" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.), "can't UPDATE temporal_unique DEFAULT"); ($result, $stdout, $stderr) = $node_publisher->psql('postgres', "DELETE FROM temporal_unique WHERE id = '[3,4)'"); is( $stderr, - qq(psql::1: ERROR: cannot delete from table "temporal_unique" because it does not have a replica identity and publishes deletes + qq(psql::1: ERROR: cannot delete from table "public.temporal_unique" because it does not have a replica identity and publishes deletes HINT: To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE.), "can't DELETE temporal_unique DEFAULT"); @@ -540,14 +540,14 @@ $node_publisher->safe_psql( ($result, $stdout, $stderr) = $node_publisher->psql('postgres', "UPDATE temporal_no_key SET a = 'b' WHERE id = '[2,3)'"); is( $stderr, - qq(psql::1: ERROR: cannot update table "temporal_no_key" because it does not have a replica identity and publishes updates + qq(psql::1: ERROR: cannot update table "public.temporal_no_key" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.), "can't UPDATE temporal_no_key NOTHING"); ($result, $stdout, $stderr) = $node_publisher->psql('postgres', "DELETE FROM temporal_no_key WHERE id = '[3,4)'"); is( $stderr, - qq(psql::1: ERROR: cannot delete from table "temporal_no_key" because it does not have a replica identity and publishes deletes + qq(psql::1: ERROR: cannot delete from table "public.temporal_no_key" because it does not have a replica identity and publishes deletes HINT: To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE.), "can't DELETE temporal_no_key NOTHING"); @@ -572,14 +572,14 @@ $node_publisher->safe_psql( ($result, $stdout, $stderr) = $node_publisher->psql('postgres', "UPDATE temporal_pk SET a = 'b' WHERE id = '[2,3)'"); is( $stderr, - qq(psql::1: ERROR: cannot update table "temporal_pk" because it does not have a replica identity and publishes updates + qq(psql::1: ERROR: cannot update table "public.temporal_pk" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.), "can't UPDATE temporal_pk NOTHING"); ($result, $stdout, $stderr) = $node_publisher->psql('postgres', "DELETE FROM temporal_pk WHERE id = '[3,4)'"); is( $stderr, - qq(psql::1: ERROR: cannot delete from table "temporal_pk" because it does not have a replica identity and publishes deletes + qq(psql::1: ERROR: cannot delete from table "public.temporal_pk" because it does not have a replica identity and publishes deletes HINT: To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE.), "can't DELETE temporal_pk NOTHING"); @@ -604,14 +604,14 @@ $node_publisher->safe_psql( ($result, $stdout, $stderr) = $node_publisher->psql('postgres', "UPDATE temporal_unique SET a = 'b' WHERE id = '[2,3)'"); is( $stderr, - qq(psql::1: ERROR: cannot update table "temporal_unique" because it does not have a replica identity and publishes updates + qq(psql::1: ERROR: cannot update table "public.temporal_unique" because it does not have a replica identity and publishes updates HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.), "can't UPDATE temporal_unique NOTHING"); ($result, $stdout, $stderr) = $node_publisher->psql('postgres', "DELETE FROM temporal_unique WHERE id = '[3,4)'"); is( $stderr, - qq(psql::1: ERROR: cannot delete from table "temporal_unique" because it does not have a replica identity and publishes deletes + qq(psql::1: ERROR: cannot delete from table "public.temporal_unique" because it does not have a replica identity and publishes deletes HINT: To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE.), "can't DELETE temporal_unique NOTHING"); diff --git a/src/test/subscription/t/035_conflicts.pl b/src/test/subscription/t/035_conflicts.pl index 80177b214c0..eedcc67ae35 100644 --- a/src/test/subscription/t/035_conflicts.pl +++ b/src/test/subscription/t/035_conflicts.pl @@ -79,9 +79,9 @@ $node_publisher->safe_psql('postgres', $node_subscriber->wait_for_log( qr/conflict detected on relation \"public.conf_tab\": conflict=multiple_unique_conflicts.* .*Could not apply remote change: remote row \(2, 3, 4\).* -.*Key already exists in unique index \"conf_tab_pkey\", modified in transaction .*: key \(a\)=\(2\), local row \(2, 2, 2\).* -.*Key already exists in unique index \"conf_tab_b_key\", modified in transaction .*: key \(b\)=\(3\), local row \(3, 3, 3\).* -.*Key already exists in unique index \"conf_tab_c_key\", modified in transaction .*: key \(c\)=\(4\), local row \(4, 4, 4\)./, +.*Key already exists in unique index \"public.conf_tab_pkey\", modified in transaction .*: key \(a\)=\(2\), local row \(2, 2, 2\).* +.*Key already exists in unique index \"public.conf_tab_b_key\", modified in transaction .*: key \(b\)=\(3\), local row \(3, 3, 3\).* +.*Key already exists in unique index \"public.conf_tab_c_key\", modified in transaction .*: key \(c\)=\(4\), local row \(4, 4, 4\)./, $log_offset); pass('multiple_unique_conflicts detected during insert'); @@ -109,9 +109,9 @@ $node_publisher->safe_psql('postgres', $node_subscriber->wait_for_log( qr/conflict detected on relation \"public.conf_tab\": conflict=multiple_unique_conflicts.* .*Could not apply remote change: remote row \(6, 7, 8\), replica identity \(a\)=\(5\).* -.*Key already exists in unique index \"conf_tab_pkey\", modified in transaction .*: key \(a\)=\(6\), local row \(6, 6, 6\).* -.*Key already exists in unique index \"conf_tab_b_key\", modified in transaction .*: key \(b\)=\(7\), local row \(7, 7, 7\).* -.*Key already exists in unique index \"conf_tab_c_key\", modified in transaction .*: key \(c\)=\(8\), local row \(8, 8, 8\)./, +.*Key already exists in unique index \"public.conf_tab_pkey\", modified in transaction .*: key \(a\)=\(6\), local row \(6, 6, 6\).* +.*Key already exists in unique index \"public.conf_tab_b_key\", modified in transaction .*: key \(b\)=\(7\), local row \(7, 7, 7\).* +.*Key already exists in unique index \"public.conf_tab_c_key\", modified in transaction .*: key \(c\)=\(8\), local row \(8, 8, 8\)./, $log_offset); pass('multiple_unique_conflicts detected during update'); @@ -135,8 +135,8 @@ $node_publisher->safe_psql('postgres', $node_subscriber->wait_for_log( qr/conflict detected on relation \"public.conf_tab_2_p1\": conflict=multiple_unique_conflicts.* .*Could not apply remote change: remote row \(55, 2, 3\).* -.*Key already exists in unique index \"conf_tab_2_p1_pkey\", modified in transaction .*: key \(a\)=\(55\), local row \(55, 2, 3\).* -.*Key already exists in unique index \"conf_tab_2_p1_a_b_key\", modified in transaction .*: key \(a, b\)=\(55, 2\), local row \(55, 2, 3\)./, +.*Key already exists in unique index \"public.conf_tab_2_p1_pkey\", modified in transaction .*: key \(a\)=\(55\), local row \(55, 2, 3\).* +.*Key already exists in unique index \"public.conf_tab_2_p1_a_b_key\", modified in transaction .*: key \(a, b\)=\(55, 2\), local row \(55, 2, 3\)./, $log_offset); pass('multiple_unique_conflicts detected on a leaf partition during insert'); -- 2.47.3