Add pg_sequence system catalog

Move sequence metadata (start, increment, etc.) into a proper system
catalog instead of storing it in the sequence heap object.  This
separates the metadata from the sequence data.  Sequence metadata is now
operated on transactionally by DDL commands, whereas previously
rollbacks of sequence-related DDL commands would be ignored.

Reviewed-by: Andreas Karlsson <andreas@proxel.se>
This commit is contained in:
Peter Eisentraut 2016-12-20 12:00:00 -05:00
parent db80acfc9d
commit 1753b1b027
19 changed files with 490 additions and 269 deletions

View File

@ -260,6 +260,11 @@
<entry>security labels on database objects</entry> <entry>security labels on database objects</entry>
</row> </row>
<row>
<entry><link linkend="catalog-pg-sequence"><structname>pg_sequence</structname></link></entry>
<entry>information about sequences</entry>
</row>
<row> <row>
<entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry> <entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
<entry>dependencies on shared objects</entry> <entry>dependencies on shared objects</entry>
@ -1546,7 +1551,8 @@
The catalog <structname>pg_class</structname> catalogs tables and most The catalog <structname>pg_class</structname> catalogs tables and most
everything else that has columns or is otherwise similar to a everything else that has columns or is otherwise similar to a
table. This includes indexes (but see also table. This includes indexes (but see also
<structname>pg_index</structname>), sequences, views, materialized <structname>pg_index</structname>), sequences (but see also
<structname>pg_sequence</structname>), views, materialized
views, composite types, and TOAST tables; see <structfield>relkind</>. views, composite types, and TOAST tables; see <structfield>relkind</>.
Below, when we mean all of these Below, when we mean all of these
kinds of objects we speak of <quote>relations</quote>. Not all kinds of objects we speak of <quote>relations</quote>. Not all
@ -5587,6 +5593,86 @@
</table> </table>
</sect1> </sect1>
<sect1 id="catalog-pg-sequence">
<title><structname>pg_sequence</structname></title>
<indexterm zone="catalog-pg-sequence">
<primary>pg_sequence</primary>
</indexterm>
<para>
The catalog <structname>pg_sequence</structname> contains information about
sequences. Some of the information about sequences, such as the name and
the schema, is in <structname>pg_class</structname>.
</para>
<table>
<title><structname>pg_sequence</> Columns</title>
<tgroup cols="4">
<thead>
<row>
<entry>Name</entry>
<entry>Type</entry>
<entry>References</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><structfield>seqrelid</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
<entry>The OID of the <structname>pg_class</> entry for this sequence</entry>
</row>
<row>
<entry><structfield>seqstart</structfield></entry>
<entry><type>int8</type></entry>
<entry></entry>
<entry>Start value of the sequence</entry>
</row>
<row>
<entry><structfield>seqincrement</structfield></entry>
<entry><type>int8</type></entry>
<entry></entry>
<entry>Increment value of the sequence</entry>
</row>
<row>
<entry><structfield>seqmax</structfield></entry>
<entry><type>int8</type></entry>
<entry></entry>
<entry>Maximum value of the sequence</entry>
</row>
<row>
<entry><structfield>seqmin</structfield></entry>
<entry><type>int8</type></entry>
<entry></entry>
<entry>Minimum value of the sequence</entry>
</row>
<row>
<entry><structfield>seqcache</structfield></entry>
<entry><type>int8</type></entry>
<entry></entry>
<entry>Cache size of the sequence</entry>
</row>
<row>
<entry><structfield>seqcycle</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>Whether the sequence cycles</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1 id="catalog-pg-shdepend"> <sect1 id="catalog-pg-shdepend">
<title><structname>pg_shdepend</structname></title> <title><structname>pg_shdepend</structname></title>

View File

@ -42,6 +42,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_foreign_table.h pg_policy.h pg_replication_origin.h \ pg_foreign_table.h pg_policy.h pg_replication_origin.h \
pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \ pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \
pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \ pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
pg_sequence.h \
toasting.h indexing.h \ toasting.h indexing.h \
) )

View File

@ -66,6 +66,7 @@
#include "commands/proclang.h" #include "commands/proclang.h"
#include "commands/schemacmds.h" #include "commands/schemacmds.h"
#include "commands/seclabel.h" #include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/trigger.h" #include "commands/trigger.h"
#include "commands/typecmds.h" #include "commands/typecmds.h"
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
@ -1114,6 +1115,11 @@ doDeletion(const ObjectAddress *object, int flags)
else else
heap_drop_with_catalog(object->objectId); heap_drop_with_catalog(object->objectId);
} }
/* for a sequence, in addition to dropping the heap, also
* delete pg_sequence tuple */
if (relKind == RELKIND_SEQUENCE)
DeleteSequenceTuple(object->objectId);
break; break;
} }

View File

@ -1535,15 +1535,16 @@ CREATE VIEW sequences AS
CAST(64 AS cardinal_number) AS numeric_precision, CAST(64 AS cardinal_number) AS numeric_precision,
CAST(2 AS cardinal_number) AS numeric_precision_radix, CAST(2 AS cardinal_number) AS numeric_precision_radix,
CAST(0 AS cardinal_number) AS numeric_scale, CAST(0 AS cardinal_number) AS numeric_scale,
CAST(p.start_value AS character_data) AS start_value, CAST(s.seqstart AS character_data) AS start_value,
CAST(p.minimum_value AS character_data) AS minimum_value, CAST(s.seqmin AS character_data) AS minimum_value,
CAST(p.maximum_value AS character_data) AS maximum_value, CAST(s.seqmax AS character_data) AS maximum_value,
CAST(p.increment AS character_data) AS increment, CAST(s.seqincrement AS character_data) AS increment,
CAST(CASE WHEN p.cycle_option THEN 'YES' ELSE 'NO' END AS yes_or_no) AS cycle_option CAST(CASE WHEN s.seqcycle THEN 'YES' ELSE 'NO' END AS yes_or_no) AS cycle_option
FROM pg_namespace nc, pg_class c, LATERAL pg_sequence_parameters(c.oid) p FROM pg_namespace nc, pg_class c, pg_sequence s
WHERE c.relnamespace = nc.oid WHERE c.relnamespace = nc.oid
AND c.relkind = 'S' AND c.relkind = 'S'
AND (NOT pg_is_other_temp_schema(nc.oid)) AND (NOT pg_is_other_temp_schema(nc.oid))
AND c.oid = s.seqrelid
AND (pg_has_role(c.relowner, 'USAGE') AND (pg_has_role(c.relowner, 'USAGE')
OR has_sequence_privilege(c.oid, 'SELECT, UPDATE, USAGE') ); OR has_sequence_privilege(c.oid, 'SELECT, UPDATE, USAGE') );

View File

