1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-28 18:48:04 +03:00

Identity columns

This is the SQL standard-conforming variant of PostgreSQL's serial
columns.  It fixes a few usability issues that serial columns have:

- CREATE TABLE / LIKE copies default but refers to same sequence
- cannot add/drop serialness with ALTER TABLE
- dropping default does not drop sequence
- need to grant separate privileges to sequence
- other slight weirdnesses because serial is some kind of special macro

Reviewed-by: Vitaly Burovoy <vitaly.burovoy@gmail.com>
This commit is contained in:
Peter Eisentraut
2017-04-06 08:33:16 -04:00
parent 6bad580d9e
commit 3217327053
57 changed files with 2140 additions and 202 deletions

View File

@@ -150,6 +150,7 @@ CreateTupleDescCopy(TupleDesc tupdesc)
memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_FIXED_PART_SIZE);
desc->attrs[i]->attnotnull = false;
desc->attrs[i]->atthasdef = false;
desc->attrs[i]->attidentity = '\0';
}
desc->tdtypeid = tupdesc->tdtypeid;
@@ -257,6 +258,7 @@ TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
/* since we're not copying constraints or defaults, clear these */
dst->attrs[dstAttno - 1]->attnotnull = false;
dst->attrs[dstAttno - 1]->atthasdef = false;
dst->attrs[dstAttno - 1]->attidentity = '\0';
}
/*
@@ -401,6 +403,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->atthasdef != attr2->atthasdef)
return false;
if (attr1->attidentity != attr2->attidentity)
return false;
if (attr1->attisdropped != attr2->attisdropped)
return false;
if (attr1->attislocal != attr2->attislocal)
@@ -534,6 +538,7 @@ TupleDescInitEntry(TupleDesc desc,
att->attnotnull = false;
att->atthasdef = false;
att->attidentity = '\0';
att->attisdropped = false;
att->attislocal = true;
att->attinhcount = 0;
@@ -591,6 +596,7 @@ TupleDescInitBuiltinEntry(TupleDesc desc,
att->attnotnull = false;
att->atthasdef = false;
att->attidentity = '\0';
att->attisdropped = false;
att->attislocal = true;
att->attinhcount = 0;

View File

@@ -1929,6 +1929,13 @@ find_expr_references_walker(Node *node,
context->addrs);
/* fall through to examine arguments */
}
else if (IsA(node, NextValueExpr))
{
NextValueExpr *nve = (NextValueExpr *) node;
add_object_address(OCLASS_CLASS, nve->seqid, 0,
context->addrs);
}
return expression_tree_walker(node, find_expr_references_walker,
(void *) context);

View File

@@ -409,6 +409,7 @@ sub emit_pgattr_row
attcacheoff => '-1',
atttypmod => '-1',
atthasdef => 'f',
attidentity => '',
attisdropped => 'f',
attislocal => 't',
attinhcount => '0',
@@ -424,7 +425,7 @@ sub bki_insert
my $row = shift;
my @attnames = @_;
my $oid = $row->{oid} ? "OID = $row->{oid} " : '';
my $bki_values = join ' ', map $row->{$_}, @attnames;
my $bki_values = join ' ', map { $_ eq '' ? '""' : $_ } map $row->{$_}, @attnames;
printf $bki "insert %s( %s)\n", $oid, $bki_values;
}
@@ -435,10 +436,14 @@ sub emit_schemapg_row
my $row = shift;
my @bool_attrs = @_;
# Replace empty string by zero char constant
$row->{attidentity} ||= '\0';
# Supply appropriate quoting for these fields.
$row->{attname} = q|{"| . $row->{attname} . q|"}|;
$row->{attstorage} = q|'| . $row->{attstorage} . q|'|;
$row->{attalign} = q|'| . $row->{attalign} . q|'|;
$row->{attidentity} = q|'| . $row->{attidentity} . q|'|;
# We don't emit initializers for the variable length fields at all.
# Only the fixed-size portions of the descriptors are ever used.

View File

