From 45b0f39326bfa0fde69fb1b56c8e1a48148fea37 Mon Sep 17 00:00:00 2001 From: TotaJ Date: Mon, 28 Mar 2022 10:20:46 +0800 Subject: [PATCH] Fix internal schema publish. --- src/common/backend/catalog/pg_publication.cpp | 94 +++++++++++++++---- src/test/regress/input/publication.source | 11 +++ src/test/regress/output/publication.source | 17 ++++ 3 files changed, 105 insertions(+), 17 deletions(-) diff --git a/src/common/backend/catalog/pg_publication.cpp b/src/common/backend/catalog/pg_publication.cpp index 9900f25bd..897080008 100644 --- a/src/common/backend/catalog/pg_publication.cpp +++ b/src/common/backend/catalog/pg_publication.cpp @@ -43,6 +43,40 @@ #include "utils/rel.h" #include "utils/syscache.h" +/* check if namespace is internal schema, internal schema doesn't need publish */ +static inline bool IsInternalSchema(Oid relnamespace) +{ + Assert(CSTORE_NAMESPACE < PG_PKG_SERVICE_NAMESPACE); + Assert(PG_PKG_SERVICE_NAMESPACE < PG_DBEPERF_NAMESPACE); + Assert(PG_DBEPERF_NAMESPACE < PG_SNAPSHOT_NAMESPACE); + Assert(PG_SNAPSHOT_NAMESPACE < PG_BLOCKCHAIN_NAMESPACE); + Assert(PG_BLOCKCHAIN_NAMESPACE < PG_DB4AI_NAMESPACE); + Assert(PG_DB4AI_NAMESPACE < PG_PLDEBUG_NAMESPACE); +#ifndef ENABLE_MULTIPLE_NODES + Assert(PG_PLDEBUG_NAMESPACE < DBE_PLDEVELOPER_NAMESPACE); + Assert(DBE_PLDEVELOPER_NAMESPACE < PG_SQLADVISOR_NAMESPACE); +#else + Assert(PG_PLDEBUG_NAMESPACE < PG_SQLADVISOR_NAMESPACE); +#endif + + /* please make sure the list is ordered */ + static Oid internalSchemaList[] = { + CSTORE_NAMESPACE, + PG_PKG_SERVICE_NAMESPACE, + PG_DBEPERF_NAMESPACE, + PG_SNAPSHOT_NAMESPACE, + PG_BLOCKCHAIN_NAMESPACE, + PG_DB4AI_NAMESPACE, + PG_PLDEBUG_NAMESPACE, +#ifndef ENABLE_MULTIPLE_NODES + DBE_PLDEVELOPER_NAMESPACE, +#endif + PG_SQLADVISOR_NAMESPACE + }; + static size_t count = lengthof(internalSchemaList); + return bsearch(&relnamespace, &internalSchemaList, count, sizeof(Oid), oid_cmp) != NULL; +} + /* * Check if relation can be in given publication and throws appropriate * error if not. @@ -66,6 +100,19 @@ static void check_publication_add_relation(Relation targetrel) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("table \"%s\" cannot be replicated", RelationGetRelationName(targetrel)), errdetail("Temporary and unlogged relations cannot be replicated."))); + + if (IsInternalSchema(targetrel->rd_rel->relnamespace)) { + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("\"%s\" is in internal schema", RelationGetRelationName(targetrel)), + errdetail("\"%s\" is a internal schema, table in this schema cannot be replicated.", + get_namespace_name(RelationGetNamespace(targetrel))))); + } + + if (!RelationIsRowFormat(targetrel)) { + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("\"%s\" is not a row table", RelationGetRelationName(targetrel)), + errdetail("Only row tables can be added to publications."))); + } } /* @@ -102,30 +149,43 @@ static Publication *GetPublication(Oid pubid) * Returns if relation represented by oid and Form_pg_class entry * is publishable. * - * Does same checks as the above, but does not need relation to be opened + * Does same checks as check_publication_add_relation, but does not need relation to be opened * and also does not throw errors. */ -static bool is_publishable_class(Oid relid, Form_pg_class reltuple) +static bool is_publishable_class(Oid relid, HeapTuple tuple, Relation rel) { - /* internal namespace, doesn't need to publish */ - if (reltuple->relnamespace == CSTORE_NAMESPACE || reltuple->relnamespace == PG_PKG_SERVICE_NAMESPACE || -#ifndef ENABLE_MULTIPLE_NODES - reltuple->relnamespace == DBE_PLDEVELOPER_NAMESPACE || -#endif - reltuple->relnamespace == PG_SNAPSHOT_NAMESPACE || reltuple->relnamespace == PG_SQLADVISOR_NAMESPACE || - reltuple->relnamespace == PG_BLOCKCHAIN_NAMESPACE || reltuple->relnamespace == PG_DB4AI_NAMESPACE || - reltuple->relnamespace == PG_PLDEBUG_NAMESPACE) { - return false; + Form_pg_class reltuple = NULL; + if (rel != NULL) { + reltuple = rel->rd_rel; + } else if (tuple != NULL) { + reltuple = (Form_pg_class)GETSTRUCT(tuple); + } else { + /* should not happen */ + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("null input of pg_class heap tuple or relation, oid: %u", relid))); } - return reltuple->relkind == RELKIND_RELATION && !IsCatalogClass(relid, reltuple) && - reltuple->relpersistence == RELPERSISTENCE_PERMANENT && + + if (IsInternalSchema(reltuple->relnamespace) || reltuple->relkind != RELKIND_RELATION || + IsCatalogClass(relid, reltuple) || reltuple->relpersistence != RELPERSISTENCE_PERMANENT || /* * Also exclude any tables created as part of initdb. This mainly * affects the preinstalled information_schema. * Note that IsCatalogClass() only checks for these inside pg_catalog * and toast schemas. */ - relid >= FirstNormalObjectId; + relid < FirstNormalObjectId) { + return false; + } + + /* check whether is row table */ + bool isRowTable = true; + if (rel != NULL) { + isRowTable = RelationIsRowFormat(rel); + } else { + /* already checkd tuple before */ + isRowTable = CheckRelOrientationByPgClassTuple(tuple, GetDefaultPgClassDesc(), ORIENTATION_ROW); + } + return isRowTable; } /* @@ -314,9 +374,9 @@ static List *GetAllTablesPublicationRelations(void) while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Oid relid = HeapTupleGetOid(tuple); - Form_pg_class relForm = (Form_pg_class)GETSTRUCT(tuple); - if (is_publishable_class(relid, relForm)) + if (is_publishable_class(relid, tuple, NULL)) { result = lappend_oid(result, relid); + } } heap_endscan(scan); @@ -432,5 +492,5 @@ Datum pg_get_publication_tables(PG_FUNCTION_ARGS) */ bool is_publishable_relation(Relation rel) { - return is_publishable_class(RelationGetRelid(rel), rel->rd_rel); + return is_publishable_class(RelationGetRelid(rel), NULL, rel); } diff --git a/src/test/regress/input/publication.source b/src/test/regress/input/publication.source index 2c25c8fdf..0a981470e 100644 --- a/src/test/regress/input/publication.source +++ b/src/test/regress/input/publication.source @@ -16,10 +16,15 @@ SET SESSION AUTHORIZATION 'regress_publication_user' PASSWORD 'Abcdef@123'; CREATE TABLE testpub_tbl1 (id int primary key, data text); CREATE TABLE testpub_tbl2 (id int primary key, data text); CREATE TABLE testpub_tbl3 (id int primary key, data text); +CREATE TABLE testpub_tbl_col(id int) WITH (orientation=column); --- create publication CREATE PUBLICATION testpub_default; ------ for all tables CREATE PUBLICATION testpub_foralltables FOR ALL TABLES; +------ after create all table publication, do IUD to non-pk column table, should ok +INSERT INTO testpub_tbl_col values(1); +UPDATE testpub_tbl_col set id = 66 where id = 1; +DELETE FROM testpub_tbl_col; CREATE TABLE testpub_tbl4 (id int primary key, data text); select pubname, tablename from pg_publication_tables where pubname='testpub_foralltables' AND tablename like 'testpub_%' order by tablename; ------ for only table testpub_tbl1 @@ -52,6 +57,11 @@ ALTER PUBLICATION testpub_default SET TABLE testpub_tbl2; select pubname, tablename from pg_publication_tables where pubname='testpub_default'; ------ drop table ALTER PUBLICATION testpub_multitbls DROP TABLE ONLY testpub_tbl2; +------ fail - add column table +ALTER PUBLICATION testpub_multitbls ADD TABLE testpub_tbl_col; +------ fail - add internal schema table +ALTER PUBLICATION testpub_multitbls ADD TABLE db4ai.snapshot; +ALTER PUBLICATION testpub_multitbls ADD TABLE dbe_pldeveloper.gs_source; select pubname, tablename from pg_publication_tables where pubname='testpub_multitbls'; ------ SET (parameter xxx) ALTER PUBLICATION testpub_default SET (publish='insert, delete'); @@ -77,6 +87,7 @@ DROP PUBLICATION IF EXISTS testpub_nonexists; DROP TABLE testpub_tbl2; DROP TABLE testpub_tbl3; DROP TABLE testpub_tbl4; +DROP TABLE testpub_tbl_col; DROP VIEW testpub_view; DROP PUBLICATION IF EXISTS testpub_default; DROP PUBLICATION IF EXISTS testpub_only_tbl1; diff --git a/src/test/regress/output/publication.source b/src/test/regress/output/publication.source index a3b6718bf..f09425f09 100644 --- a/src/test/regress/output/publication.source +++ b/src/test/regress/output/publication.source @@ -47,10 +47,15 @@ CREATE TABLE testpub_tbl2 (id int primary key, data text); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "testpub_tbl2_pkey" for table "testpub_tbl2" CREATE TABLE testpub_tbl3 (id int primary key, data text); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "testpub_tbl3_pkey" for table "testpub_tbl3" +CREATE TABLE testpub_tbl_col(id int) WITH (orientation=column); --- create publication CREATE PUBLICATION testpub_default; ------ for all tables CREATE PUBLICATION testpub_foralltables FOR ALL TABLES; +------ after create all table publication, do IUD to non-pk column table, should ok +INSERT INTO testpub_tbl_col values(1); +UPDATE testpub_tbl_col set id = 66 where id = 1; +DELETE FROM testpub_tbl_col; CREATE TABLE testpub_tbl4 (id int primary key, data text); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "testpub_tbl4_pkey" for table "testpub_tbl4" select pubname, tablename from pg_publication_tables where pubname='testpub_foralltables' AND tablename like 'testpub_%' order by tablename; @@ -122,6 +127,17 @@ select pubname, tablename from pg_publication_tables where pubname='testpub_defa ------ drop table ALTER PUBLICATION testpub_multitbls DROP TABLE ONLY testpub_tbl2; +------ fail - add column table +ALTER PUBLICATION testpub_multitbls ADD TABLE testpub_tbl_col; +ERROR: "testpub_tbl_col" is not a row table +DETAIL: Only row tables can be added to publications. +------ fail - add internal schema table +ALTER PUBLICATION testpub_multitbls ADD TABLE db4ai.snapshot; +ERROR: "snapshot" is in internal schema +DETAIL: "db4ai" is a internal schema, table in this schema cannot be replicated. +ALTER PUBLICATION testpub_multitbls ADD TABLE dbe_pldeveloper.gs_source; +ERROR: "gs_source" is in internal schema +DETAIL: "dbe_pldeveloper" is a internal schema, table in this schema cannot be replicated. select pubname, tablename from pg_publication_tables where pubname='testpub_multitbls'; pubname | tablename -------------------+-------------- @@ -172,6 +188,7 @@ NOTICE: publication "testpub_nonexists" does not exist, skipping, skipping DROP TABLE testpub_tbl2; DROP TABLE testpub_tbl3; DROP TABLE testpub_tbl4; +DROP TABLE testpub_tbl_col; DROP VIEW testpub_view; DROP PUBLICATION IF EXISTS testpub_default; DROP PUBLICATION IF EXISTS testpub_only_tbl1;