@ -169,15 +169,15 @@ CREATE OR REPLACE VIEW pg_sequences AS
N.nspname AS schemaname, N.nspname AS schemaname,
C.relname AS sequencename, C.relname AS sequencename,
pg_get_userbyid(C.relowner) AS sequenceowner, pg_get_userbyid(C.relowner) AS sequenceowner,
p.start_value AS start_value, S.seqstart AS start_value,
p.minimum_value AS min_value, S.seqmin AS min_value,
p.maximum_value AS max_value, S.seqmax AS max_value,
p.increment AS increment_by, S.seqincrement AS increment_by,
p.cycle_option AS cycle, S.seqcycle AS cycle,
p.cache_size AS cache_size, S.seqcache AS cache_size,
pg_sequence_last_value(C.oid) AS last_value pg_sequence_last_value(C.oid) AS last_value
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace), FROM pg_sequence S JOIN pg_class C ON (C.oid = S.seqrelid)
LATERAL pg_sequence_parameters(C.oid) p LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE NOT pg_is_other_temp_schema(N.oid) WHERE NOT pg_is_other_temp_schema(N.oid)
AND relkind = 'S'; AND relkind = 'S';

View File

@ -22,8 +22,10 @@
#include "access/xloginsert.h" #include "access/xloginsert.h"
#include "access/xlogutils.h" #include "access/xlogutils.h"
#include "catalog/dependency.h" #include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/objectaccess.h" #include "catalog/objectaccess.h"
#include "catalog/pg_sequence.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/sequence.h" #include "commands/sequence.h"
@ -74,7 +76,7 @@ typedef struct SeqTableData
int64 cached; /* last value already cached for nextval */ int64 cached; /* last value already cached for nextval */
/* if last != cached, we have not used up all the cached values */ /* if last != cached, we have not used up all the cached values */
int64 increment; /* copy of sequence's increment field */ int64 increment; /* copy of sequence's increment field */
/* note that increment is zero until we first do read_seq_tuple() */ /* note that increment is zero until we first do nextval_internal() */
} SeqTableData; } SeqTableData;
typedef SeqTableData *SeqTable; typedef SeqTableData *SeqTable;
@ -92,10 +94,11 @@ static int64 nextval_internal(Oid relid);
static Relation open_share_lock(SeqTable seq); static Relation open_share_lock(SeqTable seq);
static void create_seq_hashtable(void); static void create_seq_hashtable(void);
static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel); static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel, static Form_pg_sequence_data read_seq_tuple(Relation rel,
Buffer *buf, HeapTuple seqtuple); Buffer *buf, HeapTuple seqdatatuple);
static void init_params(ParseState *pstate, List *options, bool isInit, static void init_params(ParseState *pstate, List *options, bool isInit,
Form_pg_sequence new, List **owned_by); Form_pg_sequence seqform,
Form_pg_sequence_data seqdataform, List **owned_by);
static void do_setval(Oid relid, int64 next, bool iscalled); 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);
@ -107,7 +110,8 @@ static void process_owned_by(Relation seqrel, List *owned_by);
ObjectAddress ObjectAddress
DefineSequence(ParseState *pstate, CreateSeqStmt *seq) DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
{ {
FormData_pg_sequence new; FormData_pg_sequence seqform;
FormData_pg_sequence_data seqdataform;
List *owned_by; List *owned_by;
CreateStmt *stmt = makeNode(CreateStmt); CreateStmt *stmt = makeNode(CreateStmt);
Oid seqoid; Oid seqoid;
@ -117,8 +121,9 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
TupleDesc tupDesc; TupleDesc tupDesc;
Datum value[SEQ_COL_LASTCOL]; Datum value[SEQ_COL_LASTCOL];
bool null[SEQ_COL_LASTCOL]; bool null[SEQ_COL_LASTCOL];
Datum pgs_values[Natts_pg_sequence];
bool pgs_nulls[Natts_pg_sequence];
int i; int i;
NameData name;
/* Unlogged sequences are not implemented -- not clear if useful. */ /* Unlogged sequences are not implemented -- not clear if useful. */
if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED) if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@ -145,7 +150,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
} }
/* Check and set all option values */ /* Check and set all option values */
init_params(pstate, seq->options, true, &new, &owned_by); init_params(pstate, seq->options, true, &seqform, &seqdataform, &owned_by);
/* /*
* Create relation (and fill value[] and null[] for the tuple) * Create relation (and fill value[] and null[] for the tuple)
@ -171,52 +176,16 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
switch (i) switch (i)
{ {
case SEQ_COL_NAME:
coldef->typeName = makeTypeNameFromOid(NAMEOID, -1);
coldef->colname = "sequence_name";
namestrcpy(&name, seq->sequence->relname);
value[i - 1] = NameGetDatum(&name);
break;
case SEQ_COL_LASTVAL: case SEQ_COL_LASTVAL:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1); coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
coldef->colname = "last_value"; coldef->colname = "last_value";
value[i - 1] = Int64GetDatumFast(new.last_value); value[i - 1] = Int64GetDatumFast(seqdataform.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";
value[i - 1] = Int64GetDatumFast(new.increment_by);
break;
case SEQ_COL_MAXVALUE:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
coldef->colname = "max_value";
value[i - 1] = Int64GetDatumFast(new.max_value);
break;
case SEQ_COL_MINVALUE:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
coldef->colname = "min_value";
value[i - 1] = Int64GetDatumFast(new.min_value);
break;
case SEQ_COL_CACHE:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
coldef->colname = "cache_value";
value[i - 1] = Int64GetDatumFast(new.cache_value);
break; break;
case SEQ_COL_LOG: case SEQ_COL_LOG:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1); coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
coldef->colname = "log_cnt"; coldef->colname = "log_cnt";
value[i - 1] = Int64GetDatum((int64) 0); value[i - 1] = Int64GetDatum((int64) 0);
break; break;
case SEQ_COL_CYCLE:
coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
coldef->colname = "is_cycled";
value[i - 1] = BoolGetDatum(new.is_cycled);
break;
case SEQ_COL_CALLED: case SEQ_COL_CALLED:
coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
coldef->colname = "is_called"; coldef->colname = "is_called";
@ -251,6 +220,27 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
heap_close(rel, NoLock); heap_close(rel, NoLock);
/* fill in pg_sequence */
rel = heap_open(SequenceRelationId, RowExclusiveLock);
tupDesc = RelationGetDescr(rel);
memset(pgs_nulls, 0, sizeof(pgs_nulls));
pgs_values[Anum_pg_sequence_seqrelid - 1] = ObjectIdGetDatum(seqoid);
pgs_values[Anum_pg_sequence_seqstart - 1] = Int64GetDatumFast(seqform.seqstart);
pgs_values[Anum_pg_sequence_seqincrement - 1] = Int64GetDatumFast(seqform.seqincrement);
pgs_values[Anum_pg_sequence_seqmax - 1] = Int64GetDatumFast(seqform.seqmax);
pgs_values[Anum_pg_sequence_seqmin - 1] = Int64GetDatumFast(seqform.seqmin);
pgs_values[Anum_pg_sequence_seqcache - 1] = Int64GetDatumFast(seqform.seqcache);
pgs_values[Anum_pg_sequence_seqcycle - 1] = BoolGetDatum(seqform.seqcycle);
tuple = heap_form_tuple(tupDesc, pgs_values, pgs_nulls);
simple_heap_insert(rel, tuple);
CatalogUpdateIndexes(rel, tuple);
heap_freetuple(tuple);
heap_close(rel, RowExclusiveLock);
return address; return address;
} }
@ -271,10 +261,13 @@ ResetSequence(Oid seq_relid)
{ {
Relation seq_rel; Relation seq_rel;
SeqTable elm; SeqTable elm;
Form_pg_sequence seq; Form_pg_sequence_data seq;
Buffer buf; Buffer buf;
HeapTupleData seqtuple; HeapTupleData seqdatatuple;
HeapTuple tuple; HeapTuple tuple;
HeapTuple pgstuple;
Form_pg_sequence pgsform;
int64 startv;
/* /*
* Read the old sequence. This does a bit more work than really * Read the old sequence. This does a bit more work than really
@ -282,12 +275,19 @@ ResetSequence(Oid seq_relid)
* indeed a sequence. * indeed a sequence.
*/ */
init_sequence(seq_relid, &elm, &seq_rel); init_sequence(seq_relid, &elm, &seq_rel);
(void) read_seq_tuple(elm, seq_rel, &buf, &seqtuple); (void) read_seq_tuple(seq_rel, &buf, &seqdatatuple);
pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(seq_relid));
if (!HeapTupleIsValid(pgstuple))
elog(ERROR, "cache lookup failed for sequence %u", seq_relid);
pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
startv = pgsform->seqstart;
ReleaseSysCache(pgstuple);
/* /*
* Copy the existing sequence tuple. * Copy the existing sequence tuple.
*/ */
tuple = heap_copytuple(&seqtuple); tuple = heap_copytuple(&seqdatatuple);
/* Now we're done with the old page */ /* Now we're done with the old page */
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
@ -296,8 +296,8 @@ ResetSequence(Oid seq_relid)
* Modify the copied tuple to execute the restart (compare the RESTART * Modify the copied tuple to execute the restart (compare the RESTART
* action in AlterSequence) * action in AlterSequence)
*/ */
seq = (Form_pg_sequence) GETSTRUCT(tuple); seq = (Form_pg_sequence_data) GETSTRUCT(tuple);
seq->last_value = seq->start_value; seq->last_value = startv;
seq->is_called = false; seq->is_called = false;
seq->log_cnt = 0; seq->log_cnt = 0;
@ -410,11 +410,14 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
SeqTable elm; SeqTable elm;
Relation seqrel; Relation seqrel;
Buffer buf; Buffer buf;
HeapTupleData seqtuple; HeapTupleData seqdatatuple;
Form_pg_sequence seq; Form_pg_sequence seqform;
FormData_pg_sequence new; Form_pg_sequence_data seqdata;
FormData_pg_sequence_data newseqdata;
List *owned_by; List *owned_by;
ObjectAddress address; ObjectAddress address;
Relation rel;
HeapTuple tuple;
/* Open and lock sequence. */ /* Open and lock sequence. */
relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok); relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
@ -434,13 +437,22 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
stmt->sequence->relname); stmt->sequence->relname);
/* lock page' buffer and read tuple into new sequence structure */ /* lock page' buffer and read tuple into new sequence structure */
seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple); seqdata = read_seq_tuple(seqrel, &buf, &seqdatatuple);
/* Copy old values of options into workspace */ /* Copy old values of options into workspace */
memcpy(&new, seq, sizeof(FormData_pg_sequence)); memcpy(&newseqdata, seqdata, sizeof(FormData_pg_sequence_data));
rel = heap_open(SequenceRelationId, RowExclusiveLock);
tuple = SearchSysCacheCopy1(SEQRELID,
ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for sequence %u",
relid);
seqform = (Form_pg_sequence) GETSTRUCT(tuple);
/* Check and set new values */ /* Check and set new values */
init_params(pstate, stmt->options, false, &new, &owned_by); init_params(pstate, stmt->options, false, seqform, &newseqdata, &owned_by);
/* Clear local cache so that we don't think we have cached numbers */ /* Clear local cache so that we don't think we have cached numbers */
/* Note that we do not change the currval() state */ /* Note that we do not change the currval() state */
@ -453,7 +465,7 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
/* Now okay to update the on-disk tuple */ /* Now okay to update the on-disk tuple */
START_CRIT_SECTION(); START_CRIT_SECTION();
memcpy(seq, &new, sizeof(FormData_pg_sequence)); memcpy(seqdata, &newseqdata, sizeof(FormData_pg_sequence_data));
MarkBufferDirty(buf); MarkBufferDirty(buf);
@ -470,7 +482,7 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
xlrec.node = seqrel->rd_node; xlrec.node = seqrel->rd_node;
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec)); XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len); XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);
recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG); recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
@ -491,9 +503,30 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
relation_close(seqrel, NoLock); relation_close(seqrel, NoLock);
simple_heap_update(rel, &tuple->t_self, tuple);
CatalogUpdateIndexes(rel, tuple);
heap_close(rel, RowExclusiveLock);
return address; return address;
} }
void
DeleteSequenceTuple(Oid relid)
{
Relation rel;
HeapTuple tuple;
rel = heap_open(SequenceRelationId, RowExclusiveLock);
tuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for sequence %u", relid);
simple_heap_delete(rel, &tuple->t_self);
ReleaseSysCache(tuple);
heap_close(rel, RowExclusiveLock);
}
/* /*
* Note: nextval with a text argument is no longer exported as a pg_proc * Note: nextval with a text argument is no longer exported as a pg_proc
@ -537,8 +570,10 @@ nextval_internal(Oid relid)
Relation seqrel; Relation seqrel;
Buffer buf; Buffer buf;
Page page; Page page;
HeapTupleData seqtuple; HeapTuple pgstuple;
Form_pg_sequence seq; Form_pg_sequence pgsform;
HeapTupleData seqdatatuple;
Form_pg_sequence_data seq;
int64 incby, int64 incby,
maxv, maxv,
minv, minv,
@ -549,6 +584,7 @@ nextval_internal(Oid relid)
int64 result, int64 result,
next, next,
rescnt = 0; rescnt = 0;
bool cycle;
bool logit = false; bool logit = false;
/* open and AccessShareLock sequence */ /* open and AccessShareLock sequence */
@ -582,15 +618,24 @@ nextval_internal(Oid relid)
return elm->last; return elm->last;
} }
pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(pgstuple))
elog(ERROR, "cache lookup failed for sequence %u", relid);
pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
incby = pgsform->seqincrement;
maxv = pgsform->seqmax;
minv = pgsform->seqmin;
cache = pgsform->seqcache;
cycle = pgsform->seqcycle;
ReleaseSysCache(pgstuple);
/* lock page' buffer and read tuple */ /* lock page' buffer and read tuple */
seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple); seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
page = BufferGetPage(buf); page = BufferGetPage(buf);
elm->increment = incby;
last = next = result = seq->last_value; last = next = result = seq->last_value;
incby = seq->increment_by; fetch = cache;
maxv = seq->max_value;
minv = seq->min_value;
fetch = cache = seq->cache_value;
log = seq->log_cnt; log = seq->log_cnt;
if (!seq->is_called) if (!seq->is_called)
@ -641,7 +686,7 @@ nextval_internal(Oid relid)
{ {
if (rescnt > 0) if (rescnt > 0)
break; /* stop fetching */ break; /* stop fetching */
if (!seq->is_cycled) if (!cycle)
{ {
char buf[100]; char buf[100];
@ -664,7 +709,7 @@ nextval_internal(Oid relid)
{ {
if (rescnt > 0) if (rescnt > 0)
break; /* stop fetching */ break; /* stop fetching */
if (!seq->is_cycled) if (!cycle)
{ {
char buf[100]; char buf[100];
@ -747,7 +792,7 @@ nextval_internal(Oid relid)
xlrec.node = seqrel->rd_node; xlrec.node = seqrel->rd_node;
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec)); XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len); XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);
recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG); recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
@ -853,8 +898,12 @@ do_setval(Oid relid, int64 next, bool iscalled)
SeqTable elm; SeqTable elm;
Relation seqrel; Relation seqrel;
Buffer buf; Buffer buf;
HeapTupleData seqtuple; HeapTupleData seqdatatuple;
Form_pg_sequence seq; Form_pg_sequence_data seq;
HeapTuple pgstuple;
Form_pg_sequence pgsform;
int64 maxv,
minv;
/* open and AccessShareLock sequence */ /* open and AccessShareLock sequence */
init_sequence(relid, &elm, &seqrel); init_sequence(relid, &elm, &seqrel);
@ -865,6 +914,14 @@ do_setval(Oid relid, int64 next, bool iscalled)
errmsg("permission denied for sequence %s", errmsg("permission denied for sequence %s",
RelationGetRelationName(seqrel)))); RelationGetRelationName(seqrel))));
pgstuple = SearchSysCache1(SEQRELID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(pgstuple))
elog(ERROR, "cache lookup failed for sequence %u", relid);
pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
maxv = pgsform->seqmax;
minv = pgsform->seqmin;
ReleaseSysCache(pgstuple);
/* read-only transactions may only modify temp sequences */ /* read-only transactions may only modify temp sequences */
if (!seqrel->rd_islocaltemp) if (!seqrel->rd_islocaltemp)
PreventCommandIfReadOnly("setval()"); PreventCommandIfReadOnly("setval()");
@ -877,17 +934,17 @@ do_setval(Oid relid, int64 next, bool iscalled)
PreventCommandIfParallelMode("setval()"); PreventCommandIfParallelMode("setval()");
/* lock page' buffer and read tuple */ /* lock page' buffer and read tuple */
seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple); seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
if ((next < seq->min_value) || (next > seq->max_value)) if ((next < minv) || (next > maxv))
{ {
char bufv[100], char bufv[100],
bufm[100], bufm[100],
bufx[100]; bufx[100];
snprintf(bufv, sizeof(bufv), INT64_FORMAT, next); snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value); snprintf(bufm, sizeof(bufm), INT64_FORMAT, minv);
snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value); snprintf(bufx, sizeof(bufx), INT64_FORMAT, maxv);
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)", errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
@ -930,7 +987,7 @@ do_setval(Oid relid, int64 next, bool iscalled)
xlrec.node = seqrel->rd_node; xlrec.node = seqrel->rd_node;
XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec)); XLogRegisterData((char *) &xlrec, sizeof(xl_seq_rec));
XLogRegisterData((char *) seqtuple.t_data, seqtuple.t_len); XLogRegisterData((char *) seqdatatuple.t_data, seqdatatuple.t_len);
recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG); recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG);
@ -1064,7 +1121,7 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
elm->filenode = InvalidOid; elm->filenode = InvalidOid;
elm->lxid = InvalidLocalTransactionId; elm->lxid = InvalidLocalTransactionId;
elm->last_valid = false; elm->last_valid = false;
elm->last = elm->cached = elm->increment = 0; elm->last = elm->cached = 0;
} }
/* /*
@ -1099,18 +1156,18 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
* Given an opened sequence relation, lock the page buffer and find the tuple * Given an opened sequence relation, lock the page buffer and find the tuple
* *
* *buf receives the reference to the pinned-and-ex-locked buffer * *buf receives the reference to the pinned-and-ex-locked buffer
* *seqtuple receives the reference to the sequence tuple proper * *seqdatatuple receives the reference to the sequence tuple proper
* (this arg should point to a local variable of type HeapTupleData) * (this arg should point to a local variable of type HeapTupleData)
* *
* Function's return value points to the data payload of the tuple * Function's return value points to the data payload of the tuple
*/ */
static Form_pg_sequence static Form_pg_sequence_data
read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple) read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple)
{ {
Page page; Page page;
ItemId lp; ItemId lp;
sequence_magic *sm; sequence_magic *sm;
Form_pg_sequence seq; Form_pg_sequence_data seq;
*buf = ReadBuffer(rel, 0); *buf = ReadBuffer(rel, 0);
LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE); LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
@ -1125,9 +1182,9 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
lp = PageGetItemId(page, FirstOffsetNumber); lp = PageGetItemId(page, FirstOffsetNumber);
Assert(ItemIdIsNormal(lp)); Assert(ItemIdIsNormal(lp));
/* Note we currently only bother to set these two fields of *seqtuple */ /* Note we currently only bother to set these two fields of *seqdatatuple */
seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp); seqdatatuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
seqtuple->t_len = ItemIdGetLength(lp); seqdatatuple->t_len = ItemIdGetLength(lp);
/* /*
* Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@ -1137,19 +1194,16 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
* bit update, ie, don't bother to WAL-log it, since we can certainly do * bit update, ie, don't bother to WAL-log it, since we can certainly do
* this again if the update gets lost. * this again if the update gets lost.
*/ */
Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI)); Assert(!(seqdatatuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId) if (HeapTupleHeaderGetRawXmax(seqdatatuple->t_data) != InvalidTransactionId)
{ {
HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId); HeapTupleHeaderSetXmax(seqdatatuple->t_data, InvalidTransactionId);
seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED; seqdatatuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID; seqdatatuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
MarkBufferDirtyHint(*buf, true); MarkBufferDirtyHint(*buf, true);
} }
seq = (Form_pg_sequence) GETSTRUCT(seqtuple); seq = (Form_pg_sequence_data) GETSTRUCT(seqdatatuple);
/* this is a handy place to update our copy of the increment */
elm->increment = seq->increment_by;
return seq; return seq;
} }
@ -1164,7 +1218,8 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
*/ */
static void static void
init_params(ParseState *pstate, List *options, bool isInit, init_params(ParseState *pstate, List *options, bool isInit,
Form_pg_sequence new, List **owned_by) Form_pg_sequence seqform,
Form_pg_sequence_data seqdataform, List **owned_by)
{ {
DefElem *start_value = NULL; DefElem *start_value = NULL;
DefElem *restart_value = NULL; DefElem *restart_value = NULL;
@ -1263,69 +1318,69 @@ init_params(ParseState *pstate, List *options, bool isInit,
* would affect future nextval allocations. * would affect future nextval allocations.
*/ */
if (isInit) if (isInit)
new->log_cnt = 0; seqdataform->log_cnt = 0;
/* INCREMENT BY */ /* INCREMENT BY */
if (increment_by != NULL) if (increment_by != NULL)
{ {
new->increment_by = defGetInt64(increment_by); seqform->seqincrement = defGetInt64(increment_by);
if (new->increment_by == 0) if (seqform->seqincrement == 0)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("INCREMENT must not be zero"))); errmsg("INCREMENT must not be zero")));
new->log_cnt = 0; seqdataform->log_cnt = 0;
} }
else if (isInit) else if (isInit)
new->increment_by = 1; seqform->seqincrement = 1;
/* CYCLE */ /* CYCLE */
if (is_cycled != NULL) if (is_cycled != NULL)
{ {
new->is_cycled = intVal(is_cycled->arg); seqform->seqcycle = intVal(is_cycled->arg);
Assert(BoolIsValid(new->is_cycled)); Assert(BoolIsValid(seqform->seqcycle));
new->log_cnt = 0; seqdataform->log_cnt = 0;
} }
else if (isInit) else if (isInit)
new->is_cycled = false; seqform->seqcycle = false;
/* MAXVALUE (null arg means NO MAXVALUE) */ /* MAXVALUE (null arg means NO MAXVALUE) */
if (max_value != NULL && max_value->arg) if (max_value != NULL && max_value->arg)
{ {
new->max_value = defGetInt64(max_value); seqform->seqmax = defGetInt64(max_value);
new->log_cnt = 0; seqdataform->log_cnt = 0;
} }
else if (isInit || max_value != NULL) else if (isInit || max_value != NULL)
{ {
if (new->increment_by > 0) if (seqform->seqincrement > 0)
new->max_value = SEQ_MAXVALUE; /* ascending seq */ seqform->seqmax = SEQ_MAXVALUE; /* ascending seq */
else else
new->max_value = -1; /* descending seq */ seqform->seqmax = -1; /* descending seq */
new->log_cnt = 0; seqdataform->log_cnt = 0;
} }
/* MINVALUE (null arg means NO MINVALUE) */ /* MINVALUE (null arg means NO MINVALUE) */
if (min_value != NULL && min_value->arg) if (min_value != NULL && min_value->arg)
{ {
new->min_value = defGetInt64(min_value); seqform->seqmin = defGetInt64(min_value);
new->log_cnt = 0; seqdataform->log_cnt = 0;
} }
else if (isInit || min_value != NULL) else if (isInit || min_value != NULL)
{ {
if (new->increment_by > 0) if (seqform->seqincrement > 0)
new->min_value = 1; /* ascending seq */ seqform->seqmin = 1; /* ascending seq */
else else
new->min_value = SEQ_MINVALUE; /* descending seq */ seqform->seqmin = SEQ_MINVALUE; /* descending seq */
new->log_cnt = 0; seqdataform->log_cnt = 0;
} }
/* crosscheck min/max */ /* crosscheck min/max */
if (new->min_value >= new->max_value) if (seqform->seqmin >= seqform->seqmax)
{ {
char bufm[100], char bufm[100],
bufx[100]; bufx[100];
snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin);
snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value); snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax);
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("MINVALUE (%s) must be less than MAXVALUE (%s)", errmsg("MINVALUE (%s) must be less than MAXVALUE (%s)",
@ -1334,35 +1389,35 @@ init_params(ParseState *pstate, List *options, bool isInit,
/* START WITH */ /* START WITH */
if (start_value != NULL) if (start_value != NULL)
new->start_value = defGetInt64(start_value); seqform->seqstart = defGetInt64(start_value);
else if (isInit) else if (isInit)
{ {
if (new->increment_by > 0) if (seqform->seqincrement > 0)
new->start_value = new->min_value; /* ascending seq */ seqform->seqstart = seqform->seqmin; /* ascending seq */
else else
new->start_value = new->max_value; /* descending seq */ seqform->seqstart = seqform->seqmax; /* descending seq */
} }
/* crosscheck START */ /* crosscheck START */
if (new->start_value < new->min_value) if (seqform->seqstart < seqform->seqmin)
{ {
char bufs[100], char bufs[100],
bufm[100]; bufm[100];
snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value); snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqform->seqstart);
snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin);
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("START value (%s) cannot be less than MINVALUE (%s)", errmsg("START value (%s) cannot be less than MINVALUE (%s)",
bufs, bufm))); bufs, bufm)));
} }
if (new->start_value > new->max_value) if (seqform->seqstart > seqform->seqmax)
{ {
char bufs[100], char bufs[100],
bufm[100]; bufm[100];
snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value); snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqform->seqstart);
snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value); snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmax);
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("START value (%s) cannot be greater than MAXVALUE (%s)", errmsg("START value (%s) cannot be greater than MAXVALUE (%s)",
@ -1373,38 +1428,38 @@ init_params(ParseState *pstate, List *options, bool isInit,
if (restart_value != NULL) if (restart_value != NULL)
{ {
if (restart_value->arg != NULL) if (restart_value->arg != NULL)
new->last_value = defGetInt64(restart_value); seqdataform->last_value = defGetInt64(restart_value);
else else
new->last_value = new->start_value; seqdataform->last_value = seqform->seqstart;
new->is_called = false; seqdataform->is_called = false;
new->log_cnt = 0; seqdataform->log_cnt = 0;
} }
else if (isInit) else if (isInit)
{ {
new->last_value = new->start_value; seqdataform->last_value = seqform->seqstart;
new->is_called = false; seqdataform->is_called = false;
} }
/* crosscheck RESTART (or current value, if changing MIN/MAX) */ /* crosscheck RESTART (or current value, if changing MIN/MAX) */
if (new->last_value < new->min_value) if (seqdataform->last_value < seqform->seqmin)
{ {
char bufs[100], char bufs[100],
bufm[100]; bufm[100];
snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value); snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqdataform->last_value);
snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value); snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin);
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)", errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
bufs, bufm))); bufs, bufm)));
} }
if (new->last_value > new->max_value) if (seqdataform->last_value > seqform->seqmax)
{ {
char bufs[100], char bufs[100],
bufm[100]; bufm[100];
snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value); snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqdataform->last_value);
snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value); snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmax);
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)", errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
@ -1414,21 +1469,21 @@ init_params(ParseState *pstate, List *options, bool isInit,
/* CACHE */ /* CACHE */
if (cache_value != NULL) if (cache_value != NULL)
{ {
new->cache_value = defGetInt64(cache_value); seqform->seqcache = defGetInt64(cache_value);
if (new->cache_value <= 0) if (seqform->seqcache <= 0)
{ {
char buf[100]; char buf[100];
snprintf(buf, sizeof(buf), INT64_FORMAT, new->cache_value); snprintf(buf, sizeof(buf), INT64_FORMAT, seqform->seqcache);
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("CACHE (%s) must be greater than zero", errmsg("CACHE (%s) must be greater than zero",
buf))); buf)));
} }
new->log_cnt = 0; seqdataform->log_cnt = 0;
} }
else if (isInit) else if (isInit)
new->cache_value = 1; seqform->seqcache = 1;
} }
/* /*
@ -1528,7 +1583,7 @@ process_owned_by(Relation seqrel, List *owned_by)
/* /*
* Return sequence parameters, for use by information schema * Return sequence parameters (formerly for use by information schema)
*/ */
Datum Datum
pg_sequence_parameters(PG_FUNCTION_ARGS) pg_sequence_parameters(PG_FUNCTION_ARGS)
@ -1537,20 +1592,14 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
TupleDesc tupdesc; TupleDesc tupdesc;
Datum values[6]; Datum values[6];
bool isnull[6]; bool isnull[6];
SeqTable elm; HeapTuple pgstuple;
Relation seqrel; Form_pg_sequence pgsform;
Buffer buf;
HeapTupleData seqtuple;
Form_pg_sequence seq;
/* open and AccessShareLock sequence */
init_sequence(relid, &elm, &seqrel);
if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK) if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied for sequence %s", errmsg("permission denied for sequence %s",
RelationGetRelationName(seqrel)))); get_rel_name(relid))));
tupdesc = CreateTemplateTupleDesc(6, false); tupdesc = CreateTemplateTupleDesc(6, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value", TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@ -1570,17 +1619,19 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
memset(isnull, 0, sizeof(isnull)); memset(isnull, 0, sizeof(isnull));
seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple); pgstuple = SearchSysCache1(SEQRELID, relid);
if (!HeapTupleIsValid(pgstuple))
elog(ERROR, "cache lookup failed for sequence %u", relid);
pgsform = (Form_pg_sequence) GETSTRUCT(pgstuple);
values[0] = Int64GetDatum(seq->start_value); values[0] = Int64GetDatum(pgsform->seqstart);
values[1] = Int64GetDatum(seq->min_value); values[1] = Int64GetDatum(pgsform->seqmin);
values[2] = Int64GetDatum(seq->max_value); values[2] = Int64GetDatum(pgsform->seqmax);
values[3] = Int64GetDatum(seq->increment_by); values[3] = Int64GetDatum(pgsform->seqincrement);
values[4] = BoolGetDatum(seq->is_cycled); values[4] = BoolGetDatum(pgsform->seqcycle);
values[5] = Int64GetDatum(seq->cache_value); values[5] = Int64GetDatum(pgsform->seqcache);
UnlockReleaseBuffer(buf); ReleaseSysCache(pgstuple);
relation_close(seqrel, NoLock);
return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull)); return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
} }
@ -1598,7 +1649,7 @@ pg_sequence_last_value(PG_FUNCTION_ARGS)
Relation seqrel; Relation seqrel;
Buffer buf; Buffer buf;
HeapTupleData seqtuple; HeapTupleData seqtuple;
Form_pg_sequence seq; Form_pg_sequence_data seq;
bool is_called; bool is_called;
int64 result; int64 result;
@ -1611,7 +1662,7 @@ pg_sequence_last_value(PG_FUNCTION_ARGS)
errmsg("permission denied for sequence %s", errmsg("permission denied for sequence %s",
RelationGetRelationName(seqrel)))); RelationGetRelationName(seqrel))));
seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple); seq = read_seq_tuple(seqrel, &buf, &seqtuple);
is_called = seq->is_called; is_called = seq->is_called;
result = seq->last_value; result = seq->last_value;