@@ -144,37 +144,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, true, 0
false, 'p', 's', true, false, '\0', false, true, 0
};
static FormData_pg_attribute a2 = {
0, {"oid"}, OIDOID, 0, sizeof(Oid),
ObjectIdAttributeNumber, 0, -1, -1,
true, 'p', 'i', true, false, false, true, 0
true, 'p', 'i', true, false, '\0', false, true, 0
};
static FormData_pg_attribute a3 = {
0, {"xmin"}, XIDOID, 0, sizeof(TransactionId),
MinTransactionIdAttributeNumber, 0, -1, -1,
true, 'p', 'i', true, false, false, true, 0
true, 'p', 'i', true, false, '\0', false, true, 0
};
static FormData_pg_attribute a4 = {
0, {"cmin"}, CIDOID, 0, sizeof(CommandId),
MinCommandIdAttributeNumber, 0, -1, -1,
true, 'p', 'i', true, false, false, true, 0
true, 'p', 'i', true, false, '\0', false, true, 0
};
static FormData_pg_attribute a5 = {
0, {"xmax"}, XIDOID, 0, sizeof(TransactionId),
MaxTransactionIdAttributeNumber, 0, -1, -1,
true, 'p', 'i', true, false, false, true, 0
true, 'p', 'i', true, false, '\0', false, true, 0
};
static FormData_pg_attribute a6 = {
0, {"cmax"}, CIDOID, 0, sizeof(CommandId),
MaxCommandIdAttributeNumber, 0, -1, -1,
true, 'p', 'i', true, false, false, true, 0
true, 'p', 'i', true, false, '\0', false, true, 0
};
/*
@@ -186,7 +186,7 @@ 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, true, 0
true, 'p', 'i', true, false, '\0', false, true, 0
};
static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7};
@@ -621,6 +621,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attalign - 1] = CharGetDatum(new_attribute->attalign);
values[Anum_pg_attribute_attnotnull - 1] = BoolGetDatum(new_attribute->attnotnull);
values[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(new_attribute->atthasdef);
values[Anum_pg_attribute_attidentity - 1] = CharGetDatum(new_attribute->attidentity);
values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(new_attribute->attisdropped);
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);

View File

@@ -353,6 +353,7 @@ ConstructTupleDescriptor(Relation heapRelation,
to->attcacheoff = -1;
to->attnotnull = false;
to->atthasdef = false;
to->attidentity = '\0';
to->attislocal = true;
to->attinhcount = 0;
to->attcollation = collationObjectId[i];

View File

@@ -728,13 +728,13 @@ CREATE VIEW columns AS
CAST(a.attnum AS sql_identifier) AS dtd_identifier,
CAST('NO' AS yes_or_no) AS is_self_referencing,
CAST('NO' AS yes_or_no) AS is_identity,
CAST(null AS character_data) AS identity_generation,
CAST(null AS character_data) AS identity_start,
CAST(null AS character_data) AS identity_increment,
CAST(null AS character_data) AS identity_maximum,
CAST(null AS character_data) AS identity_minimum,
CAST(null AS yes_or_no) AS identity_cycle,
CAST(CASE WHEN a.attidentity IN ('a', 'd') THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_identity,
CAST(CASE a.attidentity WHEN 'a' THEN 'ALWAYS' WHEN 'd' THEN 'BY DEFAULT' END AS character_data) AS identity_generation,
CAST(seq.seqstart AS character_data) AS identity_start,
CAST(seq.seqincrement AS character_data) AS identity_increment,
CAST(seq.seqmax AS character_data) AS identity_maximum,
CAST(seq.seqmin AS character_data) AS identity_minimum,
CAST(CASE WHEN seq.seqcycle THEN 'YES' ELSE 'NO' END AS yes_or_no) AS identity_cycle,
CAST('NEVER' AS character_data) AS is_generated,
CAST(null AS character_data) AS generation_expression,
@@ -751,6 +751,8 @@ CREATE VIEW columns AS
ON (t.typtype = 'd' AND t.typbasetype = bt.oid)
LEFT JOIN (pg_collation co JOIN pg_namespace nco ON (co.collnamespace = nco.oid))
ON a.attcollation = co.oid AND (nco.nspname, co.collname) <> ('pg_catalog', 'default')
LEFT JOIN (pg_depend dep JOIN pg_sequence seq ON (dep.classid = 'pg_class'::regclass AND dep.objid = seq.seqrelid AND dep.deptype = 'i'))
ON (dep.refclassid = 'pg_class'::regclass AND dep.refobjid = c.oid AND dep.refobjsubid = a.attnum)
WHERE (NOT pg_is_other_temp_schema(nc.oid))
@@ -1545,6 +1547,7 @@ CREATE VIEW sequences AS
FROM pg_namespace nc, pg_class c, pg_sequence s
WHERE c.relnamespace = nc.oid
AND c.relkind = 'S'
AND NOT EXISTS (SELECT 1 FROM pg_depend WHERE classid = 'pg_class'::regclass AND objid = c.oid AND deptype = 'i')
AND (NOT pg_is_other_temp_schema(nc.oid))
AND c.oid = s.seqrelid
AND (pg_has_role(c.relowner, 'USAGE')

View File

@@ -488,7 +488,7 @@ getExtensionOfObject(Oid classId, Oid objectId)
/*
* Detect whether a sequence is marked as "owned" by a column
*
* An ownership marker is an AUTO dependency from the sequence to the
* An ownership marker is an AUTO or INTERNAL dependency from the sequence to the
* column. If we find one, store the identity of the owning column
* into *tableId and *colId and return TRUE; else return FALSE.
*
@@ -497,7 +497,7 @@ getExtensionOfObject(Oid classId, Oid objectId)
* not happen, though.
*/
bool
sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId)
sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
{
bool ret = false;
Relation depRel;
@@ -524,7 +524,7 @@ sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId)
Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup);
if (depform->refclassid == RelationRelationId &&
depform->deptype == DEPENDENCY_AUTO)
depform->deptype == deptype)
{
*tableId = depform->refobjid;
*colId = depform->refobjsubid;
@@ -541,27 +541,15 @@ sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId)
}
/*
* Remove any existing "owned" markers for the specified sequence.
*
* Note: we don't provide a special function to install an "owned"
* marker; just use recordDependencyOn().
*/
void
markSequenceUnowned(Oid seqId)
{
deleteDependencyRecordsForClass(RelationRelationId, seqId,
RelationRelationId, DEPENDENCY_AUTO);
}
/*
* Collect a list of OIDs of all sequences owned by the specified relation.
* Collect a list of OIDs of all sequences owned by the specified relation,
* and column if specified.
*/
List *
getOwnedSequences(Oid relid)
getOwnedSequences(Oid relid, AttrNumber attnum)
{
List *result = NIL;
Relation depRel;
ScanKeyData key[2];
ScanKeyData key[3];
SysScanDesc scan;
HeapTuple tup;
@@ -575,23 +563,28 @@ getOwnedSequences(Oid relid)
Anum_pg_depend_refobjid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relid));
if (attnum)
ScanKeyInit(&key[2],
Anum_pg_depend_refobjsubid,
BTEqualStrategyNumber, F_INT4EQ,
Int32GetDatum(attnum));
scan = systable_beginscan(depRel, DependReferenceIndexId, true,
NULL, 2, key);
NULL, attnum ? 3 : 2, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
/*
* We assume any auto dependency of a sequence on a column must be
* We assume any auto or internal dependency of a sequence on a column must be
* what we are looking for. (We need the relkind test because indexes
* can also have auto dependencies on columns.)
*/
if (deprec->classid == RelationRelationId &&
deprec->objsubid == 0 &&
deprec->refobjsubid != 0 &&
deprec->deptype == DEPENDENCY_AUTO &&
(deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) &&
get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
{
result = lappend_oid(result, deprec->objid);
@@ -605,6 +598,21 @@ getOwnedSequences(Oid relid)
return result;
}
/*
* Get owned sequence, error if not exactly one.
*/
Oid
getOwnedSequence(Oid relid, AttrNumber attnum)
{
List *seqlist = getOwnedSequences(relid, attnum);
if (list_length(seqlist) > 1)
elog(ERROR, "more than one owned sequence found");
else if (list_length(seqlist) < 1)
elog(ERROR, "no owned sequence found");
else
return linitial_oid(seqlist);
}
/*
* get_constraint_index

View File

@@ -200,7 +200,7 @@ F181 Multiple module support NO
F191 Referential delete actions YES
F200 TRUNCATE TABLE statement YES
F201 CAST function YES
F202 TRUNCATE TABLE: identity column restart option NO
F202 TRUNCATE TABLE: identity column restart option YES
F221 Explicit defaults YES
F222 INSERT statement: DEFAULT VALUES clause YES
F231 Privilege tables YES
@@ -241,9 +241,9 @@ F381 Extended schema manipulation 02 ALTER TABLE statement: ADD CONSTRAINT claus
F381 Extended schema manipulation 03 ALTER TABLE statement: DROP CONSTRAINT clause YES
F382 Alter column data type YES
F383 Set column not null clause YES
F384 Drop identity property clause NO
F384 Drop identity property clause YES
F385 Drop column generation expression clause NO
F386 Set identity column generation clause NO
F386 Set identity column generation clause YES
F391 Long identifiers YES
F392 Unicode escapes in identifiers YES
F393 Unicode escapes in literals YES
@@ -420,11 +420,11 @@ T152 DISTINCT predicate with negation YES
T171 LIKE clause in table definition YES
T172 AS subquery clause in table definition YES
T173 Extended LIKE clause in table definition YES
T174 Identity columns NO
T174 Identity columns YES
T175 Generated columns NO
T176 Sequence generator support NO
T177 Sequence generator support: simple restart option NO
T178 Identity columns: simple restart option NO
T177 Sequence generator support: simple restart option YES
T178 Identity columns: simple restart option YES
T180 System-versioned tables NO
T181 Application-time period tables NO
T191 Referential action RESTRICT YES

View File

@@ -93,17 +93,17 @@ static HTAB *seqhashtab = NULL; /* hash table for SeqTable items */
static SeqTableData *last_used_seq = NULL;
static void fill_seq_with_data(Relation rel, HeapTuple tuple);
static int64 nextval_internal(Oid relid);
static Relation open_share_lock(SeqTable seq);
static void create_seq_hashtable(void);
static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
static Form_pg_sequence_data read_seq_tuple(Relation rel,
Buffer *buf, HeapTuple seqdatatuple);
static void init_params(ParseState *pstate, List *options, bool isInit,
static void init_params(ParseState *pstate, List *options, bool for_identity,
bool isInit,
Form_pg_sequence seqform,
Form_pg_sequence_data seqdataform, List **owned_by);
static void do_setval(Oid relid, int64 next, bool iscalled);
static void process_owned_by(Relation seqrel, List *owned_by);
static void process_owned_by(Relation seqrel, List *owned_by, bool for_identity);
/*
@@ -153,7 +153,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
}
/* Check and set all option values */
init_params(pstate, seq->options, true, &seqform, &seqdataform, &owned_by);
init_params(pstate, seq->options, seq->for_identity, true, &seqform, &seqdataform, &owned_by);
/*
* Create relation (and fill value[] and null[] for the tuple)
@@ -219,7 +219,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
/* process OWNED BY if given */
if (owned_by)
process_owned_by(rel, owned_by);
process_owned_by(rel, owned_by, seq->for_identity);
heap_close(rel, NoLock);
@@ -455,7 +455,7 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
seqform = (Form_pg_sequence) GETSTRUCT(tuple);
/* Check and set new values */
init_params(pstate, stmt->options, false, seqform, &newseqdata, &owned_by);
init_params(pstate, stmt->options, stmt->for_identity, false, seqform, &newseqdata, &owned_by);
/* Clear local cache so that we don't think we have cached numbers */
/* Note that we do not change the currval() state */
@@ -498,7 +498,7 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
/* process OWNED BY if given */
if (owned_by)
process_owned_by(seqrel, owned_by);
process_owned_by(seqrel, owned_by, stmt->for_identity);
InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
@@ -554,7 +554,7 @@ nextval(PG_FUNCTION_ARGS)
*/
relid = RangeVarGetRelid(sequence, NoLock, false);
PG_RETURN_INT64(nextval_internal(relid));
PG_RETURN_INT64(nextval_internal(relid, true));
}
Datum
@@ -562,11 +562,11 @@ nextval_oid(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
PG_RETURN_INT64(nextval_internal(relid));
PG_RETURN_INT64(nextval_internal(relid, true));
}
static int64
nextval_internal(Oid relid)
int64
nextval_internal(Oid relid, bool check_permissions)
{
SeqTable elm;
Relation seqrel;
@@ -592,7 +592,8 @@ nextval_internal(Oid relid)
/* open and AccessShareLock sequence */
init_sequence(relid, &elm, &seqrel);
if (pg_class_aclcheck(elm->relid, GetUserId(),
if (check_permissions &&
pg_class_aclcheck(elm->relid, GetUserId(),
ACL_USAGE | ACL_UPDATE) != ACLCHECK_OK)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -1219,7 +1220,8 @@ read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple)
* otherwise, do not change existing options that aren't explicitly overridden.
*/
static void
init_params(ParseState *pstate, List *options, bool isInit,
init_params(ParseState *pstate, List *options, bool for_identity,
bool isInit,
Form_pg_sequence seqform,
Form_pg_sequence_data seqdataform, List **owned_by)
{
@@ -1322,6 +1324,18 @@ init_params(ParseState *pstate, List *options, bool isInit,
parser_errposition(pstate, defel->location)));
*owned_by = defGetQualifiedName(defel);
}
else if (strcmp(defel->defname, "sequence_name") == 0)
{
/*
* The parser allows this, but it is only for identity columns, in
* which case it is filtered out in parse_utilcmd.c. We only get
* here if someone puts it into a CREATE SEQUENCE.
*/
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("invalid sequence option SEQUENCE NAME"),
parser_errposition(pstate, defel->location)));
}
else
elog(ERROR, "option \"%s\" not recognized",
defel->defname);
@@ -1344,7 +1358,9 @@ init_params(ParseState *pstate, List *options, bool isInit,
newtypid != INT8OID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("sequence type must be smallint, integer, or bigint")));
for_identity
? errmsg("identity column type must be smallint, integer, or bigint")
: errmsg("sequence type must be smallint, integer, or bigint")));
if (!isInit)
{
@@ -1588,12 +1604,15 @@ init_params(ParseState *pstate, List *options, bool isInit,
* as the sequence.
*/
static void
process_owned_by(Relation seqrel, List *owned_by)
process_owned_by(Relation seqrel, List *owned_by, bool for_identity)
{
DependencyType deptype;
int nnames;
Relation tablerel;
AttrNumber attnum;
deptype = for_identity ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO;
nnames = list_length(owned_by);
Assert(nnames > 0);
if (nnames == 1)
@@ -1624,6 +1643,7 @@ process_owned_by(Relation seqrel, List *owned_by)
/* Must be a regular or foreign table */
if (!(tablerel->rd_rel->relkind == RELKIND_RELATION ||
tablerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
tablerel->rd_rel->relkind == RELKIND_VIEW ||
tablerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -1650,10 +1670,28 @@ process_owned_by(Relation seqrel, List *owned_by)
}
/*
* OK, we are ready to update pg_depend. First remove any existing AUTO
* Catch user explicitly running OWNED BY on identity sequence.
*/
if (deptype == DEPENDENCY_AUTO)
{
Oid tableId;
int32 colId;
if (sequenceIsOwned(RelationGetRelid(seqrel), DEPENDENCY_INTERNAL, &tableId, &colId))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot change ownership of identity sequence"),
errdetail("Sequence \"%s\" is linked to table \"%s\".",
RelationGetRelationName(seqrel),
get_rel_name(tableId))));
}
/*
* OK, we are ready to update pg_depend. First remove any existing
* dependencies for the sequence, then optionally add a new one.
*/
markSequenceUnowned(RelationGetRelid(seqrel));
deleteDependencyRecordsForClass(RelationRelationId, RelationGetRelid(seqrel),
RelationRelationId, deptype);
if (tablerel)
{
@@ -1666,7 +1704,7 @@ process_owned_by(Relation seqrel, List *owned_by)
depobject.classId = RelationRelationId;
depobject.objectId = RelationGetRelid(seqrel);
depobject.objectSubId = 0;
recordDependencyOn(&depobject, &refobject, DEPENDENCY_AUTO);
recordDependencyOn(&depobject, &refobject, deptype);
}
/* Done, but hold lock until commit */
@@ -1675,6 +1713,33 @@ process_owned_by(Relation seqrel, List *owned_by)
}
/*
* Return sequence parameters in a list of the form created by the parser.
*/
List *
sequence_options(Oid relid)
{
HeapTuple pgstuple;
Form_pg_sequence pgsform;
List *options = NIL;
pgstuple = SearchSysCache1(SEQRELID, relid);
if (!HeapTupleIsValid(pgstuple))
elog(ERROR, "cache lookup failed for sequence %u", relid);
pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
options = lappend(options, makeDefElem("cache", (Node *) makeInteger(pgsform->seqcache), -1));
options = lappend(options, makeDefElem("cycle", (Node *) makeInteger(pgsform->seqcycle), -1));
options = lappend(options, makeDefElem("increment", (Node *) makeInteger(pgsform->seqincrement), -1));
options = lappend(options, makeDefElem("maxvalue", (Node *) makeInteger(pgsform->seqmax), -1));
options = lappend(options, makeDefElem("minvalue", (Node *) makeInteger(pgsform->seqmin), -1));
options = lappend(options, makeDefElem("start", (Node *) makeInteger(pgsform->seqstart), -1));
ReleaseSysCache(pgstuple);
return options;
}
/*
* Return sequence parameters (formerly for use by information schema)
*/

View File

@@ -361,6 +361,11 @@ static ObjectAddress ATExecSetNotNull(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,
Node *def, LOCKMODE lockmode);
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
Node *def, LOCKMODE lockmode);
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
static void ATPrepSetStatistics(Relation rel, const char *colName,
Node *newValue, LOCKMODE lockmode);
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName,
@@ -696,6 +701,9 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
cookedDefaults = lappend(cookedDefaults, cooked);
descriptor->attrs[attnum - 1]->atthasdef = true;
}
if (colDef->identity)
descriptor->attrs[attnum - 1]->attidentity = colDef->identity;
}
/*
@@ -1281,7 +1289,7 @@ ExecuteTruncate(TruncateStmt *stmt)
foreach(cell, rels)
{
Relation rel = (Relation) lfirst(cell);
List *seqlist = getOwnedSequences(RelationGetRelid(rel));
List *seqlist = getOwnedSequences(RelationGetRelid(rel), 0);
ListCell *seqcell;
foreach(seqcell, seqlist)
@@ -2078,6 +2086,12 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
get_collation_name(defcollid),
get_collation_name(newcollid))));
/*
* Identity is never inherited. The new column can have an
* identity definition, so we always just take that one.
*/
def->identity = newdef->identity;
/* Copy storage parameter */
if (def->storage == 0)
def->storage = newdef->storage;
@@ -3217,6 +3231,9 @@ AlterTableGetLockLevel(List *cmds)
case AT_DisableRowSecurity:
case AT_ForceRowSecurity:
case AT_NoForceRowSecurity:
case AT_AddIdentity:
case AT_DropIdentity:
case AT_SetIdentity:
cmd_lockmode = AccessExclusiveLock;
break;
@@ -3447,6 +3464,18 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* No command-specific prep needed */
pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
break;
case AT_AddIdentity:
ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
pass = AT_PASS_ADD_CONSTR;
break;
case AT_DropIdentity:
ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
pass = AT_PASS_DROP;
break;
case AT_SetIdentity:
ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
pass = AT_PASS_COL_ATTRS;
break;
case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE);
ATPrepDropNotNull(rel, recurse, recursing);
@@ -3772,6 +3801,15 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
break;
case AT_AddIdentity:
address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode);
break;
case AT_SetIdentity:
address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode);
break;
case AT_DropIdentity:
address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode);
break;
case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
address = ATExecDropNotNull(rel, cmd->name, lockmode);
break;
@@ -5120,6 +5158,17 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
elog(ERROR, "cache lookup failed for relation %u", myrelid);
relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
/*
* Cannot add identity column if table has children, because identity does
* not inherit. (Adding column and identity separately will work.)
*/
if (colDef->identity &&
recurse &&
find_inheritance_children(myrelid, NoLock) != NIL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("cannot recursively add identity column to table that has child tables")));
/* skip if the name already exists and if_not_exists is true */
if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
{
@@ -5172,6 +5221,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
attribute.attalign = tform->typalign;
attribute.attnotnull = colDef->is_not_null;
attribute.atthasdef = false;
attribute.attidentity = colDef->identity;
attribute.attisdropped = false;
attribute.attislocal = colDef->is_local;
attribute.attinhcount = colDef->inhcount;
@@ -5539,6 +5589,12 @@ ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
errmsg("cannot alter system column \"%s\"",
colName)));
if (get_attidentity(RelationGetRelid(rel), attnum))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("column \"%s\" of relation \"%s\" is an identity column",
colName, RelationGetRelationName(rel))));
/*
* Check that the attribute is not in a primary key
*
@@ -5755,6 +5811,13 @@ ATExecColumnDefault(Relation rel, const char *colName,
errmsg("cannot alter system column \"%s\"",
colName)));
if (get_attidentity(RelationGetRelid(rel), attnum))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("column \"%s\" of relation \"%s\" is an identity column",
colName, RelationGetRelationName(rel)),
newDefault ? 0 : errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY instead.")));
/*
* Remove any old default for the column. We use RESTRICT here for
* safety, but at present we do not expect anything to depend on the
@@ -5789,6 +5852,224 @@ ATExecColumnDefault(Relation rel, const char *colName,
return address;
}
/*
* ALTER TABLE ALTER COLUMN ADD IDENTITY
*
* Return the address of the affected column.
*/
static ObjectAddress
ATExecAddIdentity(Relation rel, const char *colName,
Node *def, LOCKMODE lockmode)
{
Relation attrelation;
HeapTuple tuple;
Form_pg_attribute attTup;
AttrNumber attnum;
ObjectAddress address;
ColumnDef *cdef = castNode(ColumnDef, def);
attrelation = 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))));
attTup = (Form_pg_attribute) GETSTRUCT(tuple);
attnum = attTup->attnum;
/* Can't alter a system attribute */
if (attnum <= 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot alter system column \"%s\"",
colName)));
/*
* Creating a column as identity implies NOT NULL, so adding the identity
* to an existing column that is not NOT NULL would create a state that
* cannot be reproduced without contortions.
*/
if (!attTup->attnotnull)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
colName, RelationGetRelationName(rel))));
if (attTup->attidentity)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("column \"%s\" of relation \"%s\" is already an identity column",
colName, RelationGetRelationName(rel))));
if (attTup->atthasdef)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("column \"%s\" of relation \"%s\" already has a default value",
colName, RelationGetRelationName(rel))));
attTup->attidentity = cdef->identity;
CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
InvokeObjectPostAlterHook(RelationRelationId,
RelationGetRelid(rel),
attTup->attnum);
ObjectAddressSubSet(address, RelationRelationId,
RelationGetRelid(rel), attnum);
heap_freetuple(tuple);
heap_close(attrelation, RowExclusiveLock);
return address;
}
static ObjectAddress
ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode)
{
ListCell *option;
DefElem *generatedEl = NULL;
HeapTuple tuple;
Form_pg_attribute attTup;
AttrNumber attnum;
Relation attrelation;
ObjectAddress address;
foreach(option, castNode(List, def))
{
DefElem *defel = castNode(DefElem, lfirst(option));
if (strcmp(defel->defname, "generated") == 0)
{
if (generatedEl)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
generatedEl = defel;
}
else
elog(ERROR, "option \"%s\" not recognized",
defel->defname);
}
/*
* Even if there is nothing to change here, we run all the checks. There
* will be a subsequent ALTER SEQUENCE that relies on everything being
* there.
*/
attrelation = 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))));
attTup = (Form_pg_attribute) GETSTRUCT(tuple);
attnum = attTup->attnum;
if (attnum <= 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot alter system column \"%s\"",
colName)));
if (!attTup->attidentity)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("column \"%s\" of relation \"%s\" is not an identity column",
colName, RelationGetRelationName(rel))));
if (generatedEl)
{
attTup->attidentity = defGetInt32(generatedEl);
CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
InvokeObjectPostAlterHook(RelationRelationId,
RelationGetRelid(rel),
attTup->attnum);
ObjectAddressSubSet(address, RelationRelationId,
RelationGetRelid(rel), attnum);
}
heap_freetuple(tuple);
heap_close(attrelation, RowExclusiveLock);
return address;
}
static ObjectAddress
ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
{
HeapTuple tuple;
Form_pg_attribute attTup;
AttrNumber attnum;
Relation attrelation;
ObjectAddress address;
Oid seqid;
ObjectAddress seqaddress;
attrelation = 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))));
attTup = (Form_pg_attribute) GETSTRUCT(tuple);
attnum = attTup->attnum;
if (attnum <= 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot alter system column \"%s\"",
colName)));
if (!attTup->attidentity)
{
if (!missing_ok)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("column \"%s\" of relation \"%s\" is not an identity column",
colName, RelationGetRelationName(rel))));
else
{
ereport(NOTICE,
(errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
colName, RelationGetRelationName(rel))));
heap_freetuple(tuple);
heap_close(attrelation, RowExclusiveLock);
return InvalidObjectAddress;
}
}
attTup->attidentity = '\0';
CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
InvokeObjectPostAlterHook(RelationRelationId,
RelationGetRelid(rel),
attTup->attnum);
ObjectAddressSubSet(address, RelationRelationId,
RelationGetRelid(rel), attnum);
heap_freetuple(tuple);
heap_close(attrelation, RowExclusiveLock);
/* drop the internal sequence */
seqid = getOwnedSequence(RelationGetRelid(rel), attnum);
deleteDependencyRecordsForClass(RelationRelationId, seqid,
RelationRelationId, DEPENDENCY_INTERNAL);
CommandCounterIncrement();
seqaddress.classId = RelationRelationId;
seqaddress.objectId = seqid;
seqaddress.objectSubId = 0;
performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
return address;
}
/*
* ALTER TABLE ALTER COLUMN SET STATISTICS
*/
@@ -9539,7 +9820,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
Oid tableId;
int32 colId;
if (sequenceIsOwned(relationOid, &tableId, &colId))
if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot change owner of sequence \"%s\"",
@@ -9810,7 +10092,7 @@ change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lock
if (depForm->refobjsubid == 0 ||
depForm->classid != RelationRelationId ||
depForm->objsubid != 0 ||
depForm->deptype != DEPENDENCY_AUTO)
!(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
continue;
/* Use relation_open just in case it's an index */
@@ -12115,7 +12397,8 @@ AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
Oid tableId;
int32 colId;
if (sequenceIsOwned(relid, &tableId, &colId))
if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot move an owned sequence into another schema"),
@@ -12298,7 +12581,7 @@ AlterIndexNamespaces(Relation classRel, Relation rel,
}
/*
* Move all SERIAL-column sequences of the specified relation to another
* Move all identity and SERIAL-column sequences of the specified relation to another
* namespace.
*
* Note: we assume adequate permission checking was done by the caller,
@@ -12342,7 +12625,7 @@ AlterSeqNamespaces(Relation classRel, Relation rel,
if (depForm->refobjsubid == 0 ||
depForm->classid != RelationRelationId ||
depForm->objsubid != 0 ||
depForm->deptype != DEPENDENCY_AUTO)
!(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
continue;
/* Use relation_open just in case it's an index */

View File

@@ -1993,6 +1993,18 @@ ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
break;
}
case T_NextValueExpr:
{
NextValueExpr *nve = (NextValueExpr *) node;
scratch.opcode = EEOP_NEXTVALUEEXPR;
scratch.d.nextvalueexpr.seqid = nve->seqid;
scratch.d.nextvalueexpr.seqtypid = nve->typeId;
ExprEvalPushStep(state, &scratch);
break;
}
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));

