From b3cc6459db3c1e6edf24d0325edb9379ed9ec235 Mon Sep 17 00:00:00 2001 From: YinZheng-Sun <1531931667@qq.com> Date: Mon, 17 Jul 2023 22:23:52 +0800 Subject: [PATCH 1/4] [Feature] Support Invisible Columns --- src/backend/access/common/tupdesc.c | 4 + src/backend/catalog/heap.c | 19 +-- src/backend/commands/sequence.c | 1 + src/backend/commands/tablecmds.c | 144 ++++++++++++++++++ src/backend/nodes/makefuncs.c | 1 + src/backend/nodes/outfuncs.c | 4 + src/backend/parser/gram.y | 33 +++- src/backend/parser/parse_relation.c | 6 + src/backend/parser/parse_target.c | 2 +- src/backend/parser/parse_utilcmd.c | 14 ++ src/bin/pg_dump/pg_dump.c | 22 ++- src/bin/pg_dump/pg_dump.h | 1 + src/include/catalog/pg_attribute.h | 3 + src/include/catalog/pg_class.dat | 2 +- src/include/nodes/parsenodes.h | 4 + src/include/parser/kwlist.h | 2 + .../test_pg_dump/expected/test_pg_dump.out | 1 + .../modules/test_pg_dump/sql/test_pg_dump.sql | 1 + src/test/regress/expected/invisible.out | 98 ++++++++++++ .../regress/expected/invisible.out.replica | 105 +++++++++++++ src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/invisible.sql | 55 +++++++ 23 files changed, 505 insertions(+), 20 deletions(-) mode change 100644 => 100755 src/backend/catalog/heap.c mode change 100644 => 100755 src/bin/pg_dump/pg_dump.c mode change 100644 => 100755 src/bin/pg_dump/pg_dump.h create mode 100644 src/test/regress/expected/invisible.out create mode 100644 src/test/regress/expected/invisible.out.replica create mode 100644 src/test/regress/sql/invisible.sql diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 852b96ebbbf..e64a6f52ef0 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -462,6 +462,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) return false; if (attr1->attisdropped != attr2->attisdropped) return false; + if (attr1->attisinvisible != attr2->attisinvisible) + return false; if (attr1->attislocal != attr2->attislocal) return false; if (attr1->attinhcount != attr2->attinhcount) @@ -644,6 +646,7 @@ TupleDescInitEntry(TupleDesc desc, att->atthasmissing = false; att->attidentity = '\0'; att->attisdropped = false; + att->attisinvisible = false; att->attislocal = true; att->attinhcount = 0; /* attacl, attoptions and attfdwoptions are not present in tupledescs */ @@ -703,6 +706,7 @@ TupleDescInitBuiltinEntry(TupleDesc desc, att->atthasmissing = false; att->attidentity = '\0'; att->attisdropped = false; + att->attisinvisible = false; att->attislocal = true; att->attinhcount = 0; /* attacl, attoptions and attfdwoptions are not present in tupledescs */ diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c old mode 100644 new mode 100755 index 9602a429a22..f51da6b7314 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -152,37 +152,37 @@ static List *insert_ordered_unique_oid(List *list, Oid datum); static FormData_pg_attribute a1 = { 0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData), SelfItemPointerAttributeNumber, 0, -1, -1, - false, 'p', 's', true, false, false, '\0', false, true, 0 + false, 'p', 's', true, false, false, '\0', false, false, true, 0 }; static FormData_pg_attribute a2 = { 0, {"oid"}, OIDOID, 0, sizeof(Oid), ObjectIdAttributeNumber, 0, -1, -1, - true, 'p', 'i', true, false, false, '\0', false, true, 0 + true, 'p', 'i', true, false, false, '\0', false, false, true, 0 }; static FormData_pg_attribute a3 = { 0, {"xmin"}, XIDOID, 0, sizeof(TransactionId), MinTransactionIdAttributeNumber, 0, -1, -1, - true, 'p', 'i', true, false, false, '\0', false, true, 0 + true, 'p', 'i', true, false, false, '\0', false, false, true, 0 }; static FormData_pg_attribute a4 = { 0, {"cmin"}, CIDOID, 0, sizeof(CommandId), MinCommandIdAttributeNumber, 0, -1, -1, - true, 'p', 'i', true, false, false, '\0', false, true, 0 + true, 'p', 'i', true, false, false, '\0', false, false, true, 0 }; static FormData_pg_attribute a5 = { 0, {"xmax"}, XIDOID, 0, sizeof(TransactionId), MaxTransactionIdAttributeNumber, 0, -1, -1, - true, 'p', 'i', true, false, false, '\0', false, true, 0 + true, 'p', 'i', true, false, false, '\0', false, false, true, 0 }; static FormData_pg_attribute a6 = { 0, {"cmax"}, CIDOID, 0, sizeof(CommandId), MaxCommandIdAttributeNumber, 0, -1, -1, - true, 'p', 'i', true, false, false, '\0', false, true, 0 + true, 'p', 'i', true, false, false, '\0', false, false, true, 0 }; /* @@ -194,20 +194,20 @@ static FormData_pg_attribute a6 = { static FormData_pg_attribute a7 = { 0, {"tableoid"}, OIDOID, 0, sizeof(Oid), TableOidAttributeNumber, 0, -1, -1, - true, 'p', 'i', true, false, false, '\0', false, true, 0 + true, 'p', 'i', true, false, false, '\0', false, false, true, 0 }; /* POLAR px */ static FormData_pg_attribute a8 = { 0, {"_px_worker_id"}, INT4OID, 0, sizeof(PxWorkerId), PxWorkerIdAttributeNumber, 0, -1, -1, - true, 'p', 'i', true, false, false, '\0', false, true, 0 + true, 'p', 'i', true, false, false, '\0', false, false, true, 0 }; static FormData_pg_attribute a9 = { 0, {"_root_ctid"}, TIDOID, 0, sizeof(ItemPointerData), RootSelfItemPointerAttributeNumber, 0, -1, -1, - false, 'p', 's', true, false, false, '\0', false, true, 0 + false, 'p', 's', true, false, false, '\0', false, false, true, 0 }; /* POLAR end */ @@ -691,6 +691,7 @@ HeapTuple heaptuple_from_pg_attribute(Relation pg_attribute_rel, values[Anum_pg_attribute_atthasmissing - 1] = BoolGetDatum(new_attribute->atthasmissing); values[Anum_pg_attribute_attidentity - 1] = CharGetDatum(new_attribute->attidentity); values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(new_attribute->attisdropped); + values[Anum_pg_attribute_attisinvisible - 1] = BoolGetDatum(new_attribute->attisinvisible); values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal); values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount); values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation); diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 12d708099c6..af610c60be2 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -162,6 +162,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) ColumnDef *coldef = makeNode(ColumnDef); coldef->inhcount = 0; + coldef->is_invisible = false; coldef->is_local = true; coldef->is_not_null = true; coldef->is_from_type = false; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 84689117de5..ba3d45c9724 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -382,6 +382,10 @@ static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMO static void ATPrepSetNotNull(Relation rel, bool recurse, bool recursing); static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, const char *colName, LOCKMODE lockmode); +static ObjectAddress ATExecSetVisible(AlteredTableInfo *tab, Relation rel, + const char *colName, LOCKMODE lockmode); +static ObjectAddress ATExecSetInvisible(AlteredTableInfo *tab, Relation rel, + const char *colName, LOCKMODE lockmode); static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode); static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName, @@ -782,6 +786,9 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, if (colDef->identity) attr->attidentity = colDef->identity; + + if (colDef->is_invisible) + attr->attisinvisible = colDef->is_invisible; } /* @@ -3537,6 +3544,14 @@ AlterTableGetLockLevel(List *cmds) cmd_lockmode = AccessExclusiveLock; break; + /* + * changing visibilty can affect concurrent SELECTs + */ + case AT_SetInvisible: + case AT_SetVisible: + cmd_lockmode = AccessExclusiveLock; + break; + /* * These subcommands affect write operations only. */ @@ -3826,6 +3841,16 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, /* No command-specific prep needed */ pass = AT_PASS_ADD_CONSTR; break; + case AT_SetVisible: /* ALTER COLUMN SET VISIBLE */ + ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); + /* No command-specific prep needed */ + pass = AT_PASS_ADD_CONSTR; + case AT_SetInvisible: /* ALTER COLUMN SET INVISIBLE */ + ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); + /* No command-specific prep needed */ + pass = AT_PASS_ADD_CONSTR; case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */ ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); /* Performs own permission checks */ @@ -4159,6 +4184,12 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ address = ATExecSetNotNull(tab, rel, cmd->name, lockmode); break; + case AT_SetVisible: /* ALTER COLUMN SET VISIBLE */ + address = ATExecSetVisible(tab, rel, cmd->name, lockmode); + break; + case AT_SetInvisible: /* ALTER COLUMN SET INVISIBLE */ + address = ATExecSetInvisible(tab, rel, cmd->name, lockmode); + break; case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */ address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode); break; @@ -5656,6 +5687,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, attribute.atthasmissing = false; attribute.attidentity = colDef->identity; attribute.attisdropped = false; + attribute.attisinvisible = colDef->is_invisible; attribute.attislocal = colDef->is_local; attribute.attinhcount = colDef->inhcount; attribute.attcollation = collOid; @@ -6238,6 +6270,118 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, return address; } +/* + * Return the address of the modified column. If the column was already Invisible, + * InvalidObjectAddress is returned. + */ +static ObjectAddress +ATExecSetVisible(AlteredTableInfo *tab, Relation rel, + const char *colName, LOCKMODE lockmode) +{ + HeapTuple tuple; + AttrNumber attnum; + Relation attr_rel; + ObjectAddress address; + + /* + * lookup the attribute + */ + attr_rel = heap_open(AttributeRelationId, RowExclusiveLock); + + tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName); + + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + colName, RelationGetRelationName(rel)))); + + attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum; + + /* Prevent them from altering a system attribute */ + if (attnum <= 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot alter system column \"%s\"", + colName))); + + if (((Form_pg_attribute) GETSTRUCT(tuple))->attisinvisible) + { + ((Form_pg_attribute) GETSTRUCT(tuple))->attisinvisible = false; + + CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple); + + ObjectAddressSubSet(address, RelationRelationId, + RelationGetRelid(rel), attnum); + } + else + address = InvalidObjectAddress; + + InvokeObjectPostAlterHook(RelationRelationId, + RelationGetRelid(rel), attnum); + + heap_close(attr_rel, RowExclusiveLock); + heap_freetuple(tuple); + + return address; +} + +/* + * Return the address of the modified column. If the column was already Invisible, + * InvalidObjectAddress is returned. + */ +static ObjectAddress +ATExecSetInvisible(AlteredTableInfo *tab, Relation rel, + const char *colName, LOCKMODE lockmode) +{ + HeapTuple tuple; + AttrNumber attnum; + Relation attr_rel; + ObjectAddress address; + + /* + * lookup the attribute + */ + attr_rel = heap_open(AttributeRelationId, RowExclusiveLock); + + tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName); + + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + colName, RelationGetRelationName(rel)))); + + attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum; + + /* Prevent them from altering a system attribute */ + if (attnum <= 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot alter system column \"%s\"", + colName))); + + if (!((Form_pg_attribute) GETSTRUCT(tuple))->attisinvisible) + { + ((Form_pg_attribute) GETSTRUCT(tuple))->attisinvisible = true; + + CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple); + + ObjectAddressSubSet(address, RelationRelationId, + RelationGetRelid(rel), attnum); + } + else + address = InvalidObjectAddress; + + InvokeObjectPostAlterHook(RelationRelationId, + RelationGetRelid(rel), attnum); + + heap_close(attr_rel, RowExclusiveLock); + heap_freetuple(tuple); + + return address; +} + /* * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT * diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 4a2e669a864..df5d3a5f3b7 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -493,6 +493,7 @@ makeColumnDef(const char *colname, Oid typeOid, int32 typmod, Oid collOid) n->colname = pstrdup(colname); n->typeName = makeTypeNameFromOid(typeOid, typmod); n->inhcount = 0; + n->is_invisible = false; n->is_local = true; n->is_not_null = false; n->is_from_type = false; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 14db48dddc8..655f3906489 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -3733,6 +3733,10 @@ _outConstraint(StringInfo str, const Constraint *node) WRITE_CHAR_FIELD(generated_when); break; + case CONSTR_INVISIBLE: + appendStringInfoString(str, "INVISIBLE"); + break; + case CONSTR_CHECK: appendStringInfoString(str, "CHECK"); WRITE_BOOL_FIELD(is_no_inherit); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index bfe11e9e716..02d727e0788 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -656,7 +656,7 @@ static bool polar_is_ignore_user_defined_tablespace(char *tablespace_name); IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IMPORT_P IN_P INCLUDE INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER - INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION + INTERSECT INTERVAL INTO INVISIBLE INVOKER IS ISNULL ISOLATION JOIN @@ -702,7 +702,7 @@ static bool polar_is_ignore_user_defined_tablespace(char *tablespace_name); UNTIL UPDATE USER USING VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING - VERBOSE VERSION_P VIEW VIEWS VOLATILE + VERBOSE VERSION_P VIEW VIEWS VISIBLE VOLATILE WEIGHT WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE @@ -2185,6 +2185,22 @@ alter_table_cmd: n->def = (Node *) $5; $$ = (Node *)n; } + /* ALTER TABLE ALTER [COLUMN] SET INVISIBLE */ + | ALTER opt_column ColId SET INVISIBLE + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetInvisible; + n->name = $3; + $$ = (Node *)n; + } + /* ALTER TABLE ALTER [COLUMN] SET VISIBLE */ + | ALTER opt_column ColId SET VISIBLE + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetVisible; + n->name = $3; + $$ = (Node *)n; + } /* ALTER TABLE ALTER [COLUMN] RESET ( column_parameter = value [, ... ] ) */ | ALTER opt_column ColId RESET reloptions { @@ -3429,6 +3445,7 @@ columnDef: ColId Typename create_generic_options ColQualList n->colname = $1; n->typeName = $2; n->inhcount = 0; + n->is_invisible = false; n->is_local = true; n->is_not_null = false; n->is_from_type = false; @@ -3450,6 +3467,7 @@ columnOptions: ColId ColQualList n->colname = $1; n->typeName = NULL; n->inhcount = 0; + n->is_invisible = false; n->is_local = true; n->is_not_null = false; n->is_from_type = false; @@ -3468,6 +3486,7 @@ columnOptions: ColId ColQualList n->colname = $1; n->typeName = NULL; n->inhcount = 0; + n->is_invisible = false; n->is_local = true; n->is_not_null = false; n->is_from_type = false; @@ -3609,6 +3628,13 @@ ColConstraintElem: n->initially_valid = true; $$ = (Node *)n; } + | INVISIBLE + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_INVISIBLE; + n->location = @1; + $$ = (Node *)n; + } ; generated_when: @@ -12624,6 +12650,7 @@ TableFuncElement: ColId Typename opt_collate_clause n->colname = $1; n->typeName = $2; n->inhcount = 0; + n->is_invisible = false; n->is_local = true; n->is_not_null = false; n->is_from_type = false; @@ -15820,6 +15847,7 @@ reserved_keyword: | INITIALLY | INTERSECT | INTO + | INVISIBLE | LATERAL_P | LEADING | LIMIT @@ -15850,6 +15878,7 @@ reserved_keyword: | USER | USING | VARIADIC + | VISIBLE | WHEN | WHERE | WINDOW diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index bf5df26009a..b18163770b5 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -2573,6 +2573,12 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, int count, int offset, continue; } + if (attr->attisinvisible) { + if (aliascell) + aliascell = lnext(aliascell); + continue; + } + if (colnames) { char *label; diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index fabc13cc1d5..b888a8ead02 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -997,7 +997,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) attr = TupleDescAttr(pstate->p_target_relation->rd_att, i); - if (attr->attisdropped) + if (attr->attisdropped || attr->attisinvisible) continue; col = makeNode(ResTarget); diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index e711c7d934e..b30e1cdde1a 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -529,6 +529,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) bool saw_nullable; bool saw_default; bool saw_identity; + bool saw_invisible; ListCell *clist; cxt->columns = lappend(cxt->columns, column); @@ -636,6 +637,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) saw_nullable = false; saw_default = false; saw_identity = false; + saw_invisible = false; foreach(clist, column->constraints) { @@ -716,6 +718,18 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) break; } + case CONSTR_INVISIBLE: + if (saw_invisible) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("multiple invisible specifications for column \"%s\" of table \"%s\"", + column->colname, cxt->relation->relname), + parser_errposition(cxt->pstate, + constraint->location))); + column->is_invisible = true; + saw_invisible = true; + break; + case CONSTR_CHECK: cxt->ckconstraints = lappend(cxt->ckconstraints, constraint); break; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c old mode 100644 new mode 100755 index 2fc672501fd..0cb22423c33 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -8229,6 +8229,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) int i_atthasdef; int i_attidentity; int i_attisdropped; + int i_attisinvisible; int i_attlen; int i_attalign; int i_attislocal; @@ -8270,7 +8271,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) /* atthasmissing and attmissingval are new in 11 */ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, " "a.attstattarget, a.attstorage, t.typstorage, " - "a.attnotnull, a.atthasdef, a.attisdropped, " + "a.attnotnull, a.atthasdef, a.attisdropped, a.attisinvisible, " "a.attlen, a.attalign, a.attislocal, " "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " "array_to_string(a.attoptions, ', ') AS attoptions, " @@ -8299,7 +8300,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) */ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, " "a.attstattarget, a.attstorage, t.typstorage, " - "a.attnotnull, a.atthasdef, a.attisdropped, " + "a.attnotnull, a.atthasdef, a.attisdropped, a.attisinvisible, " "a.attlen, a.attalign, a.attislocal, " "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " "array_to_string(a.attoptions, ', ') AS attoptions, " @@ -8327,7 +8328,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) */ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, " "a.attstattarget, a.attstorage, t.typstorage, " - "a.attnotnull, a.atthasdef, a.attisdropped, " + "a.attnotnull, a.atthasdef, a.attisdropped, a.attisinvisible, " "a.attlen, a.attalign, a.attislocal, " "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " "array_to_string(a.attoptions, ', ') AS attoptions, " @@ -8357,7 +8358,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) */ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, " "a.attstattarget, a.attstorage, t.typstorage, " - "a.attnotnull, a.atthasdef, a.attisdropped, " + "a.attnotnull, a.atthasdef, a.attisdropped, a.attisinvisible, " "a.attlen, a.attalign, a.attislocal, " "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " "array_to_string(a.attoptions, ', ') AS attoptions, " @@ -8377,7 +8378,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) /* attoptions is new in 9.0 */ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, " "a.attstattarget, a.attstorage, t.typstorage, " - "a.attnotnull, a.atthasdef, a.attisdropped, " + "a.attnotnull, a.atthasdef, a.attisdropped, a.attisinvisible, " "a.attlen, a.attalign, a.attislocal, " "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " "array_to_string(a.attoptions, ', ') AS attoptions, " @@ -8396,7 +8397,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) /* need left join here to not fail on dropped columns ... */ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, " "a.attstattarget, a.attstorage, t.typstorage, " - "a.attnotnull, a.atthasdef, a.attisdropped, " + "a.attnotnull, a.atthasdef, a.attisdropped, a.attisinvisible, " "a.attlen, a.attalign, a.attislocal, " "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " "'' AS attoptions, 0 AS attcollation, " @@ -8425,6 +8426,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) i_atthasdef = PQfnumber(res, "atthasdef"); i_attidentity = PQfnumber(res, "attidentity"); i_attisdropped = PQfnumber(res, "attisdropped"); + i_attisinvisible = PQfnumber(res, "attisinvisible"); i_attlen = PQfnumber(res, "attlen"); i_attalign = PQfnumber(res, "attalign"); i_attislocal = PQfnumber(res, "attislocal"); @@ -8442,6 +8444,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) tbinfo->typstorage = (char *) pg_malloc(ntups * sizeof(char)); tbinfo->attidentity = (char *) pg_malloc(ntups * sizeof(char)); tbinfo->attisdropped = (bool *) pg_malloc(ntups * sizeof(bool)); + tbinfo->attisinvisible = (bool *) pg_malloc(ntups * sizeof(bool)); tbinfo->attlen = (int *) pg_malloc(ntups * sizeof(int)); tbinfo->attalign = (char *) pg_malloc(ntups * sizeof(char)); tbinfo->attislocal = (bool *) pg_malloc(ntups * sizeof(bool)); @@ -8469,6 +8472,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables) tbinfo->attidentity[j] = (i_attidentity >= 0 ? *(PQgetvalue(res, j, i_attidentity)) : '\0'); tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS); tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't'); + tbinfo->attisinvisible[j] = (PQgetvalue(res, j, i_attisinvisible)[0] == 't'); tbinfo->attlen[j] = atoi(PQgetvalue(res, j, i_attlen)); tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign)); tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't'); @@ -15682,6 +15686,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) { bool print_default; bool print_notnull; + bool print_invisible; /* * Default value --- suppress if to be printed separately. @@ -15698,6 +15703,8 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) (!tbinfo->inhNotNull[j] || tbinfo->ispartition || dopt->binary_upgrade)); + print_invisible = tbinfo->attisinvisible[j]; + /* * Skip column if fully defined by reloftype, except in * binary upgrade @@ -15758,6 +15765,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) if (print_notnull) appendPQExpBufferStr(q, " NOT NULL"); + + if (print_invisible) + appendPQExpBufferStr(q, " INVISIBLE"); } } diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h old mode 100644 new mode 100755 index f9d88cf8989..2c32cb4c999 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -310,6 +310,7 @@ typedef struct _tableInfo char *attstorage; /* attribute storage scheme */ char *typstorage; /* type storage scheme */ bool *attisdropped; /* true if attr is dropped; don't dump it */ + bool *attisinvisible; /* true if attr is invisible */ char *attidentity; int *attlen; /* attribute length, used by binary_upgrade */ char *attalign; /* attribute align, used by binary_upgrade */ diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h index dc36753ede0..0aef16e5774 100644 --- a/src/include/catalog/pg_attribute.h +++ b/src/include/catalog/pg_attribute.h @@ -143,6 +143,9 @@ CATALOG(pg_attribute,1249,AttributeRelationId) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BK /* Is dropped (ie, logically invisible) or not */ bool attisdropped BKI_DEFAULT(f); + /* Is Invisible or not */ + bool attisinvisible BKI_DEFAULT(f); + /* * This flag specifies whether this column has ever had a local * definition. It is set for normal non-inherited columns, but also for diff --git a/src/include/catalog/pg_class.dat b/src/include/catalog/pg_class.dat index 9fffdef3790..49643b934e5 100644 --- a/src/include/catalog/pg_class.dat +++ b/src/include/catalog/pg_class.dat @@ -36,7 +36,7 @@ reloftype => '0', relowner => 'PGUID', relam => '0', relfilenode => '0', reltablespace => '0', relpages => '0', reltuples => '0', relallvisible => '0', reltoastrelid => '0', relhasindex => 'f', relisshared => 'f', - relpersistence => 'p', relkind => 'r', relnatts => '24', relchecks => '0', + relpersistence => 'p', relkind => 'r', relnatts => '25', relchecks => '0', relhasoids => 'f', relhasrules => 'f', relhastriggers => 'f', relhassubclass => 'f', relrowsecurity => 'f', relforcerowsecurity => 'f', relispopulated => 't', relreplident => 'n', relispartition => 'f', diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 8f4f842ba4f..d7884a6a86e 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -671,6 +671,7 @@ typedef struct ColumnDef char *colname; /* name of column */ TypeName *typeName; /* type of column */ int inhcount; /* number of times column is inherited */ + bool is_invisible; /* invisible constraint */ bool is_local; /* column has local (non-inherited) def'n */ bool is_not_null; /* NOT NULL constraint specified? */ bool is_from_type; /* column definition came from table type */ @@ -1772,6 +1773,8 @@ typedef enum AlterTableType AT_ColumnDefault, /* alter column default */ AT_DropNotNull, /* alter column drop not null */ AT_SetNotNull, /* alter column set not null */ + AT_SetInvisible, /* alter column set invisible */ + AT_SetVisible, /* alter column set visible */ AT_SetStatistics, /* alter column set statistics */ AT_SetOptions, /* alter column set ( options ) */ AT_ResetOptions, /* alter column reset ( options ) */ @@ -2105,6 +2108,7 @@ typedef enum ConstrType /* types of constraints */ CONSTR_NOTNULL, CONSTR_DEFAULT, CONSTR_IDENTITY, + CONSTR_INVISIBLE, /* Oracle 12c new feature*/ CONSTR_CHECK, CONSTR_PRIMARY, CONSTR_UNIQUE, diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index f832e8d9fb7..d8b20ffddaa 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -222,6 +222,7 @@ PG_KEYWORD("integer", INTEGER, COL_NAME_KEYWORD) PG_KEYWORD("intersect", INTERSECT, RESERVED_KEYWORD) PG_KEYWORD("interval", INTERVAL, COL_NAME_KEYWORD) PG_KEYWORD("into", INTO, RESERVED_KEYWORD) +PG_KEYWORD("invisible", INVISIBLE, RESERVED_KEYWORD) PG_KEYWORD("invoker", INVOKER, UNRESERVED_KEYWORD) PG_KEYWORD("is", IS, TYPE_FUNC_NAME_KEYWORD) PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD) @@ -452,6 +453,7 @@ PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD) PG_KEYWORD("version", VERSION_P, UNRESERVED_KEYWORD) PG_KEYWORD("view", VIEW, UNRESERVED_KEYWORD) PG_KEYWORD("views", VIEWS, UNRESERVED_KEYWORD) +PG_KEYWORD("visible", VISIBLE, RESERVED_KEYWORD) PG_KEYWORD("volatile", VOLATILE, UNRESERVED_KEYWORD) PG_KEYWORD("weight", WEIGHT, UNRESERVED_KEYWORD) PG_KEYWORD("when", WHEN, RESERVED_KEYWORD) diff --git a/src/test/modules/test_pg_dump/expected/test_pg_dump.out b/src/test/modules/test_pg_dump/expected/test_pg_dump.out index c9bc6f76258..8f07cf1eaa5 100644 --- a/src/test/modules/test_pg_dump/expected/test_pg_dump.out +++ b/src/test/modules/test_pg_dump/expected/test_pg_dump.out @@ -50,6 +50,7 @@ CREATE FOREIGN TABLE ft1 ( c1 integer OPTIONS ("param 1" 'val1') NOT NULL, c2 text OPTIONS (param2 'val2', param3 'val3') CHECK (c2 <> ''), c3 date, + c4 integer INVISIBLE, CHECK (c3 BETWEEN '1994-01-01'::date AND '1994-01-31'::date) ) SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value'); REVOKE EXECUTE ON FUNCTION test_pg_dump(int) FROM PUBLIC; diff --git a/src/test/modules/test_pg_dump/sql/test_pg_dump.sql b/src/test/modules/test_pg_dump/sql/test_pg_dump.sql index e463dec4040..3ba804e01ae 100644 --- a/src/test/modules/test_pg_dump/sql/test_pg_dump.sql +++ b/src/test/modules/test_pg_dump/sql/test_pg_dump.sql @@ -58,6 +58,7 @@ CREATE FOREIGN TABLE ft1 ( c1 integer OPTIONS ("param 1" 'val1') NOT NULL, c2 text OPTIONS (param2 'val2', param3 'val3') CHECK (c2 <> ''), c3 date, + c4 integer INVISIBLE, CHECK (c3 BETWEEN '1994-01-01'::date AND '1994-01-31'::date) ) SERVER s0 OPTIONS (delimiter ',', quote '"', "be quoted" 'value'); diff --git a/src/test/regress/expected/invisible.out b/src/test/regress/expected/invisible.out new file mode 100644 index 00000000000..172357d8964 --- /dev/null +++ b/src/test/regress/expected/invisible.out @@ -0,0 +1,98 @@ +-- +-- INVISIBLE +-- +CREATE TABLE TEST_INVISIBLE_TBL1( + a INT INVISIBLE DEFAULT 1, + b INT NOT NULL, + c CHAR +); +CREATE TABLE TEST_INVISIBLE_TBL2( + a INT NOT NULL INVISIBLE, + b INT +); +CREATE TABLE TEST_INVISIBLE_TBL3( + a INT INVISIBLE, + b INT +); +INSERT INTO TEST_INVISIBLE_TBL1 VALUES (1, 1, 'A'); +ERROR: INSERT has more expressions than target columns +LINE 1: INSERT INTO TEST_INVISIBLE_TBL1 VALUES (1, 1, 'A'); + ^ +INSERT INTO TEST_INVISIBLE_TBL1 VALUES (1, 'A'); +INSERT INTO TEST_INVISIBLE_TBL1 VALUES ('A'); +ERROR: invalid input syntax for integer: "A" +LINE 1: INSERT INTO TEST_INVISIBLE_TBL1 VALUES ('A'); + ^ +INSERT INTO TEST_INVISIBLE_TBL1 (a, b, c) VALUES(2, 2, 'B'); +SELECT * FROM TEST_INVISIBLE_TBL1; + b | c +---+--- + 1 | A + 2 | B +(2 rows) + +SELECT a, b, c FROM TEST_INVISIBLE_TBL1; + a | b | c +---+---+--- + 1 | 1 | A + 2 | 2 | B +(2 rows) + +INSERT INTO TEST_INVISIBLE_TBL2 VALUES (1); +ERROR: null value in column "a" violates not-null constraint +DETAIL: Failing row contains (null, 1). +INSERT INTO TEST_INVISIBLE_TBL2 VALUES (1, 1); +ERROR: INSERT has more expressions than target columns +LINE 1: INSERT INTO TEST_INVISIBLE_TBL2 VALUES (1, 1); + ^ +INSERT INTO TEST_INVISIBLE_TBL2 (a, b) VALUES (1, 1); +SELECT * FROM TEST_INVISIBLE_TBL2; + b +--- + 1 +(1 row) + +SELECT a, b FROM TEST_INVISIBLE_TBL2; + a | b +---+--- + 1 | 1 +(1 row) + +INSERT INTO TEST_INVISIBLE_TBL3 VALUES (1); +INSERT INTO TEST_INVISIBLE_TBL3 (a, b) VALUES (1, 1); +SELECT * FROM TEST_INVISIBLE_TBL3; + b +--- + 1 + 1 +(2 rows) + +SELECT a, b FROM TEST_INVISIBLE_TBL3; + a | b +---+--- + | 1 + 1 | 1 +(2 rows) + +-- +-- TEST ALTER TABLE +-- +ALTER TABLE TEST_INVISIBLE_TBL1 ALTER COLUMN b SET INVISIBLE; +SELECT * FROM TEST_INVISIBLE_TBL1; + c +--- + A + B +(2 rows) + +ALTER TABLE TEST_INVISIBLE_TBL1 ALTER COLUMN a SET VISIBLE; +SELECT * FROM TEST_INVISIBLE_TBL1; + a | c +---+--- + 1 | A + 2 | B +(2 rows) + +DROP TABLE TEST_INVISIBLE_TBL1; +DROP TABLE TEST_INVISIBLE_TBL2; +DROP TABLE TEST_INVISIBLE_TBL3; diff --git a/src/test/regress/expected/invisible.out.replica b/src/test/regress/expected/invisible.out.replica new file mode 100644 index 00000000000..a716f0b49f1 --- /dev/null +++ b/src/test/regress/expected/invisible.out.replica @@ -0,0 +1,105 @@ +-- +-- INVISIBLE +-- +CREATE TABLE TEST_INVISIBLE_TBL1( + a INT INVISIBLE DEFAULT 1, + b INT NOT NULL, + c CHAR +); +ERROR: cannot execute CREATE TABLE in a read-only transaction +CREATE TABLE TEST_INVISIBLE_TBL2( + a INT NOT NULL INVISIBLE, + b INT +); +ERROR: cannot execute CREATE TABLE in a read-only transaction +CREATE TABLE TEST_INVISIBLE_TBL3( + a INT INVISIBLE, + b INT +); +ERROR: cannot execute INSERT in a read-only transaction +INSERT INTO TEST_INVISIBLE_TBL1 VALUES (1, 1, 'A'); +ERROR: cannot execute INSERT in a read-only transaction +INSERT INTO TEST_INVISIBLE_TBL1 VALUES (1, 'A'); +ERROR: cannot execute INSERT in a read-only transaction +INSERT INTO TEST_INVISIBLE_TBL1 VALUES ('A'); +ERROR: invalid input syntax for integer: "A" +ERROR: cannot execute INSERT in a read-only transaction +INSERT INTO TEST_INVISIBLE_TBL1 (a, b, c) VALUES(2, 2, 'B'); +ERROR: cannot execute INSERT in a read-only transaction +SELECT * FROM TEST_INVISIBLE_TBL1; + b | c +---+--- + 1 | A + 2 | B +(2 rows) + +SELECT a, b, c FROM TEST_INVISIBLE_TBL1; + a | b | c +---+---+--- + 1 | 1 | A + 2 | 2 | B +(2 rows) + +INSERT INTO TEST_INVISIBLE_TBL2 VALUES (1); +ERROR: cannot execute INSERT in a read-only transaction +INSERT INTO TEST_INVISIBLE_TBL2 VALUES (1, 1); +ERROR: cannot execute INSERT in a read-only transaction +INSERT INTO TEST_INVISIBLE_TBL2 (a, b) VALUES (1, 1); +ERROR: cannot execute INSERT in a read-only transaction +SELECT * FROM TEST_INVISIBLE_TBL2; + b +--- + 1 +(1 row) + +SELECT a, b FROM TEST_INVISIBLE_TBL2; + a | b +---+--- + 1 | 1 +(1 row) + +INSERT INTO TEST_INVISIBLE_TBL3 VALUES (1); +ERROR: cannot execute INSERT in a read-only transaction +INSERT INTO TEST_INVISIBLE_TBL3 (a, b) VALUES (1, 1); +ERROR: cannot execute INSERT in a read-only transaction +SELECT * FROM TEST_INVISIBLE_TBL3; + b +--- + 1 + 1 +(2 rows) + +SELECT a, b FROM TEST_INVISIBLE_TBL3; + a | b +---+--- + | 1 + 1 | 1 +(2 rows) + +-- +-- TEST ALTER TABLE +-- +ALTER TABLE TEST_INVISIBLE_TBL1 ALTER COLUMN b SET INVISIBLE; +ERROR: cannot execute ALTER TABLE in a read-only transaction +SELECT * FROM TEST_INVISIBLE_TBL1; + c +--- + A + B +(2 rows) + +ALTER TABLE TEST_INVISIBLE_TBL1 ALTER COLUMN a SET VISIBLE; +ERROR: cannot execute ALTER TABLE in a read-only transaction +SELECT * FROM TEST_INVISIBLE_TBL1; + a | c +---+--- + 1 | A + 2 | B +(2 rows) + +DROP TABLE TEST_INVISIBLE_TBL1; +ERROR: cannot execute DROP TABLE in a read-only transaction +DROP TABLE TEST_INVISIBLE_TBL2; +ERROR: cannot execute DROP TABLE in a read-only transaction +DROP TABLE TEST_INVISIBLE_TBL3; +ERROR: cannot execute DROP TABLE in a read-only transaction diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 44f40d2d338..ab162d22738 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -40,7 +40,7 @@ test: point lseg line box path polygon circle date time timetz timestamp timesta # geometry depends on point, lseg, box, path, polygon and circle # horology depends on interval, timetz, timestamp, timestamptz, reltime and abstime # ---------- -test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions +test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions invisible # ---------- # These four each depend on the previous one diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index daf04b3eec1..e9fcdaff053 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -36,6 +36,7 @@ test: money test: rangetypes test: pg_lsn test: regproc +test: invisible test: strings test: numerology test: point diff --git a/src/test/regress/sql/invisible.sql b/src/test/regress/sql/invisible.sql new file mode 100644 index 00000000000..e515a0bd19a --- /dev/null +++ b/src/test/regress/sql/invisible.sql @@ -0,0 +1,55 @@ +-- +-- INVISIBLE +-- + +CREATE TABLE TEST_INVISIBLE_TBL1( + a INT INVISIBLE DEFAULT 1, + b INT NOT NULL, + c CHAR +); + +CREATE TABLE TEST_INVISIBLE_TBL2( + a INT NOT NULL INVISIBLE, + b INT +); + +CREATE TABLE TEST_INVISIBLE_TBL3( + a INT INVISIBLE, + b INT +); + + +INSERT INTO TEST_INVISIBLE_TBL1 VALUES (1, 1, 'A'); +INSERT INTO TEST_INVISIBLE_TBL1 VALUES (1, 'A'); +INSERT INTO TEST_INVISIBLE_TBL1 VALUES ('A'); +INSERT INTO TEST_INVISIBLE_TBL1 (a, b, c) VALUES(2, 2, 'B'); + +SELECT * FROM TEST_INVISIBLE_TBL1; +SELECT a, b, c FROM TEST_INVISIBLE_TBL1; + +INSERT INTO TEST_INVISIBLE_TBL2 VALUES (1); +INSERT INTO TEST_INVISIBLE_TBL2 VALUES (1, 1); +INSERT INTO TEST_INVISIBLE_TBL2 (a, b) VALUES (1, 1); + +SELECT * FROM TEST_INVISIBLE_TBL2; +SELECT a, b FROM TEST_INVISIBLE_TBL2; + +INSERT INTO TEST_INVISIBLE_TBL3 VALUES (1); +INSERT INTO TEST_INVISIBLE_TBL3 (a, b) VALUES (1, 1); + +SELECT * FROM TEST_INVISIBLE_TBL3; +SELECT a, b FROM TEST_INVISIBLE_TBL3; + +-- +-- TEST ALTER TABLE +-- + +ALTER TABLE TEST_INVISIBLE_TBL1 ALTER COLUMN b SET INVISIBLE; +SELECT * FROM TEST_INVISIBLE_TBL1; + +ALTER TABLE TEST_INVISIBLE_TBL1 ALTER COLUMN a SET VISIBLE; +SELECT * FROM TEST_INVISIBLE_TBL1; + +DROP TABLE TEST_INVISIBLE_TBL1; +DROP TABLE TEST_INVISIBLE_TBL2; +DROP TABLE TEST_INVISIBLE_TBL3; \ No newline at end of file From cd9a63f6b009ecd6572ae59bbdbbecf0c42d70c9 Mon Sep 17 00:00:00 2001 From: Yinzheng-Sun <1531931667@qq.com> Date: Wed, 19 Jul 2023 09:28:25 +0800 Subject: [PATCH 2/4] fix: alter table --- src/backend/commands/tablecmds.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index ba3d45c9724..be0168b40d1 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -3846,11 +3846,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); /* No command-specific prep needed */ pass = AT_PASS_ADD_CONSTR; + break; case AT_SetInvisible: /* ALTER COLUMN SET INVISIBLE */ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); /* No command-specific prep needed */ pass = AT_PASS_ADD_CONSTR; + break; case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */ ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); /* Performs own permission checks */ From af1722cd2b717e55db85ee8be271288f3d18be1f Mon Sep 17 00:00:00 2001 From: Yinzheng-Sun <1531931667@qq.com> Date: Thu, 17 Aug 2023 16:12:35 +0800 Subject: [PATCH 3/4] add modify grammar, consider partition table, visible coulumns should be more than one --- src/backend/commands/sequence.c | 1 - src/backend/commands/tablecmds.c | 81 +++++++++++++++++++ src/backend/parser/gram.y | 38 ++++++--- src/include/parser/kwlist.h | 1 + src/test/regress/expected/invisible.out | 8 +- .../regress/expected/invisible.out.replica | 10 ++- src/test/regress/sql/invisible.sql | 7 +- 7 files changed, 129 insertions(+), 17 deletions(-) diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index af610c60be2..12d708099c6 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -162,7 +162,6 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) ColumnDef *coldef = makeNode(ColumnDef); coldef->inhcount = 0; - coldef->is_invisible = false; coldef->is_local = true; coldef->is_not_null = true; coldef->is_from_type = false; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index be0168b40d1..0bb7c3ff7ec 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -382,8 +382,10 @@ static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMO static void ATPrepSetNotNull(Relation rel, bool recurse, bool recursing); static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, const char *colName, LOCKMODE lockmode); +static void ATPrepSetVisible(Relation rel, bool recurse, bool recursing); static ObjectAddress ATExecSetVisible(AlteredTableInfo *tab, Relation rel, const char *colName, LOCKMODE lockmode); +static void ATPrepSetInvisible(Relation rel, bool recurse, bool recursing); static ObjectAddress ATExecSetInvisible(AlteredTableInfo *tab, Relation rel, const char *colName, LOCKMODE lockmode); static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, @@ -560,6 +562,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, static char *validnsps[] = HEAP_RELOPT_NAMESPACES; Oid ofTypeId; ObjectAddress address; + int invisibleColumnCount; /* * Truncate relname to appropriate length (probably a waste of time, as @@ -744,6 +747,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, rawDefaults = NIL; cookedDefaults = NIL; attnum = 0; + invisibleColumnCount = 0; foreach(listptr, stmt->tableElts) { @@ -788,9 +792,20 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, attr->attidentity = colDef->identity; if (colDef->is_invisible) + { attr->attisinvisible = colDef->is_invisible; + invisibleColumnCount++; + } } + /* + * table must have at least one column that is not invisible + */ + if (invisibleColumnCount == attnum && attnum != 0) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("table must have at least one column that is not invisible"))); + /* * Create the relation. Inherited defaults and constraints are passed in * for immediate handling --- since they don't need parsing, they can be @@ -2003,6 +2018,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence, * merge the column options into the column from the type */ coldef->is_not_null = restdef->is_not_null; + coldef->is_invisible = restdef->is_invisible; coldef->raw_default = restdef->raw_default; coldef->cooked_default = restdef->cooked_default; coldef->constraints = restdef->constraints; @@ -2246,6 +2262,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence, def->inhcount = 1; def->is_local = false; def->is_not_null = attribute->attnotnull; + def->is_invisible = attribute->attisinvisible; def->is_from_type = false; def->storage = attribute->attstorage; def->raw_default = NULL; @@ -3843,12 +3860,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, break; case AT_SetVisible: /* ALTER COLUMN SET VISIBLE */ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATPrepSetVisible(rel, recurse, recursing); ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); /* No command-specific prep needed */ pass = AT_PASS_ADD_CONSTR; break; case AT_SetInvisible: /* ALTER COLUMN SET INVISIBLE */ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + ATPrepSetInvisible(rel, recurse, recursing); ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); /* No command-specific prep needed */ pass = AT_PASS_ADD_CONSTR; @@ -6272,6 +6291,25 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, return address; } +static void +ATPrepSetVisible(Relation rel, bool recurse, bool recursing) +{ + /* + * If the parent is a partitioned table, Set Visible + * constraints must be added to the child tables. + */ + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + PartitionDesc partdesc = RelationGetPartitionDesc(rel); + + if (partdesc && partdesc->nparts > 0 && !recurse && !recursing) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("cannot add constraint to only the partitioned table when partitions exist"), + errhint("Do not specify the ONLY keyword."))); + } +} + /* * Return the address of the modified column. If the column was already Invisible, * InvalidObjectAddress is returned. @@ -6328,6 +6366,25 @@ ATExecSetVisible(AlteredTableInfo *tab, Relation rel, return address; } +static void +ATPrepSetInvisible(Relation rel, bool recurse, bool recursing) +{ + /* + * If the parent is a partitioned table, Set Invisible + * constraints must be added to the child tables. + */ + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + PartitionDesc partdesc = RelationGetPartitionDesc(rel); + + if (partdesc && partdesc->nparts > 0 && !recurse && !recursing) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("cannot add constraint to only the partitioned table when partitions exist"), + errhint("Do not specify the ONLY keyword."))); + } +} + /* * Return the address of the modified column. If the column was already Invisible, * InvalidObjectAddress is returned. @@ -6365,6 +6422,30 @@ ATExecSetInvisible(AlteredTableInfo *tab, Relation rel, if (!((Form_pg_attribute) GETSTRUCT(tuple))->attisinvisible) { + TupleDesc tupleDesc = RelationGetDescr(rel); + int numattrs = tupleDesc->natts; + int i; + bool visible_only = true; + Form_pg_attribute attr; + + for (i = 0; i < numattrs; ++i) { + if (i == attnum - 1) { + continue; + } + + attr = TupleDescAttr(tupleDesc, i); + + if (!attr->attisinvisible) { + visible_only = false; + break; + } + } + + if (visible_only) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("table must have at least one column that is not invisible"))); + ((Form_pg_attribute) GETSTRUCT(tuple))->attisinvisible = true; CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 02d727e0788..208d8814f45 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -666,7 +666,7 @@ static bool polar_is_ignore_user_defined_tablespace(char *tablespace_name); LEADER LEADING LEAKPROOF LEARNER LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED LOGS - MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE + MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MODIFY MONTH_P MOVE NAME_P NAMES NATIONAL NATURAL NCHAR NEW NEXT NO NODE NONE NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF @@ -2201,6 +2201,22 @@ alter_table_cmd: n->name = $3; $$ = (Node *)n; } + /* ALTER TABLE MODIFY VISIBLE */ + | MODIFY opt_column ColId VISIBLE + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetVisible; + n->name = $3; + $$ = (Node *)n; + } + /* ALTER TABLE MODIFY INVISIBLE */ + | MODIFY opt_column ColId INVISIBLE + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetInvisible; + n->name = $3; + $$ = (Node *)n; + } /* ALTER TABLE ALTER [COLUMN] RESET ( column_parameter = value [, ... ] ) */ | ALTER opt_column ColId RESET reloptions { @@ -3445,7 +3461,6 @@ columnDef: ColId Typename create_generic_options ColQualList n->colname = $1; n->typeName = $2; n->inhcount = 0; - n->is_invisible = false; n->is_local = true; n->is_not_null = false; n->is_from_type = false; @@ -3467,7 +3482,6 @@ columnOptions: ColId ColQualList n->colname = $1; n->typeName = NULL; n->inhcount = 0; - n->is_invisible = false; n->is_local = true; n->is_not_null = false; n->is_from_type = false; @@ -3486,7 +3500,6 @@ columnOptions: ColId ColQualList n->colname = $1; n->typeName = NULL; n->inhcount = 0; - n->is_invisible = false; n->is_local = true; n->is_not_null = false; n->is_from_type = false; @@ -3547,7 +3560,14 @@ ColConstraint: * or be part of a_expr NOT LIKE or similar constructs). */ ColConstraintElem: - NOT NULL_P + INVISIBLE + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_INVISIBLE; + n->location = @1; + $$ = (Node *)n; + } + | NOT NULL_P { Constraint *n = makeNode(Constraint); n->contype = CONSTR_NOTNULL; @@ -3628,13 +3648,6 @@ ColConstraintElem: n->initially_valid = true; $$ = (Node *)n; } - | INVISIBLE - { - Constraint *n = makeNode(Constraint); - n->contype = CONSTR_INVISIBLE; - n->location = @1; - $$ = (Node *)n; - } ; generated_when: @@ -15545,6 +15558,7 @@ unreserved_keyword: | MINUTE_P | MINVALUE | MODE + | MODIFY | MONTH_P | MOVE | NAME_P diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index d8b20ffddaa..9091e541618 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -261,6 +261,7 @@ PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD) PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD) PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD) PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD) +PG_KEYWORD("modify", MODIFY, UNRESERVED_KEYWORD) PG_KEYWORD("month", MONTH_P, UNRESERVED_KEYWORD) PG_KEYWORD("move", MOVE, UNRESERVED_KEYWORD) PG_KEYWORD("name", NAME_P, UNRESERVED_KEYWORD) diff --git a/src/test/regress/expected/invisible.out b/src/test/regress/expected/invisible.out index 172357d8964..e25fc1c62db 100644 --- a/src/test/regress/expected/invisible.out +++ b/src/test/regress/expected/invisible.out @@ -14,6 +14,10 @@ CREATE TABLE TEST_INVISIBLE_TBL3( a INT INVISIBLE, b INT ); +create TABLE TEST_INVISIBLE_TBL4( + a int INVISIBLE +); +ERROR: table must have at least one column that is not invisible INSERT INTO TEST_INVISIBLE_TBL1 VALUES (1, 1, 'A'); ERROR: INSERT has more expressions than target columns LINE 1: INSERT INTO TEST_INVISIBLE_TBL1 VALUES (1, 1, 'A'); @@ -78,6 +82,8 @@ SELECT a, b FROM TEST_INVISIBLE_TBL3; -- TEST ALTER TABLE -- ALTER TABLE TEST_INVISIBLE_TBL1 ALTER COLUMN b SET INVISIBLE; +ALTER TABLE TEST_INVISIBLE_TBL1 ALTER COLUMN c SET INVISIBLE; +ERROR: table must have at least one column that is not invisible SELECT * FROM TEST_INVISIBLE_TBL1; c --- @@ -85,7 +91,7 @@ SELECT * FROM TEST_INVISIBLE_TBL1; B (2 rows) -ALTER TABLE TEST_INVISIBLE_TBL1 ALTER COLUMN a SET VISIBLE; +ALTER TABLE TEST_INVISIBLE_TBL1 MODIFY a VISIBLE; SELECT * FROM TEST_INVISIBLE_TBL1; a | c ---+--- diff --git a/src/test/regress/expected/invisible.out.replica b/src/test/regress/expected/invisible.out.replica index a716f0b49f1..5d160b86ad0 100644 --- a/src/test/regress/expected/invisible.out.replica +++ b/src/test/regress/expected/invisible.out.replica @@ -16,7 +16,11 @@ CREATE TABLE TEST_INVISIBLE_TBL3( a INT INVISIBLE, b INT ); -ERROR: cannot execute INSERT in a read-only transaction +ERROR: cannot execute CREATE TABLE in a read-only transaction +create TABLE TEST_INVISIBLE_TBL4( + a int INVISIBLE +); +ERROR: cannot execute CREATE TABLE in a read-only transaction INSERT INTO TEST_INVISIBLE_TBL1 VALUES (1, 1, 'A'); ERROR: cannot execute INSERT in a read-only transaction INSERT INTO TEST_INVISIBLE_TBL1 VALUES (1, 'A'); @@ -81,6 +85,8 @@ SELECT a, b FROM TEST_INVISIBLE_TBL3; -- ALTER TABLE TEST_INVISIBLE_TBL1 ALTER COLUMN b SET INVISIBLE; ERROR: cannot execute ALTER TABLE in a read-only transaction +ALTER TABLE TEST_INVISIBLE_TBL1 ALTER COLUMN c SET INVISIBLE; +ERROR: cannot execute ALTER TABLE in a read-only transaction SELECT * FROM TEST_INVISIBLE_TBL1; c --- @@ -88,7 +94,7 @@ SELECT * FROM TEST_INVISIBLE_TBL1; B (2 rows) -ALTER TABLE TEST_INVISIBLE_TBL1 ALTER COLUMN a SET VISIBLE; +ALTER TABLE TEST_INVISIBLE_TBL1 MODIFY a VISIBLE; ERROR: cannot execute ALTER TABLE in a read-only transaction SELECT * FROM TEST_INVISIBLE_TBL1; a | c diff --git a/src/test/regress/sql/invisible.sql b/src/test/regress/sql/invisible.sql index e515a0bd19a..f680bf4fa98 100644 --- a/src/test/regress/sql/invisible.sql +++ b/src/test/regress/sql/invisible.sql @@ -18,6 +18,10 @@ CREATE TABLE TEST_INVISIBLE_TBL3( b INT ); +create TABLE TEST_INVISIBLE_TBL4( + a int INVISIBLE +); + INSERT INTO TEST_INVISIBLE_TBL1 VALUES (1, 1, 'A'); INSERT INTO TEST_INVISIBLE_TBL1 VALUES (1, 'A'); @@ -45,9 +49,10 @@ SELECT a, b FROM TEST_INVISIBLE_TBL3; -- ALTER TABLE TEST_INVISIBLE_TBL1 ALTER COLUMN b SET INVISIBLE; +ALTER TABLE TEST_INVISIBLE_TBL1 ALTER COLUMN c SET INVISIBLE; SELECT * FROM TEST_INVISIBLE_TBL1; -ALTER TABLE TEST_INVISIBLE_TBL1 ALTER COLUMN a SET VISIBLE; +ALTER TABLE TEST_INVISIBLE_TBL1 MODIFY a VISIBLE; SELECT * FROM TEST_INVISIBLE_TBL1; DROP TABLE TEST_INVISIBLE_TBL1; From 1f24ed2655d58fa049f8a2a476578a5e67069b6b Mon Sep 17 00:00:00 2001 From: Yinzheng-Sun <1531931667@qq.com> Date: Wed, 27 Sep 2023 16:25:36 +0800 Subject: [PATCH 4/4] add join test --- src/test/regress/expected/invisible.out | 29 +++++++++++++++++++ .../regress/expected/invisible.out.replica | 29 +++++++++++++++++++ src/test/regress/sql/invisible.sql | 10 +++++++ 3 files changed, 68 insertions(+) diff --git a/src/test/regress/expected/invisible.out b/src/test/regress/expected/invisible.out index e25fc1c62db..f97b09a3dbf 100644 --- a/src/test/regress/expected/invisible.out +++ b/src/test/regress/expected/invisible.out @@ -35,6 +35,20 @@ SELECT * FROM TEST_INVISIBLE_TBL1; 2 | B (2 rows) +SELECT TEST_INVISIBLE_TBL1.* FROM TEST_INVISIBLE_TBL1; + b | c +---+--- + 1 | A + 2 | B +(2 rows) + +SELECT (TEST_INVISIBLE_TBL1).* FROM TEST_INVISIBLE_TBL1; + b | c +---+--- + 1 | A + 2 | B +(2 rows) + SELECT a, b, c FROM TEST_INVISIBLE_TBL1; a | b | c ---+---+--- @@ -78,6 +92,21 @@ SELECT a, b FROM TEST_INVISIBLE_TBL3; 1 | 1 (2 rows) +-- +-- TEST JOIN +-- +SELECT * from TEST_INVISIBLE_TBL1 as t1, TEST_INVISIBLE_TBL3 as t3 where t1.a = t3.a; + b | c | b +---+---+--- + 1 | A | 1 +(1 row) + +SELECT t1.a from TEST_INVISIBLE_TBL1 as t1, TEST_INVISIBLE_TBL3 as t3 where t1.a = t3.a; + a +--- + 1 +(1 row) + -- -- TEST ALTER TABLE -- diff --git a/src/test/regress/expected/invisible.out.replica b/src/test/regress/expected/invisible.out.replica index 5d160b86ad0..3c10836e69e 100644 --- a/src/test/regress/expected/invisible.out.replica +++ b/src/test/regress/expected/invisible.out.replica @@ -37,6 +37,20 @@ SELECT * FROM TEST_INVISIBLE_TBL1; 2 | B (2 rows) +SELECT TEST_INVISIBLE_TBL1.* FROM TEST_INVISIBLE_TBL1; + b | c +---+--- + 1 | A + 2 | B +(2 rows) + +SELECT (TEST_INVISIBLE_TBL1).* FROM TEST_INVISIBLE_TBL1; + b | c +---+--- + 1 | A + 2 | B +(2 rows) + SELECT a, b, c FROM TEST_INVISIBLE_TBL1; a | b | c ---+---+--- @@ -80,6 +94,21 @@ SELECT a, b FROM TEST_INVISIBLE_TBL3; 1 | 1 (2 rows) +-- +-- TEST JOIN +-- +SELECT * from TEST_INVISIBLE_TBL1 as t1, TEST_INVISIBLE_TBL3 as t3 where t1.a = t3.a; + b | c | b +---+---+--- + 1 | A | 1 +(1 row) + +SELECT t1.a from TEST_INVISIBLE_TBL1 as t1, TEST_INVISIBLE_TBL3 as t3 where t1.a = t3.a; + a +--- + 1 +(1 row) + -- -- TEST ALTER TABLE -- diff --git a/src/test/regress/sql/invisible.sql b/src/test/regress/sql/invisible.sql index f680bf4fa98..d3d6bb0db36 100644 --- a/src/test/regress/sql/invisible.sql +++ b/src/test/regress/sql/invisible.sql @@ -29,6 +29,9 @@ INSERT INTO TEST_INVISIBLE_TBL1 VALUES ('A'); INSERT INTO TEST_INVISIBLE_TBL1 (a, b, c) VALUES(2, 2, 'B'); SELECT * FROM TEST_INVISIBLE_TBL1; +SELECT TEST_INVISIBLE_TBL1.* FROM TEST_INVISIBLE_TBL1; +SELECT (TEST_INVISIBLE_TBL1).* FROM TEST_INVISIBLE_TBL1; + SELECT a, b, c FROM TEST_INVISIBLE_TBL1; INSERT INTO TEST_INVISIBLE_TBL2 VALUES (1); @@ -44,6 +47,13 @@ INSERT INTO TEST_INVISIBLE_TBL3 (a, b) VALUES (1, 1); SELECT * FROM TEST_INVISIBLE_TBL3; SELECT a, b FROM TEST_INVISIBLE_TBL3; +-- +-- TEST JOIN +-- + +SELECT * from TEST_INVISIBLE_TBL1 as t1, TEST_INVISIBLE_TBL3 as t3 where t1.a = t3.a; +SELECT t1.a from TEST_INVISIBLE_TBL1 as t1, TEST_INVISIBLE_TBL3 as t3 where t1.a = t3.a; + -- -- TEST ALTER TABLE --