View File

@ -53,6 +53,7 @@
#include "catalog/pg_range.h" #include "catalog/pg_range.h"
#include "catalog/pg_rewrite.h" #include "catalog/pg_rewrite.h"
#include "catalog/pg_seclabel.h" #include "catalog/pg_seclabel.h"
#include "catalog/pg_sequence.h"
#include "catalog/pg_shdepend.h" #include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h" #include "catalog/pg_shdescription.h"
#include "catalog/pg_shseclabel.h" #include "catalog/pg_shseclabel.h"
@ -668,6 +669,17 @@ static const struct cachedesc cacheinfo[] = {
}, },
8 8
}, },
{SequenceRelationId, /* SEQRELID */
SequenceRelidIndexId,
1,
{
Anum_pg_sequence_seqrelid,
0,
0,
0
},
32
},
{StatisticRelationId, /* STATRELATTINH */ {StatisticRelationId, /* STATRELATTINH */
StatisticRelidAttnumInhIndexId, StatisticRelidAttnumInhIndexId,
3, 3,

View File

@ -15351,7 +15351,27 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE); snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE);
snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE); snprintf(bufx, sizeof(bufx), INT64_FORMAT, SEQ_MAXVALUE);
if (fout->remoteVersion >= 80400) if (fout->remoteVersion >= 100000)
{
appendPQExpBuffer(query,
"SELECT relname, "
"seqstart, seqincrement, "
"CASE WHEN seqincrement > 0 AND seqmax = %s THEN NULL "
" WHEN seqincrement < 0 AND seqmax = -1 THEN NULL "
" ELSE seqmax "
"END AS seqmax, "
"CASE WHEN seqincrement > 0 AND seqmin = 1 THEN NULL "
" WHEN seqincrement < 0 AND seqmin = %s THEN NULL "
" ELSE seqmin "
"END AS seqmin, "
"seqcache, seqcycle "
"FROM pg_class c "
"JOIN pg_sequence s ON (s.seqrelid = c.oid) "
"WHERE relname = ",
bufx, bufm);
appendStringLiteralAH(query, tbinfo->dobj.name, fout);
}
else if (fout->remoteVersion >= 80400)
{ {
appendPQExpBuffer(query, appendPQExpBuffer(query,
"SELECT sequence_name, " "SELECT sequence_name, "

View File

@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 201612121 #define CATALOG_VERSION_NO 201612201
#endif #endif

View File

@ -209,6 +209,9 @@ DECLARE_UNIQUE_INDEX(pg_rewrite_oid_index, 2692, on pg_rewrite using btree(oid o
DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops)); DECLARE_UNIQUE_INDEX(pg_rewrite_rel_rulename_index, 2693, on pg_rewrite using btree(ev_class oid_ops, rulename name_ops));
#define RewriteRelRulenameIndexId 2693 #define RewriteRelRulenameIndexId 2693
DECLARE_UNIQUE_INDEX(pg_sequence_seqrelid_index, 5002, on pg_sequence using btree(seqrelid oid_ops));
#define SequenceRelidIndexId 5002
DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops)); DECLARE_INDEX(pg_shdepend_depender_index, 1232, on pg_shdepend using btree(dbid oid_ops, classid oid_ops, objid oid_ops, objsubid int4_ops));
#define SharedDependDependerIndexId 1232 #define SharedDependDependerIndexId 1232
DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops)); DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));