View File

@@ -60,6 +60,7 @@
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
#include "executor/execExpr.h"
#include "executor/nodeSubplan.h"
#include "funcapi.h"
@@ -337,6 +338,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_NULLIF,
&&CASE_EEOP_SQLVALUEFUNCTION,
&&CASE_EEOP_CURRENTOFEXPR,
&&CASE_EEOP_NEXTVALUEEXPR,
&&CASE_EEOP_ARRAYEXPR,
&&CASE_EEOP_ARRAYCOERCE,
&&CASE_EEOP_ROW,
@@ -1228,6 +1230,27 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT();
}
EEO_CASE(EEOP_NEXTVALUEEXPR)
{
switch (op->d.nextvalueexpr.seqtypid)
{
case INT2OID:
*op->resvalue = Int16GetDatum((int16) nextval_internal(op->d.nextvalueexpr.seqid, false));
break;
case INT4OID:
*op->resvalue = Int32GetDatum((int32) nextval_internal(op->d.nextvalueexpr.seqid, false));
break;
case INT8OID:
*op->resvalue = Int64GetDatum((int64) nextval_internal(op->d.nextvalueexpr.seqid, false));
break;
default:
elog(ERROR, "unsupported sequence type %u", op->d.nextvalueexpr.seqtypid);
}
*op->resnull = false;
EEO_NEXT();
}
EEO_CASE(EEOP_ARRAYEXPR)
{
/* too complex for an inline implementation */

View File

@@ -2003,6 +2003,20 @@ _copyCurrentOfExpr(const CurrentOfExpr *from)
return newnode;
}
/*
* _copyNextValueExpr
*/
static NextValueExpr *
_copyNextValueExpr(const NextValueExpr *from)
{
NextValueExpr *newnode = makeNode(NextValueExpr);
COPY_SCALAR_FIELD(seqid);
COPY_SCALAR_FIELD(typeId);
return newnode;
}
/*
* _copyInferenceElem
*/
@@ -2790,6 +2804,7 @@ _copyColumnDef(const ColumnDef *from)
COPY_SCALAR_FIELD(storage);
COPY_NODE_FIELD(raw_default);
COPY_NODE_FIELD(cooked_default);
COPY_SCALAR_FIELD(identity);
COPY_NODE_FIELD(collClause);
COPY_SCALAR_FIELD(collOid);
COPY_NODE_FIELD(constraints);
@@ -2812,6 +2827,7 @@ _copyConstraint(const Constraint *from)
COPY_SCALAR_FIELD(is_no_inherit);
COPY_NODE_FIELD(raw_expr);
COPY_STRING_FIELD(cooked_expr);
COPY_SCALAR_FIELD(generated_when);
COPY_NODE_FIELD(keys);
COPY_NODE_FIELD(exclusions);
COPY_NODE_FIELD(options);
@@ -2920,6 +2936,7 @@ _copyQuery(const Query *from)
COPY_NODE_FIELD(rtable);
COPY_NODE_FIELD(jointree);
COPY_NODE_FIELD(targetList);
COPY_SCALAR_FIELD(override);
COPY_NODE_FIELD(onConflict);
COPY_NODE_FIELD(returningList);
COPY_NODE_FIELD(groupClause);
@@ -2963,6 +2980,7 @@ _copyInsertStmt(const InsertStmt *from)
COPY_NODE_FIELD(onConflictClause);
COPY_NODE_FIELD(returningList);
COPY_NODE_FIELD(withClause);
COPY_SCALAR_FIELD(override);
return newnode;
}
@@ -3811,6 +3829,7 @@ _copyCreateSeqStmt(const CreateSeqStmt *from)
COPY_NODE_FIELD(sequence);
COPY_NODE_FIELD(options);
COPY_SCALAR_FIELD(ownerId);
COPY_SCALAR_FIELD(for_identity);
COPY_SCALAR_FIELD(if_not_exists);
return newnode;
@@ -3823,6 +3842,7 @@ _copyAlterSeqStmt(const AlterSeqStmt *from)
COPY_NODE_FIELD(sequence);
COPY_NODE_FIELD(options);
COPY_SCALAR_FIELD(for_identity);
COPY_SCALAR_FIELD(missing_ok);
return newnode;
@@ -4927,6 +4947,9 @@ copyObjectImpl(const void *from)
case T_CurrentOfExpr:
retval = _copyCurrentOfExpr(from);
break;
case T_NextValueExpr:
retval = _copyNextValueExpr(from);
break;
case T_InferenceElem:
retval = _copyInferenceElem(from);
break;

