From d561664c41418d1b066ac2627e888f3a902df010 Mon Sep 17 00:00:00 2001 From: YinZheng-Sun <1531931667@qq.com> Date: Mon, 17 Jul 2023 22:23:52 +0800 Subject: [PATCH] [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 | 95 ++++++++++++ src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/invisible.sql | 51 +++++++ 22 files changed, 393 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/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 0f506a4d665..796e7918968 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 32e59b784dd..f4b1d282b2d 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -381,6 +381,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, @@ -772,6 +776,9 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, if (colDef->identity) attr->attidentity = colDef->identity; + + if (colDef->is_invisible) + attr->attisinvisible = colDef->is_invisible; } /* @@ -3527,6 +3534,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. */ @@ -3816,6 +3831,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 */ @@ -4149,6 +4174,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; @@ -5646,6 +5677,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; @@ -6228,6 +6260,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 044d339e8c4..c9fae8e1b26 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -654,7 +654,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 @@ -700,7 +700,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 @@ -2178,6 +2178,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 { @@ -3422,6 +3438,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; @@ -3443,6 +3460,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; @@ -3461,6 +3479,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; @@ -3602,6 +3621,13 @@ ColConstraintElem: n->initially_valid = true; $$ = (Node *)n; } + | INVISIBLE + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_INVISIBLE; + n->location = @1; + $$ = (Node *)n; + } ; generated_when: @@ -12617,6 +12643,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; @@ -15796,6 +15823,7 @@ reserved_keyword: | INITIALLY | INTERSECT | INTO + | INVISIBLE | LATERAL_P | LEADING | LIMIT @@ -15826,6 +15854,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 82115b5f117..5c4ebef8b46 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -670,6 +670,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 */ @@ -1771,6 +1772,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 ) */ @@ -2104,6 +2107,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 4869b484c9d..41a79d5787a 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -220,6 +220,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) @@ -450,6 +451,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..2c211d034a7 --- /dev/null +++ b/src/test/regress/expected/invisible.out @@ -0,0 +1,95 @@ +-- +-- 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) + 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..00edc6d6e4e --- /dev/null +++ b/src/test/regress/sql/invisible.sql @@ -0,0 +1,51 @@ +-- +-- 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;