View File

@ -0,0 +1,30 @@
#ifndef PG_SEQUENCE_H
#define PG_SEQUENCE_H
#include "catalog/genbki.h"
#define SequenceRelationId 2224
CATALOG(pg_sequence,2224) BKI_WITHOUT_OIDS
{
Oid seqrelid;
int64 seqstart;
int64 seqincrement;
int64 seqmax;
int64 seqmin;
int64 seqcache;
bool seqcycle;
} FormData_pg_sequence;
typedef FormData_pg_sequence *Form_pg_sequence;
#define Natts_pg_sequence 7
#define Anum_pg_sequence_seqrelid 1
#define Anum_pg_sequence_seqstart 2
#define Anum_pg_sequence_seqincrement 3
#define Anum_pg_sequence_seqmax 4
#define Anum_pg_sequence_seqmin 5
#define Anum_pg_sequence_seqcache 6
#define Anum_pg_sequence_seqcycle 7
#endif /* PG_SEQUENCE_H */

View File

@ -22,38 +22,24 @@
#include "storage/relfilenode.h" #include "storage/relfilenode.h"
typedef struct FormData_pg_sequence typedef struct FormData_pg_sequence_data
{ {
NameData sequence_name;
int64 last_value; int64 last_value;
int64 start_value;
int64 increment_by;
int64 max_value;
int64 min_value;
int64 cache_value;
int64 log_cnt; int64 log_cnt;
bool is_cycled;
bool is_called; bool is_called;
} FormData_pg_sequence; } FormData_pg_sequence_data;
typedef FormData_pg_sequence *Form_pg_sequence; typedef FormData_pg_sequence_data *Form_pg_sequence_data;
/* /*
* Columns of a sequence relation * Columns of a sequence relation
*/ */
#define SEQ_COL_NAME 1 #define SEQ_COL_LASTVAL 1
#define SEQ_COL_LASTVAL 2 #define SEQ_COL_LOG 2
#define SEQ_COL_STARTVAL 3 #define SEQ_COL_CALLED 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_FIRSTCOL SEQ_COL_LASTVAL
#define SEQ_COL_LASTCOL SEQ_COL_CALLED #define SEQ_COL_LASTCOL SEQ_COL_CALLED
/* XLOG stuff */ /* XLOG stuff */
@ -77,6 +63,7 @@ extern Datum pg_sequence_last_value(PG_FUNCTION_ARGS);
extern ObjectAddress DefineSequence(ParseState *pstate, CreateSeqStmt *stmt); extern ObjectAddress DefineSequence(ParseState *pstate, CreateSeqStmt *stmt);
extern ObjectAddress AlterSequence(ParseState *pstate, AlterSeqStmt *stmt); extern ObjectAddress AlterSequence(ParseState *pstate, AlterSeqStmt *stmt);
extern void DeleteSequenceTuple(Oid relid);
extern void ResetSequence(Oid seq_relid); extern void ResetSequence(Oid seq_relid);
extern void ResetSequenceCaches(void); extern void ResetSequenceCaches(void);