View File

@@ -733,6 +733,15 @@ _equalCurrentOfExpr(const CurrentOfExpr *a, const CurrentOfExpr *b)
return true;
}
static bool
_equalNextValueExpr(const NextValueExpr *a, const NextValueExpr *b)
{
COMPARE_SCALAR_FIELD(seqid);
COMPARE_SCALAR_FIELD(typeId);
return true;
}
static bool
_equalInferenceElem(const InferenceElem *a, const InferenceElem *b)
{
@@ -963,6 +972,7 @@ _equalQuery(const Query *a, const Query *b)
COMPARE_NODE_FIELD(rtable);
COMPARE_NODE_FIELD(jointree);
COMPARE_NODE_FIELD(targetList);
COMPARE_SCALAR_FIELD(override);
COMPARE_NODE_FIELD(onConflict);
COMPARE_NODE_FIELD(returningList);
COMPARE_NODE_FIELD(groupClause);
@@ -1002,6 +1012,7 @@ _equalInsertStmt(const InsertStmt *a, const InsertStmt *b)
COMPARE_NODE_FIELD(onConflictClause);
COMPARE_NODE_FIELD(returningList);
COMPARE_NODE_FIELD(withClause);
COMPARE_SCALAR_FIELD(override);
return true;
}
@@ -1713,6 +1724,7 @@ _equalCreateSeqStmt(const CreateSeqStmt *a, const CreateSeqStmt *b)
COMPARE_NODE_FIELD(sequence);
COMPARE_NODE_FIELD(options);
COMPARE_SCALAR_FIELD(ownerId);
COMPARE_SCALAR_FIELD(for_identity);
COMPARE_SCALAR_FIELD(if_not_exists);
return true;
@@ -1723,6 +1735,7 @@ _equalAlterSeqStmt(const AlterSeqStmt *a, const AlterSeqStmt *b)
{
COMPARE_NODE_FIELD(sequence);
COMPARE_NODE_FIELD(options);
COMPARE_SCALAR_FIELD(for_identity);
COMPARE_SCALAR_FIELD(missing_ok);
return true;
@@ -2530,6 +2543,7 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
COMPARE_SCALAR_FIELD(storage);
COMPARE_NODE_FIELD(raw_default);
COMPARE_NODE_FIELD(cooked_default);
COMPARE_SCALAR_FIELD(identity);
COMPARE_NODE_FIELD(collClause);
COMPARE_SCALAR_FIELD(collOid);
COMPARE_NODE_FIELD(constraints);
@@ -2550,6 +2564,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_SCALAR_FIELD(is_no_inherit);
COMPARE_NODE_FIELD(raw_expr);
COMPARE_STRING_FIELD(cooked_expr);
COMPARE_SCALAR_FIELD(generated_when);
COMPARE_NODE_FIELD(keys);
COMPARE_NODE_FIELD(exclusions);
COMPARE_NODE_FIELD(options);
@@ -3099,6 +3114,9 @@ equal(const void *a, const void *b)
case T_CurrentOfExpr:
retval = _equalCurrentOfExpr(a, b);
break;
case T_NextValueExpr:
retval = _equalNextValueExpr(a, b);
break;
case T_InferenceElem:
retval = _equalInferenceElem(a, b);
break;

View File

@@ -246,6 +246,9 @@ exprType(const Node *expr)
case T_CurrentOfExpr:
type = BOOLOID;
break;
case T_NextValueExpr:
type = ((const NextValueExpr *) expr)->typeId;
break;
case T_InferenceElem:
{
const InferenceElem *n = (const InferenceElem *) expr;
@@ -919,6 +922,9 @@ exprCollation(const Node *expr)
case T_CurrentOfExpr:
coll = InvalidOid; /* result is always boolean */
break;
case T_NextValueExpr:
coll = InvalidOid; /* result is always an integer type */
break;
case T_InferenceElem:
coll = exprCollation((Node *) ((const InferenceElem *) expr)->expr);
break;
@@ -1122,6 +1128,9 @@ exprSetCollation(Node *expr, Oid collation)
case T_CurrentOfExpr:
Assert(!OidIsValid(collation)); /* result is always boolean */
break;
case T_NextValueExpr:
Assert(!OidIsValid(collation)); /* result is always an integer type */
break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
break;
@@ -1881,6 +1890,7 @@ expression_tree_walker(Node *node,
case T_CaseTestExpr:
case T_SetToDefault:
case T_CurrentOfExpr:
case T_NextValueExpr:
case T_SQLValueFunction:
case T_RangeTblRef:
case T_SortGroupClause:
@@ -2476,6 +2486,7 @@ expression_tree_mutator(Node *node,
case T_CaseTestExpr:
case T_SetToDefault:
case T_CurrentOfExpr:
case T_NextValueExpr:
case T_SQLValueFunction:
case T_RangeTblRef:
case T_SortGroupClause:

View File

@@ -2763,6 +2763,7 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_CHAR_FIELD(storage);
WRITE_NODE_FIELD(raw_default);
WRITE_NODE_FIELD(cooked_default);
WRITE_CHAR_FIELD(identity);
WRITE_NODE_FIELD(collClause);
WRITE_OID_FIELD(collOid);
WRITE_NODE_FIELD(constraints);
@@ -2868,6 +2869,7 @@ _outQuery(StringInfo str, const Query *node)
WRITE_NODE_FIELD(rtable);
WRITE_NODE_FIELD(jointree);
WRITE_NODE_FIELD(targetList);
WRITE_ENUM_FIELD(override, OverridingKind);
WRITE_NODE_FIELD(onConflict);
WRITE_NODE_FIELD(returningList);
WRITE_NODE_FIELD(groupClause);
@@ -3405,6 +3407,13 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_STRING_FIELD(cooked_expr);
break;
case CONSTR_IDENTITY:
appendStringInfoString(str, "IDENTITY");
WRITE_NODE_FIELD(raw_expr);
WRITE_STRING_FIELD(cooked_expr);
WRITE_CHAR_FIELD(generated_when);
break;
case CONSTR_CHECK:
appendStringInfoString(str, "CHECK");
WRITE_BOOL_FIELD(is_no_inherit);

View File

@@ -247,6 +247,7 @@ _readQuery(void)
READ_NODE_FIELD(rtable);
READ_NODE_FIELD(jointree);
READ_NODE_FIELD(targetList);
READ_ENUM_FIELD(override, OverridingKind);
READ_NODE_FIELD(onConflict);
READ_NODE_FIELD(returningList);
READ_NODE_FIELD(groupClause);

View File

@@ -487,6 +487,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
}
qry->override = stmt->override;
isOnConflictUpdate = (stmt->onConflictClause &&
stmt->onConflictClause->action == ONCONFLICT_UPDATE);

View File

@@ -292,6 +292,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> alter_table_cmd alter_type_cmd opt_collate_clause
replica_identity partition_cmd
%type <list> alter_table_cmds alter_type_cmds
%type <list> alter_identity_column_option_list
%type <defelt> alter_identity_column_option
%type <dbehavior> opt_drop_behavior
@@ -449,7 +451,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
select_offset_value2 opt_select_fetch_first_value
%type <ival> row_or_rows first_or_next
%type <list> OptSeqOptList SeqOptList
%type <list> OptSeqOptList SeqOptList OptParenthesizedSeqOptList
%type <defelt> SeqOptElem
%type <istmt> insert_rest
@@ -569,6 +571,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
opt_frame_clause frame_extent frame_bound
%type <str> opt_existing_window_name
%type <boolean> opt_if_not_exists
%type <ival> generated_when override_kind
%type <partspec> PartitionSpec OptPartitionSpec
%type <str> part_strategy
%type <partelem> part_elem
@@ -631,7 +634,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR
FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS
GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING
GENERATED GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING
HANDLER HAVING HEADER_P HOLD HOUR_P
@@ -655,7 +658,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
NULLS_P NUMERIC
OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OPTIONS OR
ORDER ORDINALITY OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
ORDER ORDINALITY OUT_P OUTER_P OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER
PARALLEL PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POLICY
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
@@ -726,6 +729,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
* same as if they weren't keywords). We need to do this for PARTITION,
* RANGE, ROWS to support opt_existing_window_name; and for RANGE, ROWS
* so that they can follow a_expr without creating postfix-operator problems;
* for GENERATED so that it can follow b_expr;
* and for NULL so that it can follow b_expr in ColQualList without creating
* postfix-operator problems.
*
@@ -744,7 +748,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
* blame any funny behavior of UNBOUNDED on the SQL standard, though.
*/
%nonassoc UNBOUNDED /* ideally should have same precedence as IDENT */
%nonassoc IDENT NULL_P PARTITION RANGE ROWS PRECEDING FOLLOWING CUBE ROLLUP
%nonassoc IDENT GENERATED NULL_P PARTITION RANGE ROWS PRECEDING FOLLOWING CUBE ROLLUP
%left Op OPERATOR /* multi-character ops and user-defined operators */
%left '+' '-'
%left '*' '/' '%'
@@ -2128,6 +2132,50 @@ alter_table_cmd:
n->def = (Node *) makeString($6);
$$ = (Node *)n;
}
/* ALTER TABLE <name> ALTER [COLUMN] <colname> ADD GENERATED ... AS IDENTITY ... */
| ALTER opt_column ColId ADD_P GENERATED generated_when AS IDENTITY_P OptParenthesizedSeqOptList
{
AlterTableCmd *n = makeNode(AlterTableCmd);
Constraint *c = makeNode(Constraint);
c->contype = CONSTR_IDENTITY;
c->generated_when = $6;
c->options = $9;
c->location = @5;
n->subtype = AT_AddIdentity;
n->name = $3;
n->def = (Node *) c;
$$ = (Node *)n;
}
/* ALTER TABLE <name> ALTER [COLUMN] <colname> SET <sequence options>/RESET */
| ALTER opt_column ColId alter_identity_column_option_list
{
AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = AT_SetIdentity;
n->name = $3;
n->def = (Node *) $4;
$$ = (Node *)n;
}
/* ALTER TABLE <name> ALTER [COLUMN] <colname> DROP IDENTITY */
| ALTER opt_column ColId DROP IDENTITY_P
{
AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = AT_DropIdentity;
n->name = $3;
n->missing_ok = false;
$$ = (Node *)n;
}
/* ALTER TABLE <name> ALTER [COLUMN] <colname> DROP IDENTITY IF EXISTS */
| ALTER opt_column ColId DROP IDENTITY_P IF_P EXISTS
{
AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = AT_DropIdentity;
n->name = $3;
n->missing_ok = true;
$$ = (Node *)n;
}
/* ALTER TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */
| DROP opt_column IF_P EXISTS ColId opt_drop_behavior
{
@@ -2565,6 +2613,39 @@ reloption_elem:
}
;
alter_identity_column_option_list:
alter_identity_column_option
{ $$ = list_make1($1); }
| alter_identity_column_option_list alter_identity_column_option
{ $$ = lappend($1, $2); }
;
alter_identity_column_option:
RESTART
{
$$ = makeDefElem("restart", NULL, @1);
}
| RESTART opt_with NumericOnly
{
$$ = makeDefElem("restart", (Node *)$3, @1);
}
| SET SeqOptElem
{
if (strcmp($2->defname, "as") == 0 ||
strcmp($2->defname, "restart") == 0 ||
strcmp($2->defname, "owned_by") == 0)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("sequence option \"%s\" not supported here", $2->defname),
parser_errposition(@2)));
$$ = $2;
}
| SET GENERATED generated_when
{
$$ = makeDefElem("generated", (Node *) makeInteger($3), @1);
}
;
ForValues:
/* a LIST partition */
FOR VALUES IN_P '(' partbound_datum_list ')'
@@ -3347,6 +3428,15 @@ ColConstraintElem:
n->cooked_expr = NULL;
$$ = (Node *)n;
}
| GENERATED generated_when AS IDENTITY_P OptParenthesizedSeqOptList
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_IDENTITY;
n->generated_when = $2;
n->options = $5;
n->location = @1;
$$ = (Node *)n;
}
| REFERENCES qualified_name opt_column_list key_match key_actions
{
Constraint *n = makeNode(Constraint);
@@ -3364,6 +3454,11 @@ ColConstraintElem:
}
;
generated_when:
ALWAYS { $$ = ATTRIBUTE_IDENTITY_ALWAYS; }
| BY DEFAULT { $$ = ATTRIBUTE_IDENTITY_BY_DEFAULT; }
;
/*
* ConstraintAttr represents constraint attributes, which we parse as if
* they were independent constraint clauses, in order to avoid shift/reduce
@@ -3430,6 +3525,7 @@ TableLikeOptionList:
TableLikeOption:
DEFAULTS { $$ = CREATE_TABLE_LIKE_DEFAULTS; }
| CONSTRAINTS { $$ = CREATE_TABLE_LIKE_CONSTRAINTS; }
| IDENTITY_P { $$ = CREATE_TABLE_LIKE_IDENTITY; }
| INDEXES { $$ = CREATE_TABLE_LIKE_INDEXES; }
| STORAGE { $$ = CREATE_TABLE_LIKE_STORAGE; }
| COMMENTS { $$ = CREATE_TABLE_LIKE_COMMENTS; }
@@ -3967,6 +4063,10 @@ OptSeqOptList: SeqOptList { $$ = $1; }
| /*EMPTY*/ { $$ = NIL; }
;
OptParenthesizedSeqOptList: '(' SeqOptList ')' { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }
;
SeqOptList: SeqOptElem { $$ = list_make1($1); }
| SeqOptList SeqOptElem { $$ = lappend($1, $2); }
;
@@ -4011,6 +4111,11 @@ SeqOptElem: AS SimpleTypename
{
$$ = makeDefElem("owned_by", (Node *)$3, @1);
}
| SEQUENCE NAME_P any_name
{
/* not documented, only used by pg_dump */
$$ = makeDefElem("sequence_name", (Node *)$3, @1);
}
| START opt_with NumericOnly
{
$$ = makeDefElem("start", (Node *)$3, @1);
@@ -10412,12 +10517,26 @@ insert_rest:
$$->cols = NIL;
$$->selectStmt = $1;
}
| OVERRIDING override_kind VALUE_P SelectStmt
{
$$ = makeNode(InsertStmt);
$$->cols = NIL;
$$->override = $2;
$$->selectStmt = $4;
}
| '(' insert_column_list ')' SelectStmt
{
$$ = makeNode(InsertStmt);
$$->cols = $2;
$$->selectStmt = $4;
}
| '(' insert_column_list ')' OVERRIDING override_kind VALUE_P SelectStmt
{
$$ = makeNode(InsertStmt);
$$->cols = $2;
$$->override = $5;
$$->selectStmt = $7;
}
| DEFAULT VALUES
{
$$ = makeNode(InsertStmt);
@@ -10426,6 +10545,11 @@ insert_rest:
}
;
override_kind:
USER { $$ = OVERRIDING_USER_VALUE; }
| SYSTEM_P { $$ = OVERRIDING_SYSTEM_VALUE; }
;
insert_column_list:
insert_column_item
{ $$ = list_make1($1); }
@@ -14597,6 +14721,7 @@ unreserved_keyword:
| FORWARD
| FUNCTION
| FUNCTIONS
| GENERATED
| GLOBAL
| GRANTED
| HANDLER
@@ -14666,6 +14791,7 @@ unreserved_keyword:
| OPTIONS
| ORDINALITY
| OVER
| OVERRIDING
| OWNED
| OWNER
| PARALLEL

