diff options
author | Peter Eisentraut <peter_e@gmx.net> | 2017-04-06 08:33:16 -0400 |
---|---|---|
committer | Peter Eisentraut <peter_e@gmx.net> | 2017-04-06 08:41:37 -0400 |
commit | 3217327053638085d24dd4d276e7c1f7ac2c4c6b (patch) | |
tree | 513d1264a2935b05e28b0d8322d73a0411a3d02f /src/backend/commands/sequence.c | |
parent | 6bad580d9e678a0b604883e14d8401d469b06566 (diff) | |
download | postgresql-3217327053638085d24dd4d276e7c1f7ac2c4c6b.tar.gz postgresql-3217327053638085d24dd4d276e7c1f7ac2c4c6b.zip |
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>
Diffstat (limited to 'src/backend/commands/sequence.c')
-rw-r--r-- | src/backend/commands/sequence.c | 101 |
1 files changed, 83 insertions, 18 deletions
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 89b810bbb7b..ad28225b363 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -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 */ @@ -1676,6 +1714,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) */ Datum |