View File

@ -81,6 +81,7 @@ enum SysCacheIdentifier
REPLORIGIDENT, REPLORIGIDENT,
REPLORIGNAME, REPLORIGNAME,
RULERELNAME, RULERELNAME,
SEQRELID,
STATRELATTINH, STATRELATTINH,
TABLESPACEOID, TABLESPACEOID,
TRFOID, TRFOID,

View File

@ -1623,16 +1623,16 @@ UNION ALL
pg_sequences| SELECT n.nspname AS schemaname, pg_sequences| SELECT n.nspname AS schemaname,
c.relname AS sequencename, c.relname AS sequencename,
pg_get_userbyid(c.relowner) AS sequenceowner, pg_get_userbyid(c.relowner) AS sequenceowner,
p.start_value, s.seqstart AS start_value,
p.minimum_value AS min_value, s.seqmin AS min_value,
p.maximum_value AS max_value, s.seqmax AS max_value,
p.increment AS increment_by, s.seqincrement AS increment_by,
p.cycle_option AS cycle, s.seqcycle AS cycle,
p.cache_size, s.seqcache AS cache_size,
pg_sequence_last_value((c.oid)::regclass) AS last_value pg_sequence_last_value((c.oid)::regclass) AS last_value
FROM (pg_class c FROM ((pg_sequence s
LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))), JOIN pg_class c ON ((c.oid = s.seqrelid)))
LATERAL pg_sequence_parameters(c.oid) p(start_value, minimum_value, maximum_value, increment, cycle_option, cache_size) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char")); WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char"));
pg_settings| SELECT a.name, pg_settings| SELECT a.name,
a.setting, a.setting,