View File

@@ -42,6 +42,7 @@
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "miscadmin.h"
@@ -356,6 +357,132 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
return result;
}
static void
generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
Oid seqtypid, List *seqoptions, bool for_identity,
char **snamespace_p, char **sname_p)
{
ListCell *option;
DefElem *nameEl = NULL;
Oid snamespaceid;
char *snamespace;
char *sname;
CreateSeqStmt *seqstmt;
AlterSeqStmt *altseqstmt;
List *attnamelist;
/*
* Determine namespace and name to use for the sequence.
*
* First, check if a sequence name was passed in as an option. This is
* used by pg_dump. Else, generate a name.
*
* Although we use ChooseRelationName, it's not guaranteed that the
* selected sequence name won't conflict; given sufficiently long
* field names, two different serial columns in the same table could
* be assigned the same sequence name, and we'd not notice since we
* aren't creating the sequence quite yet. In practice this seems
* quite unlikely to be a problem, especially since few people would
* need two serial columns in one table.
*/
foreach(option, seqoptions)
{
DefElem *defel = castNode(DefElem, lfirst(option));
if (strcmp(defel->defname, "sequence_name") == 0)
{
if (nameEl)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
nameEl = defel;
}
}
if (nameEl)
{
RangeVar *rv = makeRangeVarFromNameList(castNode(List, nameEl->arg));
snamespace = rv->schemaname;
sname = rv->relname;
seqoptions = list_delete_ptr(seqoptions, nameEl);
}
else
{
if (cxt->rel)
snamespaceid = RelationGetNamespace(cxt->rel);
else
{
snamespaceid = RangeVarGetCreationNamespace(cxt->relation);
RangeVarAdjustRelationPersistence(cxt->relation, snamespaceid);
}
snamespace = get_namespace_name(snamespaceid);
sname = ChooseRelationName(cxt->relation->relname,
column->colname,
"seq",
snamespaceid);
}
ereport(DEBUG1,
(errmsg("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"",
cxt->stmtType, sname,
cxt->relation->relname, column->colname)));
/*
* Build a CREATE SEQUENCE command to create the sequence object, and
* add it to the list of things to be done before this CREATE/ALTER
* TABLE.
*/
seqstmt = makeNode(CreateSeqStmt);
seqstmt->for_identity = for_identity;
seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
seqstmt->options = seqoptions;
/*
* If a sequence data type was specified, add it to the options. Prepend
* to the list rather than append; in case a user supplied their own AS
* clause, the "redundant options" error will point to their occurrence,
* not our synthetic one.
*/
if (seqtypid)
seqstmt->options = lcons(makeDefElem("as", (Node *) makeTypeNameFromOid(seqtypid, -1), -1),
seqstmt->options);
/*
* If this is ALTER ADD COLUMN, make sure the sequence will be owned
* by the table's owner. The current user might be someone else
* (perhaps a superuser, or someone who's only a member of the owning
* role), but the SEQUENCE OWNED BY mechanisms will bleat unless table
* and sequence have exactly the same owning role.
*/
if (cxt->rel)
seqstmt->ownerId = cxt->rel->rd_rel->relowner;
else
seqstmt->ownerId = InvalidOid;
cxt->blist = lappend(cxt->blist, seqstmt);
/*
* Build an ALTER SEQUENCE ... OWNED BY command to mark the sequence
* as owned by this column, and add it to the list of things to be
* done after this CREATE/ALTER TABLE.
*/
altseqstmt = makeNode(AlterSeqStmt);
altseqstmt->sequence = makeRangeVar(snamespace, sname, -1);
attnamelist = list_make3(makeString(snamespace),
makeString(cxt->relation->relname),
makeString(column->colname));
altseqstmt->options = list_make1(makeDefElem("owned_by",
(Node *) attnamelist, -1));
altseqstmt->for_identity = for_identity;
cxt->alist = lappend(cxt->alist, altseqstmt);
if (snamespace_p)
*snamespace_p = snamespace;
if (sname_p)
*sname_p = sname;
}
/*
* transformColumnDefinition -
* transform a single ColumnDef within CREATE TABLE
@@ -367,7 +494,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
bool is_serial;
bool saw_nullable;
bool saw_default;
Constraint *constraint;
bool saw_identity;
ListCell *clist;
cxt->columns = lappend(cxt->columns, column);
@@ -422,83 +549,17 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
/* Special actions for SERIAL pseudo-types */
if (is_serial)
{
Oid snamespaceid;
char *snamespace;
char *sname;
char *qstring;
A_Const *snamenode;
TypeCast *castnode;
FuncCall *funccallnode;
CreateSeqStmt *seqstmt;
AlterSeqStmt *altseqstmt;
List *attnamelist;
Constraint *constraint;
/*
* Determine namespace and name to use for the sequence.
*
* Although we use ChooseRelationName, it's not guaranteed that the
* selected sequence name won't conflict; given sufficiently long
* field names, two different serial columns in the same table could
* be assigned the same sequence name, and we'd not notice since we
* aren't creating the sequence quite yet. In practice this seems
* quite unlikely to be a problem, especially since few people would
* need two serial columns in one table.
*/
if (cxt->rel)
snamespaceid = RelationGetNamespace(cxt->rel);
else
{
snamespaceid = RangeVarGetCreationNamespace(cxt->relation);
RangeVarAdjustRelationPersistence(cxt->relation, snamespaceid);
}
snamespace = get_namespace_name(snamespaceid);
sname = ChooseRelationName(cxt->relation->relname,
column->colname,
"seq",
snamespaceid);
ereport(DEBUG1,
(errmsg("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"",
cxt->stmtType, sname,
cxt->relation->relname, column->colname)));
/*
* Build a CREATE SEQUENCE command to create the sequence object, and
* add it to the list of things to be done before this CREATE/ALTER
* TABLE.
*/
seqstmt = makeNode(CreateSeqStmt);
seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1));
/*
* If this is ALTER ADD COLUMN, make sure the sequence will be owned
* by the table's owner. The current user might be someone else
* (perhaps a superuser, or someone who's only a member of the owning
* role), but the SEQUENCE OWNED BY mechanisms will bleat unless table
* and sequence have exactly the same owning role.
*/
if (cxt->rel)
seqstmt->ownerId = cxt->rel->rd_rel->relowner;
else
seqstmt->ownerId = InvalidOid;
cxt->blist = lappend(cxt->blist, seqstmt);
/*
* Build an ALTER SEQUENCE ... OWNED BY command to mark the sequence
* as owned by this column, and add it to the list of things to be
* done after this CREATE/ALTER TABLE.
*/
altseqstmt = makeNode(AlterSeqStmt);
altseqstmt->sequence = makeRangeVar(snamespace, sname, -1);
attnamelist = list_make3(makeString(snamespace),
makeString(cxt->relation->relname),
makeString(column->colname));
altseqstmt->options = list_make1(makeDefElem("owned_by",
(Node *) attnamelist, -1));
cxt->alist = lappend(cxt->alist, altseqstmt);
generateSerialExtraStmts(cxt, column,
column->typeName->typeOid, NIL, false,
&snamespace, &sname);
/*
* Create appropriate constraints for SERIAL. We do this in full,
@@ -540,10 +601,11 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
saw_nullable = false;
saw_default = false;
saw_identity = false;
foreach(clist, column->constraints)
{
constraint = castNode(Constraint, lfirst(clist));
Constraint *constraint = castNode(Constraint, lfirst(clist));
switch (constraint->contype)
{
@@ -584,6 +646,33 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
saw_default = true;
break;
case CONSTR_IDENTITY:
{
Type ctype;
Oid typeOid;
ctype = typenameType(cxt->pstate, column->typeName, NULL);
typeOid = HeapTupleGetOid(ctype);
ReleaseSysCache(ctype);
if (saw_identity)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("multiple identity specifications for column \"%s\" of table \"%s\"",
column->colname, cxt->relation->relname),
parser_errposition(cxt->pstate,
constraint->location)));
generateSerialExtraStmts(cxt, column,
typeOid, constraint->options, true,
NULL, NULL);
column->identity = constraint->generated_when;
saw_identity = true;
column->is_not_null = TRUE;
break;
}
case CONSTR_CHECK:
cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
break;
@@ -660,6 +749,14 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
constraint->contype);
break;
}
if (saw_default && saw_identity)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("both default and identity specified for column \"%s\" of table \"%s\"",
column->colname, cxt->relation->relname),
parser_errposition(cxt->pstate,
constraint->location)));
}
/*
@@ -932,6 +1029,27 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
def->cooked_default = this_default;
}
/*
* Copy identity if requested
*/
if (attribute->attidentity &&
(table_like_clause->options & CREATE_TABLE_LIKE_IDENTITY))
{
Oid seq_relid;
List *seq_options;
/*
* find sequence owned by old column; extract sequence parameters;
* build new create sequence command
*/
seq_relid = getOwnedSequence(RelationGetRelid(relation), attribute->attnum);
seq_options = sequence_options(seq_relid);
generateSerialExtraStmts(cxt, def,
InvalidOid, seq_options, true,
NULL, NULL);
def->identity = attribute->attidentity;
}
/* Likewise, copy storage if requested */
if (table_like_clause->options & CREATE_TABLE_LIKE_STORAGE)
def->storage = attribute->attstorage;
@@ -2628,6 +2746,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
case AT_AlterColumnType:
{
ColumnDef *def = (ColumnDef *) cmd->def;
AttrNumber attnum;
/*
* For ALTER COLUMN TYPE, transform the USING clause if
@@ -2640,6 +2759,103 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
EXPR_KIND_ALTER_COL_TRANSFORM);
}
/*
* For identity column, create ALTER SEQUENCE command to
* change the data type of the sequence.
*/
attnum = get_attnum(relid, cmd->name);
/* if attribute not found, something will error about it later */
if (attnum != InvalidAttrNumber && get_attidentity(relid, attnum))
{
Oid seq_relid = getOwnedSequence(relid, attnum);
Oid typeOid = typenameTypeId(pstate, def->typeName);
AlterSeqStmt *altseqstmt = makeNode(AlterSeqStmt);
altseqstmt->sequence = makeRangeVar(get_namespace_name(get_rel_namespace(seq_relid)),
get_rel_name(seq_relid),
-1);
altseqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(typeOid, -1), -1));
altseqstmt->for_identity = true;
cxt.blist = lappend(cxt.blist, altseqstmt);
}
newcmds = lappend(newcmds, cmd);
break;
}
case AT_AddIdentity:
{
Constraint *def = castNode(Constraint, cmd->def);
ColumnDef *newdef = makeNode(ColumnDef);
AttrNumber attnum;
newdef->colname = cmd->name;
newdef->identity = def->generated_when;
cmd->def = (Node *) newdef;
attnum = get_attnum(relid, cmd->name);
/* if attribute not found, something will error about it later */
if (attnum != InvalidAttrNumber)
generateSerialExtraStmts(&cxt, newdef,
get_atttype(relid, attnum),
def->options, true,
NULL, NULL);
newcmds = lappend(newcmds, cmd);
break;
}
case AT_SetIdentity:
{
/*
* Create an ALTER SEQUENCE statement for the internal
* sequence of the identity column.
*/
ListCell *lc;
List *newseqopts = NIL;
List *newdef = NIL;
List *seqlist;
AttrNumber attnum;
/*
* Split options into those handled by ALTER SEQUENCE and
* those for ALTER TABLE proper.
*/
foreach(lc, castNode(List, cmd->def))
{
DefElem *def = castNode(DefElem, lfirst(lc));
if (strcmp(def->defname, "generated") == 0)
newdef = lappend(newdef, def);
else
newseqopts = lappend(newseqopts, def);
}
attnum = get_attnum(relid, cmd->name);
if (attnum)
{
seqlist = getOwnedSequences(relid, attnum);
if (seqlist)
{
AlterSeqStmt *seqstmt;
Oid seq_relid;
seqstmt = makeNode(AlterSeqStmt);
seq_relid = linitial_oid(seqlist);
seqstmt->sequence = makeRangeVar(get_namespace_name(get_rel_namespace(seq_relid)),
get_rel_name(seq_relid), -1);
seqstmt->options = newseqopts;
seqstmt->for_identity = true;
seqstmt->missing_ok = false;
cxt.alist = lappend(cxt.alist, seqstmt);
}
}
/* If column was not found or was not an identity column, we
* just let the ALTER TABLE command error out later. */
cmd->def = (Node *) newdef;
newcmds = lappend(newcmds, cmd);
break;
}

