<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/alter_sequence.sgml,v 1.19 2007/10/25 18:54:03 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_sequence.sgml,v 1.20 2008/05/16 23:36:04 tgl Exp $
PostgreSQL documentation
-->
<synopsis>
ALTER SEQUENCE <replaceable class="parameter">name</replaceable> [ INCREMENT [ BY ] <replaceable class="parameter">increment</replaceable> ]
[ MINVALUE <replaceable class="parameter">minvalue</replaceable> | NO MINVALUE ] [ MAXVALUE <replaceable class="parameter">maxvalue</replaceable> | NO MAXVALUE ]
- [ RESTART [ WITH ] <replaceable class="parameter">start</replaceable> ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
+ [ RESTART [ [ WITH ] <replaceable class="parameter">start</replaceable> ] ] [ CACHE <replaceable class="parameter">cache</replaceable> ] [ [ NO ] CYCLE ]
[ OWNED BY { <replaceable class="parameter">table</replaceable>.<replaceable class="parameter">column</replaceable> | NONE } ]
ALTER SEQUENCE <replaceable class="parameter">name</replaceable> RENAME TO <replaceable class="parameter">new_name</replaceable>
ALTER SEQUENCE <replaceable class="parameter">name</replaceable> SET SCHEMA <replaceable class="parameter">new_schema</replaceable>
<term><replaceable class="parameter">start</replaceable></term>
<listitem>
<para>
- The optional clause <literal>RESTART WITH <replaceable
- class="parameter">start</replaceable></literal> changes the
+ The optional clause <literal>RESTART [ WITH <replaceable
+ class="parameter">start</replaceable> ]</literal> changes the
current value of the sequence. This is equivalent to calling the
<function>setval</> function with <literal>is_called</literal> =
<literal>false</>: the specified value will be returned by the
<emphasis>next</> call of <function>nextval</>.
+ Writing <literal>RESTART</> with no <replaceable
+ class="parameter">start</replaceable> value is equivalent to supplying
+ the start value used when the sequence was created.
</para>
</listitem>
</varlistentry>
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/truncate.sgml,v 1.25 2008/03/28 00:21:55 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/truncate.sgml,v 1.26 2008/05/16 23:36:04 tgl Exp $
PostgreSQL documentation
-->
<refsynopsisdiv>
<synopsis>
-TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+TRUNCATE [ TABLE ] <replaceable class="PARAMETER">name</replaceable> [, ... ]
+ [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]
</synopsis>
</refsynopsisdiv>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>RESTART IDENTITY</literal></term>
+ <listitem>
+ <para>
+ Automatically restart sequences owned by columns of
+ the truncated table(s).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>CONTINUE IDENTITY</literal></term>
+ <listitem>
+ <para>
+ Do not change the values of sequences. This is the default.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>CASCADE</literal></term>
<listitem>
<listitem>
<para>
Refuse to truncate if any of the tables have foreign-key references
- from tables that are not to be truncated. This is the default.
+ from tables that are not listed in the command. This is the default.
</para>
</listitem>
</varlistentry>
cause visible inconsistency between the contents of the truncated
table and other tables in the database.
</para>
+ </warning>
+
+ <para>
+ <command>TRUNCATE</> is transaction-safe with respect to the data
+ in the tables: the truncation will be safely rolled back if the surrounding
+ transaction does not commit.
+ </para>
+ <warning>
<para>
- <command>TRUNCATE</> is transaction-safe, however: the truncation
- will be safely rolled back if the surrounding transaction does not
- commit.
+ Any <command>ALTER SEQUENCE RESTART</> operations performed as a
+ consequence of using the <literal>RESTART IDENTITY</> option are
+ nontransactional and will not be rolled back. To minimize risk,
+ these operations are performed only after all the rest of
+ <command>TRUNCATE</>'s work is done. In practice this will only
+ be an issue if <command>TRUNCATE</> is performed inside a
+ transaction block that is aborted afterwards.
</para>
</warning>
</refsect1>
<title>Examples</title>
<para>
- Truncate the tables <literal>bigtable</literal> and <literal>fattable</literal>:
+ Truncate the tables <literal>bigtable</literal> and
+ <literal>fattable</literal>:
<programlisting>
TRUNCATE bigtable, fattable;
</programlisting>
</para>
+ <para>
+ The same, and also reset any associated sequence generators:
+
+<programlisting>
+TRUNCATE bigtable, fattable RESTART IDENTITY;
+</programlisting>
+ </para>
+
<para>
Truncate the table <literal>othertable</literal>, and cascade to any tables
that reference <literal>othertable</literal> via foreign-key
<title>Compatibility</title>
<para>
- There is no <command>TRUNCATE</command> command in the SQL standard.
+ The draft SQL:2008 standard includes a <command>TRUNCATE</command> command,
+ but at this writing it is uncertain whether that will reach standardization
+ or be fully compatible with <productname>PostgreSQL</productname>'s
+ implementation.
</para>
</refsect1>
</refentry>
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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++)
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";
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;
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 */
*/
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;
*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);
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),
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],
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)
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)));
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
{
List *rels = NIL;
List *relids = NIL;
+ List *seq_relids = NIL;
EState *estate;
ResultRelInfo *resultRelInfos;
ResultRelInfo *resultRelInfo;
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();
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);
+ }
+ }
}
/*
* 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 $
*
*-------------------------------------------------------------------------
*/
TruncateStmt *newnode = makeNode(TruncateStmt);
COPY_NODE_FIELD(relations);
+ COPY_SCALAR_FIELD(restart_seqs);
COPY_SCALAR_FIELD(behavior);
return newnode;
* 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 $
*
*-------------------------------------------------------------------------
*/
_equalTruncateStmt(TruncateStmt *a, TruncateStmt *b)
{
COMPARE_NODE_FIELD(relations);
+ COMPARE_SCALAR_FIELD(restart_seqs);
COMPARE_SCALAR_FIELD(behavior);
return true;
*
*
* 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
%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
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
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
{
$$ = makeDefElem("start", (Node *)$3);
}
+ | RESTART
+ {
+ $$ = makeDefElem("restart", NULL);
+ }
| RESTART opt_with NumericOnly
{
$$ = makeDefElem("restart", (Node *)$3);
*****************************************************************************/
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
| CONNECTION
| CONSTRAINTS
| CONTENT_P
+ | CONTINUE_P
| CONVERSION_P
| COPY
| COST
| HEADER_P
| HOLD
| HOUR_P
+ | IDENTITY_P
| IF_P
| IMMEDIATE
| IMMUTABLE
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{"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},
{"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},
* by PostgreSQL
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.491 2008/05/12 00:00:53 alvherre Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.492 2008/05/16 23:36:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
dumpSequence(Archive *fout, TableInfo *tbinfo)
{
PGresult *res;
- char *last,
+ char *startv,
+ *last,
*incby,
*maxv = NULL,
*minv = NULL,
snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE);
snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE);
- appendPQExpBuffer(query,
- "SELECT sequence_name, last_value, increment_by, "
- "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
- " WHEN increment_by < 0 AND max_value = -1 THEN NULL "
- " ELSE max_value "
- "END AS max_value, "
- "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL "
- " WHEN increment_by < 0 AND min_value = %s THEN NULL "
- " ELSE min_value "
- "END AS min_value, "
- "cache_value, is_cycled, is_called from %s",
- bufx, bufm,
- fmtId(tbinfo->dobj.name));
+ if (g_fout->remoteVersion >= 80400)
+ {
+ appendPQExpBuffer(query,
+ "SELECT sequence_name, "
+ "start_value, last_value, increment_by, "
+ "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
+ " WHEN increment_by < 0 AND max_value = -1 THEN NULL "
+ " ELSE max_value "
+ "END AS max_value, "
+ "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL "
+ " WHEN increment_by < 0 AND min_value = %s THEN NULL "
+ " ELSE min_value "
+ "END AS min_value, "
+ "cache_value, is_cycled, is_called from %s",
+ bufx, bufm,
+ fmtId(tbinfo->dobj.name));
+ }
+ else
+ {
+ appendPQExpBuffer(query,
+ "SELECT sequence_name, "
+ "0 as start_value, last_value, increment_by, "
+ "CASE WHEN increment_by > 0 AND max_value = %s THEN NULL "
+ " WHEN increment_by < 0 AND max_value = -1 THEN NULL "
+ " ELSE max_value "
+ "END AS max_value, "
+ "CASE WHEN increment_by > 0 AND min_value = 1 THEN NULL "
+ " WHEN increment_by < 0 AND min_value = %s THEN NULL "
+ " ELSE min_value "
+ "END AS min_value, "
+ "cache_value, is_cycled, is_called from %s",
+ bufx, bufm,
+ fmtId(tbinfo->dobj.name));
+ }
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
}
#endif
- last = PQgetvalue(res, 0, 1);
- incby = PQgetvalue(res, 0, 2);
- if (!PQgetisnull(res, 0, 3))
- maxv = PQgetvalue(res, 0, 3);
+ startv = PQgetvalue(res, 0, 1);
+ last = PQgetvalue(res, 0, 2);
+ incby = PQgetvalue(res, 0, 3);
if (!PQgetisnull(res, 0, 4))
- minv = PQgetvalue(res, 0, 4);
- cache = PQgetvalue(res, 0, 5);
- cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
- called = (strcmp(PQgetvalue(res, 0, 7), "t") == 0);
+ maxv = PQgetvalue(res, 0, 4);
+ if (!PQgetisnull(res, 0, 5))
+ minv = PQgetvalue(res, 0, 5);
+ cache = PQgetvalue(res, 0, 6);
+ cycled = (strcmp(PQgetvalue(res, 0, 7), "t") == 0);
+ called = (strcmp(PQgetvalue(res, 0, 8), "t") == 0);
/*
* The logic we use for restoring sequences is as follows:
"CREATE SEQUENCE %s\n",
fmtId(tbinfo->dobj.name));
- if (!called)
- appendPQExpBuffer(query, " START WITH %s\n", last);
+ if (g_fout->remoteVersion >= 80400)
+ appendPQExpBuffer(query, " START WITH %s\n", startv);
+ else
+ {
+ /*
+ * Versions before 8.4 did not remember the true start value. If
+ * is_called is false then the sequence has never been incremented
+ * so we can use last_val. Otherwise punt and let it default.
+ */
+ if (!called)
+ appendPQExpBuffer(query, " START WITH %s\n", last);
+ }
appendPQExpBuffer(query, " INCREMENT BY %s\n", incby);
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.460 2008/05/16 16:31:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.461 2008/05/16 23:36:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200805161
+#define CATALOG_VERSION_NO 200805162
#endif
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.34 2008/03/24 19:47:35 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.35 2008/05/16 23:36:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern void markSequenceUnowned(Oid seqId);
+extern List *getOwnedSequences(Oid relid);
+
extern Oid get_constraint_index(Oid constraintId);
extern Oid get_index_constraint(Oid indexId);
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/commands/sequence.h,v 1.40 2008/03/27 03:57:34 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/sequence.h,v 1.41 2008/05/16 23:36:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
NameData sequence_name;
#ifndef INT64_IS_BUSTED
int64 last_value;
+ int64 start_value;
int64 increment_by;
int64 max_value;
int64 min_value;
#else
int32 last_value;
int32 pad1;
- int32 increment_by;
+ int32 start_value;
int32 pad2;
- int32 max_value;
+ int32 increment_by;
int32 pad3;
- int32 min_value;
+ int32 max_value;
int32 pad4;
- int32 cache_value;
+ int32 min_value;
int32 pad5;
- int32 log_cnt;
+ int32 cache_value;
int32 pad6;
+ int32 log_cnt;
+ int32 pad7;
#endif
bool is_cycled;
bool is_called;
#define SEQ_COL_NAME 1
#define SEQ_COL_LASTVAL 2
-#define SEQ_COL_INCBY 3
-#define SEQ_COL_MAXVALUE 4
-#define SEQ_COL_MINVALUE 5
-#define SEQ_COL_CACHE 6
-#define SEQ_COL_LOG 7
-#define SEQ_COL_CYCLE 8
-#define SEQ_COL_CALLED 9
+#define SEQ_COL_STARTVAL 3
+#define SEQ_COL_INCBY 4
+#define SEQ_COL_MAXVALUE 5
+#define SEQ_COL_MINVALUE 6
+#define SEQ_COL_CACHE 7
+#define SEQ_COL_LOG 8
+#define SEQ_COL_CYCLE 9
+#define SEQ_COL_CALLED 10
#define SEQ_COL_FIRSTCOL SEQ_COL_NAME
#define SEQ_COL_LASTCOL SEQ_COL_CALLED
extern void DefineSequence(CreateSeqStmt *stmt);
extern void AlterSequence(AlterSeqStmt *stmt);
+extern void AlterSequenceInternal(Oid relid, List *options);
extern void seq_redo(XLogRecPtr lsn, XLogRecord *rptr);
extern void seq_desc(StringInfo buf, uint8 xl_info, char *rec);
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.365 2008/05/09 23:32:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.366 2008/05/16 23:36:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
NodeTag type;
List *relations; /* relations (RangeVars) to be truncated */
+ bool restart_seqs; /* restart owned sequences? */
DropBehavior behavior; /* RESTRICT or CASCADE behavior */
} TruncateStmt;
CREATE SEQUENCE foo_seq;
ALTER TABLE foo_seq RENAME TO foo_seq_new;
SELECT * FROM foo_seq_new;
- sequence_name | last_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called
----------------+------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq | 1 | 1 | 9223372036854775807 | 1 | 1 | 1 | f | f
+ sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
+ foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 1 | f | f
+(1 row)
+
+SELECT nextval('foo_seq_new');
+ nextval
+---------
+ 1
+(1 row)
+
+SELECT nextval('foo_seq_new');
+ nextval
+---------
+ 2
+(1 row)
+
+SELECT * FROM foo_seq_new;
+ sequence_name | last_value | start_value | increment_by | max_value | min_value | cache_value | log_cnt | is_cycled | is_called
+---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
+ foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 32 | f | t
(1 row)
DROP SEQUENCE foo_seq_new;
32
(1 row)
-ALTER SEQUENCE sequence_test2 RESTART WITH 16
- INCREMENT BY 4 MAXVALUE 22 MINVALUE 5 CYCLE;
+ALTER SEQUENCE sequence_test2 RESTART WITH 24
+ INCREMENT BY 4 MAXVALUE 36 MINVALUE 5 CYCLE;
+SELECT nextval('sequence_test2');
+ nextval
+---------
+ 24
+(1 row)
+
SELECT nextval('sequence_test2');
nextval
---------
- 16
+ 28
+(1 row)
+
+SELECT nextval('sequence_test2');
+ nextval
+---------
+ 32
+(1 row)
+
+SELECT nextval('sequence_test2');
+ nextval
+---------
+ 36
+(1 row)
+
+SELECT nextval('sequence_test2');
+ nextval
+---------
+ 5
+(1 row)
+
+ALTER SEQUENCE sequence_test2 RESTART;
+SELECT nextval('sequence_test2');
+ nextval
+---------
+ 32
(1 row)
SELECT nextval('sequence_test2');
nextval
---------
- 20
+ 36
(1 row)
SELECT nextval('sequence_test2');
DROP TABLE trunc_trigger_test;
DROP TABLE trunc_trigger_log;
DROP FUNCTION trunctrigger();
+-- test TRUNCATE ... RESTART IDENTITY
+CREATE SEQUENCE truncate_a_id1 START WITH 33;
+CREATE TABLE truncate_a (id serial,
+ id1 integer default nextval('truncate_a_id1'));
+NOTICE: CREATE TABLE will create implicit sequence "truncate_a_id_seq" for serial column "truncate_a.id"
+ALTER SEQUENCE truncate_a_id1 OWNED BY truncate_a.id1;
+INSERT INTO truncate_a DEFAULT VALUES;
+INSERT INTO truncate_a DEFAULT VALUES;
+SELECT * FROM truncate_a;
+ id | id1
+----+-----
+ 1 | 33
+ 2 | 34
+(2 rows)
+
+TRUNCATE truncate_a;
+INSERT INTO truncate_a DEFAULT VALUES;
+INSERT INTO truncate_a DEFAULT VALUES;
+SELECT * FROM truncate_a;
+ id | id1
+----+-----
+ 3 | 35
+ 4 | 36
+(2 rows)
+
+TRUNCATE truncate_a RESTART IDENTITY;
+INSERT INTO truncate_a DEFAULT VALUES;
+INSERT INTO truncate_a DEFAULT VALUES;
+SELECT * FROM truncate_a;
+ id | id1
+----+-----
+ 1 | 33
+ 2 | 34
+(2 rows)
+
+DROP TABLE truncate_a;
+SELECT nextval('truncate_a_id1'); -- fail, seq should have been dropped
+ERROR: relation "truncate_a_id1" does not exist
CREATE SEQUENCE foo_seq;
ALTER TABLE foo_seq RENAME TO foo_seq_new;
SELECT * FROM foo_seq_new;
+SELECT nextval('foo_seq_new');
+SELECT nextval('foo_seq_new');
+SELECT * FROM foo_seq_new;
DROP SEQUENCE foo_seq_new;
-- renaming serial sequences
SELECT nextval('sequence_test2');
-ALTER SEQUENCE sequence_test2 RESTART WITH 16
- INCREMENT BY 4 MAXVALUE 22 MINVALUE 5 CYCLE;
+ALTER SEQUENCE sequence_test2 RESTART WITH 24
+ INCREMENT BY 4 MAXVALUE 36 MINVALUE 5 CYCLE;
+SELECT nextval('sequence_test2');
+SELECT nextval('sequence_test2');
+SELECT nextval('sequence_test2');
+SELECT nextval('sequence_test2');
+SELECT nextval('sequence_test2');
+
+ALTER SEQUENCE sequence_test2 RESTART;
+
SELECT nextval('sequence_test2');
SELECT nextval('sequence_test2');
SELECT nextval('sequence_test2');
DROP TABLE trunc_trigger_log;
DROP FUNCTION trunctrigger();
+
+-- test TRUNCATE ... RESTART IDENTITY
+CREATE SEQUENCE truncate_a_id1 START WITH 33;
+CREATE TABLE truncate_a (id serial,
+ id1 integer default nextval('truncate_a_id1'));
+ALTER SEQUENCE truncate_a_id1 OWNED BY truncate_a.id1;
+
+INSERT INTO truncate_a DEFAULT VALUES;
+INSERT INTO truncate_a DEFAULT VALUES;
+SELECT * FROM truncate_a;
+
+TRUNCATE truncate_a;
+
+INSERT INTO truncate_a DEFAULT VALUES;
+INSERT INTO truncate_a DEFAULT VALUES;
+SELECT * FROM truncate_a;
+
+TRUNCATE truncate_a RESTART IDENTITY;
+
+INSERT INTO truncate_a DEFAULT VALUES;
+INSERT INTO truncate_a DEFAULT VALUES;
+SELECT * FROM truncate_a;
+
+DROP TABLE truncate_a;
+
+SELECT nextval('truncate_a_id1'); -- fail, seq should have been dropped