View File

@ -128,6 +128,7 @@ pg_range|t
pg_replication_origin|t pg_replication_origin|t
pg_rewrite|t pg_rewrite|t
pg_seclabel|t pg_seclabel|t
pg_sequence|t
pg_shdepend|t pg_shdepend|t
pg_shdescription|t pg_shdescription|t
pg_shseclabel|t pg_shseclabel|t

View File

@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
CREATE SEQUENCE foo_seq; CREATE SEQUENCE foo_seq;
ALTER TABLE foo_seq RENAME TO foo_seq_new; ALTER TABLE foo_seq RENAME TO foo_seq_new;
SELECT * FROM foo_seq_new; 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 last_value | log_cnt | is_called
---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- ------------+---------+-----------
foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f 1 | 0 | f
(1 row) (1 row)
SELECT nextval('foo_seq_new'); SELECT nextval('foo_seq_new');
@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
(1 row) (1 row)
SELECT * FROM foo_seq_new; 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 last_value | log_cnt | is_called
---------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+----------- ------------+---------+-----------
foo_seq | 2 | 1 | 1 | 9223372036854775807 | 1 | 1 | 31 | f | t 2 | 31 | t
(1 row) (1 row)
DROP SEQUENCE foo_seq_new; DROP SEQUENCE foo_seq_new;
@ -536,3 +536,24 @@ SELECT * FROM information_schema.sequences WHERE sequence_name IN
DROP USER regress_seq_user; DROP USER regress_seq_user;
DROP SEQUENCE seq; DROP SEQUENCE seq;
-- cache tests
CREATE SEQUENCE test_seq1 CACHE 10;
SELECT nextval('test_seq1');
nextval
---------
1
(1 row)
SELECT nextval('test_seq1');
nextval
---------
2
(1 row)
SELECT nextval('test_seq1');
nextval
---------
3
(1 row)
DROP SEQUENCE test_seq1;