View File

@@ -21,6 +21,7 @@
#include "postgres.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "foreign/fdwapi.h"
@@ -61,6 +62,7 @@ static Query *rewriteRuleAction(Query *parsetree,
static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);
static List *rewriteTargetListIU(List *targetList,
CmdType commandType,
OverridingKind override,
Relation target_relation,
int result_rti,
List **attrno_list);
@@ -709,6 +711,7 @@ adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
static List *
rewriteTargetListIU(List *targetList,
CmdType commandType,
OverridingKind override,
Relation target_relation,
int result_rti,
List **attrno_list)
@@ -789,6 +792,7 @@ rewriteTargetListIU(List *targetList,
for (attrno = 1; attrno <= numattrs; attrno++)
{
TargetEntry *new_tle = new_tles[attrno - 1];
bool apply_default;
att_tup = target_relation->rd_att->attrs[attrno - 1];
@@ -801,12 +805,51 @@ rewriteTargetListIU(List *targetList,
* it's an INSERT and there's no tlist entry for the column, or the
* tlist entry is a DEFAULT placeholder node.
*/
if ((new_tle == NULL && commandType == CMD_INSERT) ||
(new_tle && new_tle->expr && IsA(new_tle->expr, SetToDefault)))
apply_default = ((new_tle == NULL && commandType == CMD_INSERT) ||
(new_tle && new_tle->expr && IsA(new_tle->expr, SetToDefault)));
if (commandType == CMD_INSERT)
{
if (att_tup->attidentity == ATTRIBUTE_IDENTITY_ALWAYS && !apply_default)
{
if (override != OVERRIDING_SYSTEM_VALUE)
ereport(ERROR,
(errcode(ERRCODE_GENERATED_ALWAYS),
errmsg("cannot insert into column \"%s\"", NameStr(att_tup->attname)),
errdetail("Column \"%s\" is an identity column defined as GENERATED ALWAYS.",
NameStr(att_tup->attname)),
errhint("Use OVERRIDING SYSTEM VALUE to override.")));
}
if (att_tup->attidentity == ATTRIBUTE_IDENTITY_BY_DEFAULT && override == OVERRIDING_USER_VALUE)
apply_default = true;
}
if (commandType == CMD_UPDATE)
{
if (att_tup->attidentity == ATTRIBUTE_IDENTITY_ALWAYS && !apply_default)
ereport(ERROR,
(errcode(ERRCODE_GENERATED_ALWAYS),
errmsg("column \"%s\" can only be updated to DEFAULT", NameStr(att_tup->attname)),
errdetail("Column \"%s\" is an identity column defined as GENERATED ALWAYS.",
NameStr(att_tup->attname))));
}
if (apply_default)
{
Node *new_expr;
new_expr = build_column_default(target_relation, attrno);
if (att_tup->attidentity)
{
NextValueExpr *nve = makeNode(NextValueExpr);
nve->seqid = getOwnedSequence(RelationGetRelid(target_relation), attrno);
nve->typeId = att_tup->atttypid;
new_expr = (Node *) nve;
}
else
new_expr = build_column_default(target_relation, attrno);
/*
* If there is no default (ie, default is effectively NULL), we
@@ -3232,6 +3275,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
/* Process the main targetlist ... */
parsetree->targetList = rewriteTargetListIU(parsetree->targetList,
parsetree->commandType,
parsetree->override,
rt_entry_relation,
parsetree->resultRelation,
&attrnos);
@@ -3244,6 +3288,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
parsetree->targetList =
rewriteTargetListIU(parsetree->targetList,
parsetree->commandType,
parsetree->override,
rt_entry_relation,
parsetree->resultRelation, NULL);
}
@@ -3254,6 +3299,7 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
parsetree->onConflict->onConflictSet =
rewriteTargetListIU(parsetree->onConflict->onConflictSet,
CMD_UPDATE,
parsetree->override,
rt_entry_relation,
parsetree->resultRelation,
NULL);
@@ -3263,7 +3309,9 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
{
parsetree->targetList =
rewriteTargetListIU(parsetree->targetList,
parsetree->commandType, rt_entry_relation,
parsetree->commandType,
parsetree->override,
rt_entry_relation,
parsetree->resultRelation, NULL);
rewriteTargetListUD(parsetree, rt_entry, rt_entry_relation);
}

View File

@@ -5936,6 +5936,14 @@ get_insert_query_def(Query *query, deparse_context *context)
if (query->targetList)
appendStringInfoString(buf, ") ");
if (query->override)
{
if (query->override == OVERRIDING_SYSTEM_VALUE)
appendStringInfoString(buf, "OVERRIDING SYSTEM VALUE ");
else if (query->override == OVERRIDING_USER_VALUE)
appendStringInfoString(buf, "OVERRIDING USER VALUE ");
}
if (select_rte)
{
/* Add the SELECT */

View File

@@ -836,6 +836,38 @@ get_attnum(Oid relid, const char *attname)
return InvalidAttrNumber;
}
/*
* get_attidentity
*
* Given the relation id and the attribute name,
* return the "attidentity" field from the attribute relation.
*
* Returns '\0' if not found.
*
* Since no identity is represented by '\0', this can also be used as a
* Boolean test.
*/
char
get_attidentity(Oid relid, AttrNumber attnum)
{
HeapTuple tp;
tp = SearchSysCache2(ATTNUM,
ObjectIdGetDatum(relid),
Int16GetDatum(attnum));
if (HeapTupleIsValid(tp))
{
Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
char result;
result = att_tup->attidentity;
ReleaseSysCache(tp);
return result;
}
else
return '\0';
}
/*
* get_atttype
*

View File

@@ -3268,6 +3268,7 @@ RelationBuildLocalRelation(const char *relname,
has_not_null = false;
for (i = 0; i < natts; i++)
{
rel->rd_att->attrs[i]->attidentity = tupDesc->attrs[i]->attidentity;
rel->rd_att->attrs[i]->attnotnull = tupDesc->attrs[i]->attnotnull;
has_not_null |= tupDesc->attrs[i]->attnotnull;
}

View File

@@ -327,6 +327,7 @@ Section: Class 42 - Syntax Error or Access Rule Violation
42P21 E ERRCODE_COLLATION_MISMATCH collation_mismatch
42P22 E ERRCODE_INDETERMINATE_COLLATION indeterminate_collation
42809 E ERRCODE_WRONG_OBJECT_TYPE wrong_object_type
428C9 E ERRCODE_GENERATED_ALWAYS generated_always
# Note: for ERRCODE purposes, we divide namable objects into these categories:
# databases, schemas, prepared statements, cursors, tables, columns,