diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2008-05-16 23:36:05 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2008-05-16 23:36:05 +0000 |
commit | 10a3471bed7b57fb986a5be8afdee5f0dda419de (patch) | |
tree | 32de8db702827c67c5cb85479d9bbff22c7b6e94 /src/backend | |
parent | 8a2f5d221b0d6e41dc66b7e7389668bd208e3529 (diff) | |
download | postgresql-10a3471bed7b57fb986a5be8afdee5f0dda419de.tar.gz postgresql-10a3471bed7b57fb986a5be8afdee5f0dda419de.zip |
Add a RESTART (without parameter) option to ALTER SEQUENCE, allowing a
sequence to be reset to its original starting value. This requires adding the
original start value to the set of parameters (columns) of a sequence object,
which is a user-visible change with potential compatibility implications;
it also forces initdb.
Also add hopefully-SQL-compatible RESTART/CONTINUE IDENTITY options to
TRUNCATE TABLE. RESTART IDENTITY executes ALTER SEQUENCE RESTART for all
sequences "owned by" any of the truncated relations. CONTINUE IDENTITY is
a no-op option.
Zoltan Boszormenyi
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/pg_depend.c | 54 | ||||
-rw-r--r-- | src/backend/commands/sequence.c | 129 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 57 | ||||
-rw-r--r-- | src/backend/nodes/copyfuncs.c | 3 | ||||
-rw-r--r-- | src/backend/nodes/equalfuncs.c | 3 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 31 | ||||
-rw-r--r-- | src/backend/parser/keywords.c | 4 |
7 files changed, 238 insertions, 43 deletions
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index 32d8d07044b..d1fd8f5200f 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.27 2008/03/26 21:10:37 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.28 2008/05/16 23:36:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -418,6 +418,58 @@ markSequenceUnowned(Oid seqId) heap_close(depRel, RowExclusiveLock); } +/* + * Collect a list of OIDs of all sequences owned by the specified relation. + */ +List * +getOwnedSequences(Oid relid) +{ + List *result = NIL; + Relation depRel; + ScanKeyData key[2]; + SysScanDesc scan; + HeapTuple tup; + + depRel = heap_open(DependRelationId, AccessShareLock); + + ScanKeyInit(&key[0], + Anum_pg_depend_refclassid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(RelationRelationId)); + ScanKeyInit(&key[1], + Anum_pg_depend_refobjid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + + scan = systable_beginscan(depRel, DependReferenceIndexId, true, + SnapshotNow, 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 + * 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 && + get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE) + { + result = lappend_oid(result, deprec->objid); + } + } + + systable_endscan(scan); + + heap_close(depRel, AccessShareLock); + + return result; +} + /* * get_constraint_index diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 670fcbcfbdb..748413ebed3 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.150 2008/05/12 00:00:47 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.151 2008/05/16 23:36:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -91,7 +91,7 @@ static Relation open_share_lock(SeqTable seq); static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel); static Form_pg_sequence read_info(SeqTable elm, Relation rel, Buffer *buf); static void init_params(List *options, bool isInit, - Form_pg_sequence new, List **owned_by); + Form_pg_sequence new, Form_pg_sequence old, List **owned_by); static void do_setval(Oid relid, int64 next, bool iscalled); static void process_owned_by(Relation seqrel, List *owned_by); @@ -119,10 +119,10 @@ DefineSequence(CreateSeqStmt *seq) NameData name; /* Check and set all option values */ - init_params(seq->options, true, &new, &owned_by); + init_params(seq->options, true, &new, NULL, &owned_by); /* - * Create relation (and fill *null & *value) + * Create relation (and fill value[] and null[] for the tuple) */ stmt->tableElts = NIL; for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++) @@ -151,6 +151,11 @@ DefineSequence(CreateSeqStmt *seq) coldef->colname = "last_value"; value[i - 1] = Int64GetDatumFast(new.last_value); break; + case SEQ_COL_STARTVAL: + coldef->typename = makeTypeNameFromOid(INT8OID, -1); + coldef->colname = "start_value"; + value[i - 1] = Int64GetDatumFast(new.start_value); + break; case SEQ_COL_INCBY: coldef->typename = makeTypeNameFromOid(INT8OID, -1); coldef->colname = "increment_by"; @@ -314,6 +319,29 @@ void AlterSequence(AlterSeqStmt *stmt) { Oid relid; + + /* find sequence */ + relid = RangeVarGetRelid(stmt->sequence, false); + + /* allow ALTER to sequence owner only */ + /* if you change this, see also callers of AlterSequenceInternal! */ + if (!pg_class_ownercheck(relid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + stmt->sequence->relname); + + /* do the work */ + AlterSequenceInternal(relid, stmt->options); +} + +/* + * AlterSequenceInternal + * + * Same as AlterSequence except that the sequence is specified by OID + * and we assume the caller already checked permissions. + */ +void +AlterSequenceInternal(Oid relid, List *options) +{ SeqTable elm; Relation seqrel; Buffer buf; @@ -323,23 +351,14 @@ AlterSequence(AlterSeqStmt *stmt) List *owned_by; /* open and AccessShareLock sequence */ - relid = RangeVarGetRelid(stmt->sequence, false); init_sequence(relid, &elm, &seqrel); - /* allow ALTER to sequence owner only */ - if (!pg_class_ownercheck(elm->relid, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - stmt->sequence->relname); - /* lock page' buffer and read tuple into new sequence structure */ seq = read_info(elm, seqrel, &buf); page = BufferGetPage(buf); - /* Copy old values of options into workspace */ - memcpy(&new, seq, sizeof(FormData_pg_sequence)); - - /* Check and set new values */ - init_params(stmt->options, false, &new, &owned_by); + /* Fill workspace with appropriate new info */ + init_params(options, false, &new, seq, &owned_by); /* Clear local cache so that we don't think we have cached numbers */ /* Note that we do not change the currval() state */ @@ -970,7 +989,7 @@ read_info(SeqTable elm, Relation rel, Buffer *buf) */ static void init_params(List *options, bool isInit, - Form_pg_sequence new, List **owned_by) + Form_pg_sequence new, Form_pg_sequence old, List **owned_by) { DefElem *last_value = NULL; DefElem *increment_by = NULL; @@ -982,6 +1001,12 @@ init_params(List *options, bool isInit, *owned_by = NIL; + /* Copy old values of options into workspace */ + if (old != NULL) + memcpy(new, old, sizeof(FormData_pg_sequence)); + else + memset(new, 0, sizeof(FormData_pg_sequence)); + foreach(option, options) { DefElem *defel = (DefElem *) lfirst(option); @@ -994,13 +1019,24 @@ init_params(List *options, bool isInit, errmsg("conflicting or redundant options"))); increment_by = defel; } - - /* - * start is for a new sequence restart is for alter - */ - else if (strcmp(defel->defname, "start") == 0 || - strcmp(defel->defname, "restart") == 0) + else if (strcmp(defel->defname, "start") == 0) + { + if (!isInit) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("use RESTART not START in ALTER SEQUENCE"))); + if (last_value) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + last_value = defel; + } + else if (strcmp(defel->defname, "restart") == 0) { + if (isInit) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("use START not RESTART in CREATE SEQUENCE"))); if (last_value) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), @@ -1109,24 +1145,59 @@ init_params(List *options, bool isInit, bufm, bufx))); } - /* START WITH */ + /* START/RESTART [WITH] */ if (last_value != NULL) { - new->last_value = defGetInt64(last_value); + if (last_value->arg != NULL) + new->last_value = defGetInt64(last_value); + else + { + Assert(old != NULL); + new->last_value = old->start_value; + } + if (isInit) + new->start_value = new->last_value; new->is_called = false; new->log_cnt = 1; } else if (isInit) { if (new->increment_by > 0) - new->last_value = new->min_value; /* ascending seq */ + new->start_value = new->min_value; /* ascending seq */ else - new->last_value = new->max_value; /* descending seq */ + new->start_value = new->max_value; /* descending seq */ + new->last_value = new->start_value; new->is_called = false; new->log_cnt = 1; } - /* crosscheck */ + /* crosscheck START */ + if (new->start_value < new->min_value) + { + char bufs[100], + bufm[100]; + + snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value); + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("START value (%s) cannot be less than MINVALUE (%s)", + bufs, bufm))); + } + if (new->start_value > new->max_value) + { + char bufs[100], + bufm[100]; + + snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value); + snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value); + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("START value (%s) cannot be greater than MAXVALUE (%s)", + bufs, bufm))); + } + + /* must crosscheck RESTART separately */ if (new->last_value < new->min_value) { char bufs[100], @@ -1136,7 +1207,7 @@ init_params(List *options, bool isInit, snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("START value (%s) cannot be less than MINVALUE (%s)", + errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)", bufs, bufm))); } if (new->last_value > new->max_value) @@ -1148,7 +1219,7 @@ init_params(List *options, bool isInit, snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value); ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("START value (%s) cannot be greater than MAXVALUE (%s)", + errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)", bufs, bufm))); } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 48e229e0fb5..0bef48e07bf 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.253 2008/05/12 00:00:48 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.254 2008/05/16 23:36:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,6 +37,7 @@ #include "catalog/toasting.h" #include "commands/cluster.h" #include "commands/defrem.h" +#include "commands/sequence.h" #include "commands/tablecmds.h" #include "commands/tablespace.h" #include "commands/trigger.h" @@ -531,6 +532,7 @@ ExecuteTruncate(TruncateStmt *stmt) { List *rels = NIL; List *relids = NIL; + List *seq_relids = NIL; EState *estate; ResultRelInfo *resultRelInfos; ResultRelInfo *resultRelInfo; @@ -596,6 +598,40 @@ ExecuteTruncate(TruncateStmt *stmt) heap_truncate_check_FKs(rels, false); #endif + /* + * If we are asked to restart sequences, find all the sequences, + * lock them (we only need AccessShareLock because that's all that + * ALTER SEQUENCE takes), and check permissions. We want to do this + * early since it's pointless to do all the truncation work only to fail + * on sequence permissions. + */ + if (stmt->restart_seqs) + { + foreach(cell, rels) + { + Relation rel = (Relation) lfirst(cell); + List *seqlist = getOwnedSequences(RelationGetRelid(rel)); + ListCell *seqcell; + + foreach(seqcell, seqlist) + { + Oid seq_relid = lfirst_oid(seqcell); + Relation seq_rel; + + seq_rel = relation_open(seq_relid, AccessShareLock); + + /* This check must match AlterSequence! */ + if (!pg_class_ownercheck(seq_relid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(seq_rel)); + + seq_relids = lappend_oid(seq_relids, seq_relid); + + relation_close(seq_rel, NoLock); + } + } + } + /* Prepare to catch AFTER triggers. */ AfterTriggerBeginQuery(); @@ -694,6 +730,25 @@ ExecuteTruncate(TruncateStmt *stmt) heap_close(rel, NoLock); } + + /* + * Lastly, restart any owned sequences if we were asked to. This is done + * last because it's nontransactional: restarts will not roll back if + * we abort later. Hence it's important to postpone them as long as + * possible. (This is also a big reason why we locked and + * permission-checked the sequences beforehand.) + */ + if (stmt->restart_seqs) + { + List *options = list_make1(makeDefElem("restart", NULL)); + + foreach(cell, seq_relids) + { + Oid seq_relid = lfirst_oid(cell); + + AlterSequenceInternal(seq_relid, options); + } + } } /* diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 67dcf9a2182..a42c40327f2 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.393 2008/04/29 14:59:16 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.394 2008/05/16 23:36:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2156,6 +2156,7 @@ _copyTruncateStmt(TruncateStmt *from) TruncateStmt *newnode = makeNode(TruncateStmt); COPY_NODE_FIELD(relations); + COPY_SCALAR_FIELD(restart_seqs); COPY_SCALAR_FIELD(behavior); return newnode; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 0c444901322..435ee6a6afb 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.322 2008/04/29 14:59:16 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.323 2008/05/16 23:36:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1010,6 +1010,7 @@ static bool _equalTruncateStmt(TruncateStmt *a, TruncateStmt *b) { COMPARE_NODE_FIELD(relations); + COMPARE_SCALAR_FIELD(restart_seqs); COMPARE_SCALAR_FIELD(behavior); return true; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 56a56627e7f..591920d6f68 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.614 2008/04/29 20:44:49 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.615 2008/05/16 23:36:05 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -206,7 +206,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) %type <str> OptSchemaName %type <list> OptSchemaEltList -%type <boolean> TriggerActionTime TriggerForSpec opt_trusted +%type <boolean> TriggerActionTime TriggerForSpec opt_trusted opt_restart_seqs %type <str> opt_lancompiler %type <str> TriggerEvents @@ -381,7 +381,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS - CONTENT_P CONVERSION_P COPY COST CREATE CREATEDB + CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB CREATEROLE CREATEUSER CROSS CSV CURRENT_P CURRENT_DATE CURRENT_ROLE CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE @@ -399,10 +399,10 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) HANDLER HAVING HEADER_P HOLD HOUR_P - IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT - INDEX INDEXES INHERIT INHERITS INITIALLY INNER_P INOUT INPUT_P - INSENSITIVE INSERT INSTEAD INT_P INTEGER INTERSECT - INTERVAL INTO INVOKER IS ISNULL ISOLATION + IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P + INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY + INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER + INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION JOIN @@ -2489,6 +2489,10 @@ OptSeqElem: CACHE NumericOnly { $$ = makeDefElem("start", (Node *)$3); } + | RESTART + { + $$ = makeDefElem("restart", NULL); + } | RESTART opt_with NumericOnly { $$ = makeDefElem("restart", (Node *)$3); @@ -3364,15 +3368,22 @@ attrs: '.' attr_name *****************************************************************************/ TruncateStmt: - TRUNCATE opt_table qualified_name_list opt_drop_behavior + TRUNCATE opt_table qualified_name_list opt_restart_seqs opt_drop_behavior { TruncateStmt *n = makeNode(TruncateStmt); n->relations = $3; - n->behavior = $4; + n->restart_seqs = $4; + n->behavior = $5; $$ = (Node *)n; } ; +opt_restart_seqs: + CONTINUE_P IDENTITY_P { $$ = false; } + | RESTART IDENTITY_P { $$ = true; } + | /* EMPTY */ { $$ = false; } + ; + /***************************************************************************** * * The COMMENT ON statement can take different forms based upon the type of @@ -8964,6 +8975,7 @@ unreserved_keyword: | CONNECTION | CONSTRAINTS | CONTENT_P + | CONTINUE_P | CONVERSION_P | COPY | COST @@ -9014,6 +9026,7 @@ unreserved_keyword: | HEADER_P | HOLD | HOUR_P + | IDENTITY_P | IF_P | IMMEDIATE | IMMUTABLE diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index b5312a487fd..46b306b98d1 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.195 2008/03/27 03:57:33 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.196 2008/05/16 23:36:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -101,6 +101,7 @@ static const ScanKeyword ScanKeywords[] = { {"constraint", CONSTRAINT, RESERVED_KEYWORD}, {"constraints", CONSTRAINTS, UNRESERVED_KEYWORD}, {"content", CONTENT_P, UNRESERVED_KEYWORD}, + {"continue", CONTINUE_P, UNRESERVED_KEYWORD}, {"conversion", CONVERSION_P, UNRESERVED_KEYWORD}, {"copy", COPY, UNRESERVED_KEYWORD}, {"cost", COST, UNRESERVED_KEYWORD}, @@ -181,6 +182,7 @@ static const ScanKeyword ScanKeywords[] = { {"header", HEADER_P, UNRESERVED_KEYWORD}, {"hold", HOLD, UNRESERVED_KEYWORD}, {"hour", HOUR_P, UNRESERVED_KEYWORD}, + {"identity", IDENTITY_P, UNRESERVED_KEYWORD}, {"if", IF_P, UNRESERVED_KEYWORD}, {"ilike", ILIKE, TYPE_FUNC_NAME_KEYWORD}, {"immediate", IMMEDIATE, UNRESERVED_KEYWORD}, |