View File

@ -86,55 +86,48 @@ SELECT table_name, column_name, is_updatable
FROM information_schema.columns FROM information_schema.columns
WHERE table_name LIKE E'r_\\_view%' WHERE table_name LIKE E'r_\\_view%'
ORDER BY table_name, ordinal_position; ORDER BY table_name, ordinal_position;
table_name | column_name | is_updatable table_name | column_name | is_updatable
------------+---------------+-------------- ------------+-------------+--------------
ro_view1 | a | NO ro_view1 | a | NO
ro_view1 | b | NO ro_view1 | b | NO
ro_view10 | a | NO ro_view10 | a | NO
ro_view11 | a | NO ro_view11 | a | NO
ro_view11 | b | NO ro_view11 | b | NO
ro_view12 | a | NO ro_view12 | a | NO
ro_view13 | a | NO ro_view13 | a | NO
ro_view13 | b | NO ro_view13 | b | NO
ro_view17 | a | NO ro_view17 | a | NO
ro_view17 | b | NO ro_view17 | b | NO
ro_view18 | a | NO ro_view18 | a | NO
ro_view19 | sequence_name | NO ro_view19 | last_value | NO
ro_view19 | last_value | NO ro_view19 | log_cnt | NO
ro_view19 | start_value | NO ro_view19 | is_called | NO
ro_view19 | increment_by | NO ro_view2 | a | NO
ro_view19 | max_value | NO ro_view2 | b | NO
ro_view19 | min_value | NO ro_view20 | a | NO
ro_view19 | cache_value | NO ro_view20 | b | NO
ro_view19 | log_cnt | NO ro_view20 | g | NO
ro_view19 | is_cycled | NO ro_view3 | ?column? | NO
ro_view19 | is_called | NO ro_view4 | count | NO
ro_view2 | a | NO ro_view5 | a | NO
ro_view2 | b | NO ro_view5 | rank | NO
ro_view20 | a | NO ro_view6 | a | NO
ro_view20 | b | NO ro_view6 | b | NO
ro_view20 | g | NO ro_view7 | a | NO
ro_view3 | ?column? | NO ro_view7 | b | NO
ro_view4 | count | NO ro_view8 | a | NO
ro_view5 | a | NO ro_view8 | b | NO
ro_view5 | rank | NO ro_view9 | a | NO
ro_view6 | a | NO ro_view9 | b | NO
ro_view6 | b | NO rw_view14 | ctid | NO
ro_view7 | a | NO rw_view14 | a | YES
ro_view7 | b | NO rw_view14 | b | YES
ro_view8 | a | NO rw_view15 | a | YES
ro_view8 | b | NO rw_view15 | upper | NO
ro_view9 | a | NO rw_view16 | a | YES
ro_view9 | b | NO rw_view16 | b | YES
rw_view14 | ctid | NO rw_view16 | aa | YES
rw_view14 | a | YES (39 rows)
rw_view14 | b | YES
rw_view15 | a | YES
rw_view15 | upper | NO
rw_view16 | a | YES
rw_view16 | b | YES
rw_view16 | aa | YES
(46 rows)
-- Read-only views -- Read-only views
DELETE FROM ro_view1; DELETE FROM ro_view1;
@ -327,7 +320,7 @@ DELETE FROM ro_view18;
ERROR: cannot delete from view "ro_view18" ERROR: cannot delete from view "ro_view18"
DETAIL: Views that do not select from a single table or view are not automatically updatable. DETAIL: Views that do not select from a single table or view are not automatically updatable.
HINT: To enable deleting from the view, provide an INSTEAD OF DELETE trigger or an unconditional ON DELETE DO INSTEAD rule. HINT: To enable deleting from the view, provide an INSTEAD OF DELETE trigger or an unconditional ON DELETE DO INSTEAD rule.
UPDATE ro_view19 SET max_value=1000; UPDATE ro_view19 SET last_value=1000;
ERROR: cannot update view "ro_view19" ERROR: cannot update view "ro_view19"
DETAIL: Views that do not select from a single table or view are not automatically updatable. DETAIL: Views that do not select from a single table or view are not automatically updatable.
HINT: To enable updating the view, provide an INSTEAD OF UPDATE trigger or an unconditional ON UPDATE DO INSTEAD rule. HINT: To enable updating the view, provide an INSTEAD OF UPDATE trigger or an unconditional ON UPDATE DO INSTEAD rule.

View File

@ -273,3 +273,11 @@ SELECT * FROM information_schema.sequences WHERE sequence_name IN
DROP USER regress_seq_user; DROP USER regress_seq_user;
DROP SEQUENCE seq; DROP SEQUENCE seq;
-- cache tests
CREATE SEQUENCE test_seq1 CACHE 10;
SELECT nextval('test_seq1');
SELECT nextval('test_seq1');
SELECT nextval('test_seq1');
DROP SEQUENCE test_seq1;

View File

@ -95,7 +95,7 @@ DELETE FROM rw_view16 WHERE a=-3; -- should be OK
-- Read-only views -- Read-only views
INSERT INTO ro_view17 VALUES (3, 'ROW 3'); INSERT INTO ro_view17 VALUES (3, 'ROW 3');
DELETE FROM ro_view18; DELETE FROM ro_view18;
UPDATE ro_view19 SET max_value=1000; UPDATE ro_view19 SET last_value=1000;
UPDATE ro_view20 SET b=upper(b); UPDATE ro_view20 SET b=upper(b);
DROP TABLE base_tbl CASCADE; DROP TABLE base_tbl CASCADE;