pg_sequence catalog
While I was hacking around sequence stuff, I felt the urge to look into
an old peeve: That sequence metadata is not stored in a proper catalog.
Right now in order to find out some metadata about sequences (min, max,
increment, etc.), you need to look into the sequence. That is like
having to query a table in order to find out about its schema.
There are also known issues with the current storage such as that we
can't safely update the sequence name stored in the sequence when we
rename, so we just don't.
This patch introduces a new catalog pg_sequence that stores the sequence
metadata. The sequences themselves now only store the counter and
supporting information.
I don't know if this is a net improvement. Maybe this introduces as
many new issues as it removes. But I figured I'll post this, so that at
least we can discuss it.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
pg_sequence.patchtext/x-patch; name=pg_sequence.patchDownload
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 1ce7610..cbf0d79 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -41,7 +41,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.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_collation.h pg_range.h pg_transform.h \
+ pg_collation.h pg_range.h pg_transform.h pg_sequence.h \
toasting.h indexing.h \
)
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 04d7840..8e1e1ac 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -66,6 +66,7 @@
#include "commands/proclang.h"
#include "commands/schemacmds.h"
#include "commands/seclabel.h"
+#include "commands/sequence.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
#include "nodes/nodeFuncs.h"
@@ -1155,6 +1156,8 @@ doDeletion(const ObjectAddress *object, int flags)
else
heap_drop_with_catalog(object->objectId);
}
+ if (relKind == RELKIND_SEQUENCE)
+ DeleteSequenceTuple(object->objectId);
break;
}
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index 00550eb..182d2d0 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -1535,15 +1535,16 @@ CREATE VIEW sequences AS
CAST(64 AS cardinal_number) AS numeric_precision,
CAST(2 AS cardinal_number) AS numeric_precision_radix,
CAST(0 AS cardinal_number) AS numeric_scale,
- CAST(p.start_value AS character_data) AS start_value,
- CAST(p.minimum_value AS character_data) AS minimum_value,
- CAST(p.maximum_value AS character_data) AS maximum_value,
- CAST(p.increment AS character_data) AS increment,
- CAST(CASE WHEN p.cycle_option 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
+ CAST(s.seqstart AS character_data) AS start_value,
+ CAST(s.seqmin AS character_data) AS minimum_value,
+ CAST(s.seqmax AS character_data) AS maximum_value,
+ CAST(s.seqincrement AS character_data) AS increment,
+ CAST(CASE WHEN s.seqcycle THEN 'YES' ELSE 'NO' END AS yes_or_no) AS cycle_option
+ FROM pg_namespace nc, pg_class c, pg_sequence s
WHERE c.relnamespace = nc.oid
AND c.relkind = 'S'
AND (NOT pg_is_other_temp_schema(nc.oid))
+ AND c.oid = s.seqrelid
AND (pg_has_role(c.relowner, 'USAGE')
OR has_sequence_privilege(c.oid, 'SELECT, UPDATE, USAGE') );
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index c98f981..95bd172 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -22,8 +22,10 @@
#include "access/xloginsert.h"
#include "access/xlogutils.h"
#include "catalog/dependency.h"
+#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
+#include "catalog/pg_sequence.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/sequence.h"
@@ -74,7 +76,7 @@ typedef struct SeqTableData
int64 cached; /* last value already cached for nextval */
/* if last != cached, we have not used up all the cached values */
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;
typedef SeqTableData *SeqTable;
@@ -92,10 +94,11 @@ static int64 nextval_internal(Oid relid);
static Relation open_share_lock(SeqTable seq);
static void create_seq_hashtable(void);
static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
- Buffer *buf, HeapTuple seqtuple);
+static Form_pg_sequence_data read_seq_tuple(Relation rel,
+ Buffer *buf, HeapTuple seqdatatuple);
static void init_params(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 process_owned_by(Relation seqrel, List *owned_by);
@@ -107,7 +110,8 @@ static void process_owned_by(Relation seqrel, List *owned_by);
ObjectAddress
DefineSequence(CreateSeqStmt *seq)
{
- FormData_pg_sequence new;
+ FormData_pg_sequence seqform;
+ FormData_pg_sequence_data seqdataform;
List *owned_by;
CreateStmt *stmt = makeNode(CreateStmt);
Oid seqoid;
@@ -117,8 +121,9 @@ DefineSequence(CreateSeqStmt *seq)
TupleDesc tupDesc;
Datum value[SEQ_COL_LASTCOL];
bool null[SEQ_COL_LASTCOL];
+ Datum pgs_values[Natts_pg_sequence];
+ bool pgs_nulls[Natts_pg_sequence];
int i;
- NameData name;
/* Unlogged sequences are not implemented -- not clear if useful. */
if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -145,7 +150,7 @@ DefineSequence(CreateSeqStmt *seq)
}
/* Check and set all option values */
- init_params(seq->options, true, &new, &owned_by);
+ init_params(seq->options, true, &seqform, &seqdataform, &owned_by);
/*
* Create relation (and fill value[] and null[] for the tuple)
@@ -171,52 +176,16 @@ DefineSequence(CreateSeqStmt *seq)
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:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
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";
- 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);
+ value[i - 1] = Int64GetDatumFast(seqdataform.last_value);
break;
case SEQ_COL_LOG:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
coldef->colname = "log_cnt";
value[i - 1] = Int64GetDatum((int64) 0);
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:
coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
coldef->colname = "is_called";
@@ -251,6 +220,27 @@ DefineSequence(CreateSeqStmt *seq)
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;
}
@@ -271,10 +261,13 @@ ResetSequence(Oid seq_relid)
{
Relation seq_rel;
SeqTable elm;
- Form_pg_sequence seq;
+ Form_pg_sequence_data seq;
Buffer buf;
- HeapTupleData seqtuple;
+ HeapTupleData seqdatatuple;
HeapTuple tuple;
+ HeapTuple pgstuple;
+ Form_pg_sequence pgsform;
+ int64 startv;
/*
* Read the old sequence. This does a bit more work than really
@@ -282,12 +275,19 @@ ResetSequence(Oid seq_relid)
* indeed a sequence.
*/
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.
*/
- tuple = heap_copytuple(&seqtuple);
+ tuple = heap_copytuple(&seqdatatuple);
/* Now we're done with the old page */
UnlockReleaseBuffer(buf);
@@ -296,8 +296,8 @@ ResetSequence(Oid seq_relid)
* Modify the copied tuple to execute the restart (compare the RESTART
* action in AlterSequence)
*/
- seq = (Form_pg_sequence) GETSTRUCT(tuple);
- seq->last_value = seq->start_value;
+ seq = (Form_pg_sequence_data) GETSTRUCT(tuple);
+ seq->last_value = startv;
seq->is_called = false;
seq->log_cnt = 0;
@@ -410,11 +410,14 @@ AlterSequence(AlterSeqStmt *stmt)
SeqTable elm;
Relation seqrel;
Buffer buf;
- HeapTupleData seqtuple;
- Form_pg_sequence seq;
- FormData_pg_sequence new;
+ HeapTupleData seqdatatuple;
+ Form_pg_sequence seqform;
+ Form_pg_sequence_data seqdata;
+ FormData_pg_sequence_data newseqdata;
List *owned_by;
ObjectAddress address;
+ Relation rel;
+ HeapTuple tuple;
/* Open and lock sequence. */
relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
@@ -434,13 +437,22 @@ AlterSequence(AlterSeqStmt *stmt)
stmt->sequence->relname);
/* 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 */
- 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 */
- init_params(stmt->options, false, &new, &owned_by);
+ init_params(stmt->options, false, seqform, &newseqdata, &owned_by);
/* Clear local cache so that we don't think we have cached numbers */
/* Note that we do not change the currval() state */
@@ -453,7 +465,7 @@ AlterSequence(AlterSeqStmt *stmt)
/* Now okay to update the on-disk tuple */
START_CRIT_SECTION();
- memcpy(seq, &new, sizeof(FormData_pg_sequence));
+ memcpy(seqdata, &newseqdata, sizeof(FormData_pg_sequence_data));
MarkBufferDirty(buf);
@@ -470,7 +482,7 @@ AlterSequence(AlterSeqStmt *stmt)
xlrec.node = seqrel->rd_node;
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);
@@ -491,9 +503,30 @@ AlterSequence(AlterSeqStmt *stmt)
relation_close(seqrel, NoLock);
+ simple_heap_update(rel, &tuple->t_self, tuple);
+ CatalogUpdateIndexes(rel, tuple);
+ heap_close(rel, RowExclusiveLock);
+
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
@@ -537,8 +570,10 @@ nextval_internal(Oid relid)
Relation seqrel;
Buffer buf;
Page page;
- HeapTupleData seqtuple;
- Form_pg_sequence seq;
+ HeapTuple pgstuple;
+ Form_pg_sequence pgsform;
+ HeapTupleData seqdatatuple;
+ Form_pg_sequence_data seq;
int64 incby,
maxv,
minv,
@@ -549,6 +584,7 @@ nextval_internal(Oid relid)
int64 result,
next,
rescnt = 0;
+ bool cycle;
bool logit = false;
/* open and AccessShareLock sequence */
@@ -582,15 +618,24 @@ nextval_internal(Oid relid)
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 */
- seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+ seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
page = BufferGetPage(buf);
+ elm->increment = incby;
last = next = result = seq->last_value;
- incby = seq->increment_by;
- maxv = seq->max_value;
- minv = seq->min_value;
- fetch = cache = seq->cache_value;
+ fetch = cache;
log = seq->log_cnt;
if (!seq->is_called)
@@ -641,7 +686,7 @@ nextval_internal(Oid relid)
{
if (rescnt > 0)
break; /* stop fetching */
- if (!seq->is_cycled)
+ if (!cycle)
{
char buf[100];
@@ -664,7 +709,7 @@ nextval_internal(Oid relid)
{
if (rescnt > 0)
break; /* stop fetching */
- if (!seq->is_cycled)
+ if (!cycle)
{
char buf[100];
@@ -747,7 +792,7 @@ nextval_internal(Oid relid)
xlrec.node = seqrel->rd_node;
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);
@@ -853,8 +898,12 @@ do_setval(Oid relid, int64 next, bool iscalled)
SeqTable elm;
Relation seqrel;
Buffer buf;
- HeapTupleData seqtuple;
- Form_pg_sequence seq;
+ HeapTupleData seqdatatuple;
+ Form_pg_sequence_data seq;
+ HeapTuple pgstuple;
+ Form_pg_sequence pgsform;
+ int64 maxv,
+ minv;
/* open and AccessShareLock sequence */
init_sequence(relid, &elm, &seqrel);
@@ -865,6 +914,14 @@ do_setval(Oid relid, int64 next, bool iscalled)
errmsg("permission denied for sequence %s",
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 */
if (!seqrel->rd_islocaltemp)
PreventCommandIfReadOnly("setval()");
@@ -877,17 +934,17 @@ do_setval(Oid relid, int64 next, bool iscalled)
PreventCommandIfParallelMode("setval()");
/* 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],
bufm[100],
bufx[100];
snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
- snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, minv);
+ snprintf(bufx, sizeof(bufx), INT64_FORMAT, maxv);
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
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;
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);
@@ -1064,7 +1121,7 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
elm->filenode = InvalidOid;
elm->lxid = InvalidLocalTransactionId;
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
*
* *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)
*
* Function's return value points to the data payload of the tuple
*/
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+static Form_pg_sequence_data
+read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple)
{
Page page;
ItemId lp;
sequence_magic *sm;
- Form_pg_sequence seq;
+ Form_pg_sequence_data seq;
*buf = ReadBuffer(rel, 0);
LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
@@ -1125,9 +1182,9 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
lp = PageGetItemId(page, FirstOffsetNumber);
Assert(ItemIdIsNormal(lp));
- /* Note we currently only bother to set these two fields of *seqtuple */
- seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- seqtuple->t_len = ItemIdGetLength(lp);
+ /* Note we currently only bother to set these two fields of *seqdatatuple */
+ seqdatatuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ seqdatatuple->t_len = ItemIdGetLength(lp);
/*
* 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
* this again if the update gets lost.
*/
- Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
- if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+ Assert(!(seqdatatuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+ if (HeapTupleHeaderGetRawXmax(seqdatatuple->t_data) != InvalidTransactionId)
{
- HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
- seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
- seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
+ HeapTupleHeaderSetXmax(seqdatatuple->t_data, InvalidTransactionId);
+ seqdatatuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+ seqdatatuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
MarkBufferDirtyHint(*buf, true);
}
- seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
-
- /* this is a handy place to update our copy of the increment */
- elm->increment = seq->increment_by;
+ seq = (Form_pg_sequence_data) GETSTRUCT(seqdatatuple);
return seq;
}
@@ -1164,7 +1218,8 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
*/
static void
init_params(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 *restart_value = NULL;
@@ -1255,69 +1310,69 @@ init_params(List *options, bool isInit,
* would affect future nextval allocations.
*/
if (isInit)
- new->log_cnt = 0;
+ seqdataform->log_cnt = 0;
/* INCREMENT BY */
if (increment_by != NULL)
{
- new->increment_by = defGetInt64(increment_by);
- if (new->increment_by == 0)
+ seqform->seqincrement = defGetInt64(increment_by);
+ if (seqform->seqincrement == 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("INCREMENT must not be zero")));
- new->log_cnt = 0;
+ seqdataform->log_cnt = 0;
}
else if (isInit)
- new->increment_by = 1;
+ seqform->seqincrement = 1;
/* CYCLE */
if (is_cycled != NULL)
{
- new->is_cycled = intVal(is_cycled->arg);
- Assert(BoolIsValid(new->is_cycled));
- new->log_cnt = 0;
+ seqform->seqcycle = intVal(is_cycled->arg);
+ Assert(BoolIsValid(seqform->seqcycle));
+ seqdataform->log_cnt = 0;
}
else if (isInit)
- new->is_cycled = false;
+ seqform->seqcycle = false;
/* MAXVALUE (null arg means NO MAXVALUE) */
if (max_value != NULL && max_value->arg)
{
- new->max_value = defGetInt64(max_value);
- new->log_cnt = 0;
+ seqform->seqmax = defGetInt64(max_value);
+ seqdataform->log_cnt = 0;
}
else if (isInit || max_value != NULL)
{
- if (new->increment_by > 0)
- new->max_value = SEQ_MAXVALUE; /* ascending seq */
+ if (seqform->seqincrement > 0)
+ seqform->seqmax = SEQ_MAXVALUE; /* ascending seq */
else
- new->max_value = -1; /* descending seq */
- new->log_cnt = 0;
+ seqform->seqmax = -1; /* descending seq */
+ seqdataform->log_cnt = 0;
}
/* MINVALUE (null arg means NO MINVALUE) */
if (min_value != NULL && min_value->arg)
{
- new->min_value = defGetInt64(min_value);
- new->log_cnt = 0;
+ seqform->seqmin = defGetInt64(min_value);
+ seqdataform->log_cnt = 0;
}
else if (isInit || min_value != NULL)
{
- if (new->increment_by > 0)
- new->min_value = 1; /* ascending seq */
+ if (seqform->seqincrement > 0)
+ seqform->seqmin = 1; /* ascending seq */
else
- new->min_value = SEQ_MINVALUE; /* descending seq */
- new->log_cnt = 0;
+ seqform->seqmin = SEQ_MINVALUE; /* descending seq */
+ seqdataform->log_cnt = 0;
}
/* crosscheck min/max */
- if (new->min_value >= new->max_value)
+ if (seqform->seqmin >= seqform->seqmax)
{
char bufm[100],
bufx[100];
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
- snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin);
+ snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("MINVALUE (%s) must be less than MAXVALUE (%s)",
@@ -1326,35 +1381,35 @@ init_params(List *options, bool isInit,
/* START WITH */
if (start_value != NULL)
- new->start_value = defGetInt64(start_value);
+ seqform->seqstart = defGetInt64(start_value);
else if (isInit)
{
- if (new->increment_by > 0)
- new->start_value = new->min_value; /* ascending seq */
+ if (seqform->seqincrement > 0)
+ seqform->seqstart = seqform->seqmin; /* ascending seq */
else
- new->start_value = new->max_value; /* descending seq */
+ seqform->seqstart = seqform->seqmax; /* descending seq */
}
/* crosscheck START */
- if (new->start_value < new->min_value)
+ if (seqform->seqstart < seqform->seqmin)
{
char bufs[100],
bufm[100];
- snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqform->seqstart);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin);
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)
+ if (seqform->seqstart > seqform->seqmax)
{
char bufs[100],
bufm[100];
- snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqform->seqstart);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmax);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("START value (%s) cannot be greater than MAXVALUE (%s)",
@@ -1365,38 +1420,38 @@ init_params(List *options, bool isInit,
if (restart_value != NULL)
{
if (restart_value->arg != NULL)
- new->last_value = defGetInt64(restart_value);
+ seqdataform->last_value = defGetInt64(restart_value);
else
- new->last_value = new->start_value;
- new->is_called = false;
- new->log_cnt = 0;
+ seqdataform->last_value = seqform->seqstart;
+ seqdataform->is_called = false;
+ seqdataform->log_cnt = 0;
}
else if (isInit)
{
- new->last_value = new->start_value;
- new->is_called = false;
+ seqdataform->last_value = seqform->seqstart;
+ seqdataform->is_called = false;
}
/* 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],
bufm[100];
- snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqdataform->last_value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
bufs, bufm)));
}
- if (new->last_value > new->max_value)
+ if (seqdataform->last_value > seqform->seqmax)
{
char bufs[100],
bufm[100];
- snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqdataform->last_value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmax);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
@@ -1406,21 +1461,21 @@ init_params(List *options, bool isInit,
/* CACHE */
if (cache_value != NULL)
{
- new->cache_value = defGetInt64(cache_value);
- if (new->cache_value <= 0)
+ seqform->seqcache = defGetInt64(cache_value);
+ if (seqform->seqcache <= 0)
{
char buf[100];
- snprintf(buf, sizeof(buf), INT64_FORMAT, new->cache_value);
+ snprintf(buf, sizeof(buf), INT64_FORMAT, seqform->seqcache);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("CACHE (%s) must be greater than zero",
buf)));
}
- new->log_cnt = 0;
+ seqdataform->log_cnt = 0;
}
else if (isInit)
- new->cache_value = 1;
+ seqform->seqcache = 1;
}
/*
@@ -1519,7 +1574,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
pg_sequence_parameters(PG_FUNCTION_ARGS)
@@ -1528,20 +1583,14 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
TupleDesc tupdesc;
Datum values[5];
bool isnull[5];
- SeqTable elm;
- Relation seqrel;
- Buffer buf;
- HeapTupleData seqtuple;
- Form_pg_sequence seq;
-
- /* open and AccessShareLock sequence */
- init_sequence(relid, &elm, &seqrel);
+ HeapTuple pgstuple;
+ Form_pg_sequence pgsform;
if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied for sequence %s",
- RelationGetRelationName(seqrel))));
+ get_rel_name(relid))));
tupdesc = CreateTemplateTupleDesc(5, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1559,16 +1608,18 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
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[1] = Int64GetDatum(seq->min_value);
- values[2] = Int64GetDatum(seq->max_value);
- values[3] = Int64GetDatum(seq->increment_by);
- values[4] = BoolGetDatum(seq->is_cycled);
+ values[0] = Int64GetDatum(pgsform->seqstart);
+ values[1] = Int64GetDatum(pgsform->seqmin);
+ values[2] = Int64GetDatum(pgsform->seqmax);
+ values[3] = Int64GetDatum(pgsform->seqincrement);
+ values[4] = BoolGetDatum(pgsform->seqcycle);
- UnlockReleaseBuffer(buf);
- relation_close(seqrel, NoLock);
+ ReleaseSysCache(pgstuple);
return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
}
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 65ffe84..1a6205b 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -52,6 +52,7 @@
#include "catalog/pg_range.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_seclabel.h"
+#include "catalog/pg_sequence.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_shseclabel.h"
@@ -656,6 +657,17 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {SequenceRelationId, /* SEQRELID */
+ SequenceRelidIndexId,
+ 1,
+ {
+ Anum_pg_sequence_seqrelid,
+ 0,
+ 0,
+ 0
+ },
+ 32
+ },
{StatisticRelationId, /* STATRELATTINH */
StatisticRelidAttnumInhIndexId,
3,
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ca5eb3d..3ef880e 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -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));
#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));
#define SharedDependDependerIndexId 1232
DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h
new file mode 100644
index 0000000..2e24329
--- /dev/null
+++ b/src/include/catalog/pg_sequence.h
@@ -0,0 +1,30 @@
+#ifndef PG_SEQUENCE_H
+#define PG_SEQUENCE_H
+
+#include "catalog/genbki.h"
+
+#define SequenceRelationId 4032
+
+CATALOG(pg_sequence,4032) 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 */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 6af60d8..5f08c3b 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -21,38 +21,24 @@
#include "storage/relfilenode.h"
-typedef struct FormData_pg_sequence
+typedef struct FormData_pg_sequence_data
{
- NameData sequence_name;
int64 last_value;
- int64 start_value;
- int64 increment_by;
- int64 max_value;
- int64 min_value;
- int64 cache_value;
int64 log_cnt;
- bool is_cycled;
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
*/
-#define SEQ_COL_NAME 1
-#define SEQ_COL_LASTVAL 2
-#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_LASTVAL 1
+#define SEQ_COL_LOG 2
+#define SEQ_COL_CALLED 3
-#define SEQ_COL_FIRSTCOL SEQ_COL_NAME
+#define SEQ_COL_FIRSTCOL SEQ_COL_LASTVAL
#define SEQ_COL_LASTCOL SEQ_COL_CALLED
/* XLOG stuff */
@@ -75,6 +61,7 @@ extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
extern ObjectAddress DefineSequence(CreateSeqStmt *stmt);
extern ObjectAddress AlterSequence(AlterSeqStmt *stmt);
+extern void DeleteSequenceTuple(Oid relid);
extern void ResetSequence(Oid seq_relid);
extern void ResetSequenceCaches(void);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 256615b..9c52be4 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -80,6 +80,7 @@ enum SysCacheIdentifier
REPLORIGIDENT,
REPLORIGNAME,
RULERELNAME,
+ SEQRELID,
STATRELATTINH,
TABLESPACEOID,
TRFOID,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 1c087a3..2e1b1b3 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -125,6 +125,7 @@ pg_range|t
pg_replication_origin|t
pg_rewrite|t
pg_seclabel|t
+pg_sequence|t
pg_shdepend|t
pg_shdescription|t
pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 4ffbe92..11541b6 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
CREATE SEQUENCE foo_seq;
ALTER TABLE foo_seq RENAME TO 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
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f
+ last_value | log_cnt | is_called
+------------+---------+-----------
+ 1 | 0 | f
(1 row)
SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
(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 | 31 | f | t
+ last_value | log_cnt | is_called
+------------+---------+-----------
+ 2 | 31 | t
(1 row)
DROP SEQUENCE foo_seq_new;
@@ -517,3 +517,24 @@ SELECT * FROM information_schema.sequences WHERE sequence_name IN
DROP USER regress_seq_user;
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;
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index f60991e..db3570f 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -86,55 +86,48 @@ SELECT table_name, column_name, is_updatable
FROM information_schema.columns
WHERE table_name LIKE E'r_\\_view%'
ORDER BY table_name, ordinal_position;
- table_name | column_name | is_updatable
-------------+---------------+--------------
- ro_view1 | a | NO
- ro_view1 | b | NO
- ro_view10 | a | NO
- ro_view11 | a | NO
- ro_view11 | b | NO
- ro_view12 | a | NO
- ro_view13 | a | NO
- ro_view13 | b | NO
- ro_view17 | a | NO
- ro_view17 | b | NO
- ro_view18 | a | NO
- ro_view19 | sequence_name | NO
- ro_view19 | last_value | NO
- ro_view19 | start_value | NO
- ro_view19 | increment_by | NO
- ro_view19 | max_value | NO
- ro_view19 | min_value | NO
- ro_view19 | cache_value | NO
- ro_view19 | log_cnt | NO
- ro_view19 | is_cycled | NO
- ro_view19 | is_called | NO
- ro_view2 | a | NO
- ro_view2 | b | NO
- ro_view20 | a | NO
- ro_view20 | b | NO
- ro_view20 | g | NO
- ro_view3 | ?column? | NO
- ro_view4 | count | NO
- ro_view5 | a | NO
- ro_view5 | rank | NO
- ro_view6 | a | NO
- ro_view6 | b | NO
- ro_view7 | a | NO
- ro_view7 | b | NO
- ro_view8 | a | NO
- ro_view8 | b | NO
- ro_view9 | a | NO
- ro_view9 | b | NO
- rw_view14 | ctid | NO
- rw_view14 | a | YES
- 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)
+ table_name | column_name | is_updatable
+------------+-------------+--------------
+ ro_view1 | a | NO
+ ro_view1 | b | NO
+ ro_view10 | a | NO
+ ro_view11 | a | NO
+ ro_view11 | b | NO
+ ro_view12 | a | NO
+ ro_view13 | a | NO
+ ro_view13 | b | NO
+ ro_view17 | a | NO
+ ro_view17 | b | NO
+ ro_view18 | a | NO
+ ro_view19 | last_value | NO
+ ro_view19 | log_cnt | NO
+ ro_view19 | is_called | NO
+ ro_view2 | a | NO
+ ro_view2 | b | NO
+ ro_view20 | a | NO
+ ro_view20 | b | NO
+ ro_view20 | g | NO
+ ro_view3 | ?column? | NO
+ ro_view4 | count | NO
+ ro_view5 | a | NO
+ ro_view5 | rank | NO
+ ro_view6 | a | NO
+ ro_view6 | b | NO
+ ro_view7 | a | NO
+ ro_view7 | b | NO
+ ro_view8 | a | NO
+ ro_view8 | b | NO
+ ro_view9 | a | NO
+ ro_view9 | b | NO
+ rw_view14 | ctid | NO
+ rw_view14 | a | YES
+ rw_view14 | b | YES
+ rw_view15 | a | YES
+ rw_view15 | upper | NO
+ rw_view16 | a | YES
+ rw_view16 | b | YES
+ rw_view16 | aa | YES
+(39 rows)
-- Read-only views
DELETE FROM ro_view1;
@@ -327,7 +320,7 @@ DELETE FROM 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.
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"
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.
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 98a2e7d..4dcb806 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -262,3 +262,11 @@ CREATE SEQUENCE seq3;
DROP USER regress_seq_user;
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;
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index 03c3f9d..f008479 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -95,7 +95,7 @@ CREATE VIEW ro_view20 AS SELECT a, b, generate_series(1, a) g FROM base_tbl; --
-- Read-only views
INSERT INTO ro_view17 VALUES (3, 'ROW 3');
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);
DROP TABLE base_tbl CASCADE;
On 31 August 2016 at 21:17, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:
While I was hacking around sequence stuff, I felt the urge to look into
an old peeve: That sequence metadata is not stored in a proper catalog.
Right now in order to find out some metadata about sequences (min, max,
increment, etc.), you need to look into the sequence. That is like
having to query a table in order to find out about its schema.There are also known issues with the current storage such as that we
can't safely update the sequence name stored in the sequence when we
rename, so we just don't.
... and don't have a comment warning the poor confused reader about
that, either. As I discovered when doing my first pass at sequence
decoding.
I don't know if this is a net improvement. Maybe this introduces as
many new issues as it removes. But I figured I'll post this, so that at
least we can discuss it.
This will change behaviour subtly. Probably not in ways we care much
about, but I'd rather that be an informed decision than an implicit
"oops we didn't think about that" one.
Things stored in the Form_pg_sequence are affected immediately,
non-transactionally, by ALTER SEQUENCE. If you:
BEGIN;
ALTER SEQUENCE myseq INTERVAL 10;
ROLLBACK;
then the *next call to nextval* after the ALTER will step by 10. Well,
roughly, there's some slush there due to caching etc. Rolling back has
no effect. Yet other effects of ALTER SEQUENCE, notably RENAME, are
transactional and are subject to normal locking, visibility and
rollback rules.
Even more fun, ALTER SEQUENCE ... RESTART is immediate and
non-transactional .... but TRUNCATE ... RESTART IDENTITY *is*
transactional and takes effect only at commit. ALTER SEQUENCE writes a
new Form_pg_sequence with the new value to the existing relfilenode.
TRUNCATE instead updates pg_class for the sequence with a new
relfilenode and writes its changes to the new relfilenode. So even two
operations that seem the same are very different.
If understand the proposed change correctly, this change will make
previously non-transactional ALTER SEQUENCE operations transactional
and subject to normal rules, since the relevant information is now in
a proper catalog.
Personally I think that's a big improvement. The current situation is
warts upon warts.
Prior proposals to move sequences away from a
one-relation-and-one-filenode-per-sequence model have fallen down in
early planning stages. This seems like a simpler, more sensible step
to take, and it won't block later cleanups of how we store sequences
if we decide to go there.
It'll also make it slightly easier to handle logical decoding of
sequence advances, since it'll be possible to send *just* the new
sequence value. Right now we can't tell if interval, etc, also got
changed, and have to send most of the Form_pg_sequence on the wire
for every sequence advance, which is a little sucky. This change won't
solve the problem I outlined in the other thread though, that
sequences are transactional sometimes and not other times.
So +1 for the idea from me. It'll just need relnotes warning of the
subtle behaviour change.
--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Craig Ringer <craig@2ndquadrant.com> writes:
On 31 August 2016 at 21:17, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:I don't know if this is a net improvement. Maybe this introduces as
many new issues as it removes. But I figured I'll post this, so that at
least we can discuss it.
This will change behaviour subtly.
Uh, not as subtly as all that, because "select * from sequence" will
now return a different set of columns, which will flat out break a
lot of clients that use that method to get sequence properties.
In previous discussions of this idea, we had speculated about turning
sequences into, effectively, views, so that we could still return all
the same columns --- only now some of them would be coming from a
catalog rather than the non-transactional per-sequence storage.
I'm not sure how feasible that is, but it's worth thinking about.
Personally, my big beef with the current approach to sequences is that
we eat a whole relation (including a whole relfilenode) per sequence.
I wish that we could reduce a sequence to just a single row in a
catalog, including the nontransactional state. Not sure how feasible
that is either, but accomplishing it would move the benefits of making
a change out of the "debatable whether it's worth it" category, IMO.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
I wrote:
Personally, my big beef with the current approach to sequences is that
we eat a whole relation (including a whole relfilenode) per sequence.
I wish that we could reduce a sequence to just a single row in a
catalog, including the nontransactional state. Not sure how feasible
that is either, but accomplishing it would move the benefits of making
a change out of the "debatable whether it's worth it" category, IMO.
BTW, another thing to keep in mind here is the ideas that have been
kicked around in the past about alternative sequence implementations
managed through a "sequence AM API". I dunno whether now is the time
to start creating that API abstraction, but let's at least consider
it if we're whacking the catalog representation around.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 31 August 2016 at 22:01, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Personally, my big beef with the current approach to sequences is that
we eat a whole relation (including a whole relfilenode) per sequence.
I wish that we could reduce a sequence to just a single row in a
catalog, including the nontransactional state.
I'd be happy to see incremental improvement in this space as Peter has
suggested, though I certainly see the value of something like seqam
too.
It sounds like you're thinking of something like a normal(ish) heap
tuple where we just overwrite some fields in-place without fiddling
xmin/xmax and making a new row version. Right? Like we currently
overwrite the lone Form_pg_sequence on the 1-page sequence relations.
I initially thought that TRUNCATE ... RESTART IDENTITY would be
somewhat of a problem with this. We effectively have a temporary
"timeline" fork in the sequence value where it's provisionally
restarted and we start using values from the restarted sequence within
the xact that restarted it. But actually, it'd fit pretty well.
TRUNCATE ... RESTART IDENTITY would write a new row version with a new
xmin, and set xmax on the old sequence row. nextval(...) within the
truncating xact would update the new row's non-transactional fields
when it allocated new sequence chunks. On commit, everyone starts
using the new row due to normal transactional visibility rules. On
rollback everyone ignores it like they would any other dead tuple from
an aborted act and uses the old tuple's nontransactional fields. It
Just Works(TM).
nextval(...) takes AccessShareLock on a sequence relation. TRUNCATE
... RESTART IDENTITY takes AccessExclusiveLock. So we can never have
nextval(...) advancing the "old" timeline in other xacts at the same
time as we consume values on the restarted sequence inside the xact
that did the restarting. We still need the new "timeline" though,
because we have to retain the old value for rollback.
It feels intuitively pretty gross to effectively dirty-read and write
a few fields of a tuple. But that's what we do all the time with
xmin/xmax etc, it's not really that different.
It'd certainly make sequence decoding easier too. A LOT easier.
--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 31/08/16 16:10, Tom Lane wrote:
I wrote:
Personally, my big beef with the current approach to sequences is that
we eat a whole relation (including a whole relfilenode) per sequence.
I wish that we could reduce a sequence to just a single row in a
catalog, including the nontransactional state. Not sure how feasible
that is either, but accomplishing it would move the benefits of making
a change out of the "debatable whether it's worth it" category, IMO.BTW, another thing to keep in mind here is the ideas that have been
kicked around in the past about alternative sequence implementations
managed through a "sequence AM API". I dunno whether now is the time
to start creating that API abstraction, but let's at least consider
it if we're whacking the catalog representation around.
FWIW if I was going to continue with the sequence AM API, the next patch
would have included split of sequence metadata and sequence state into
separate catalogs, so from that point this actually seems like an
improvement (I didn't look at the code though).
As a side note, I don't plan to resurrect the seqam patch at least until
we have reasonable built-in logical replication functionality.
--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Craig Ringer <craig@2ndquadrant.com> writes:
On 31 August 2016 at 22:01, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Personally, my big beef with the current approach to sequences is that
we eat a whole relation (including a whole relfilenode) per sequence.
I wish that we could reduce a sequence to just a single row in a
catalog, including the nontransactional state.
It sounds like you're thinking of something like a normal(ish) heap
tuple where we just overwrite some fields in-place without fiddling
xmin/xmax and making a new row version. Right? Like we currently
overwrite the lone Form_pg_sequence on the 1-page sequence relations.
That would be what to do with the nontransactional state. If I recall
previous discussions correctly, there's a stumbling block if you want
to treat ALTER SEQUENCE changes as transactional --- but maybe that
doesn't make sense anyway. If we did want to try that, maybe we need
two auxiliary catalogs, one for the transactionally-updatable sequence
fields and one for the nontransactional fields.
It feels intuitively pretty gross to effectively dirty-read and write
a few fields of a tuple. But that's what we do all the time with
xmin/xmax etc, it's not really that different.
True. I think two rows would work around that, but maybe we don't
have to.
Another issue is what is the low-level interlock between nextvals
in different processes. Right now it's the buffer lock on the
sequence's page. With a scheme like this, if we just kept doing
that, we'd have a single lock covering probably O(100) different
sequences which might lead to contention problems. We could probably
improve on that with some thought.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2016-08-31 11:23:27 -0400, Tom Lane wrote:
Another issue is what is the low-level interlock between nextvals
in different processes. Right now it's the buffer lock on the
sequence's page. With a scheme like this, if we just kept doing
that, we'd have a single lock covering probably O(100) different
sequences which might lead to contention problems. We could probably
improve on that with some thought.
I was thinking of forcing the rows to be spread to exactly one page per
sequence...
Andres
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Andres Freund wrote:
On 2016-08-31 11:23:27 -0400, Tom Lane wrote:
Another issue is what is the low-level interlock between nextvals
in different processes. Right now it's the buffer lock on the
sequence's page. With a scheme like this, if we just kept doing
that, we'd have a single lock covering probably O(100) different
sequences which might lead to contention problems. We could probably
improve on that with some thought.I was thinking of forcing the rows to be spread to exactly one page per
sequence...
I was thinking that nextval could grab a shared buffer lock and release
immediately, just to ensure no one holds exclusive buffer lock
concurrently (which would be used for things like dropping one seq tuple
from the page, when a sequence is dropped); then control access to each
sequence tuple using LockDatabaseObject. This is a HW lock, heavier
than a buffer's LWLock, but it seems better than wasting a full 8kb for
each sequence.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2016-08-31 12:56:45 -0300, Alvaro Herrera wrote:
I was thinking that nextval could grab a shared buffer lock and release
immediately, just to ensure no one holds exclusive buffer lock
concurrently (which would be used for things like dropping one seq tuple
from the page, when a sequence is dropped); then control access to each
sequence tuple using LockDatabaseObject. This is a HW lock, heavier
than a buffer's LWLock, but it seems better than wasting a full 8kb for
each sequence.
That's going to go be a *lot* slower, I don't think that's ok. I've a
hard time worrying about the space waste here; especially considering
where we're coming from.
Andres
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Andres Freund <andres@anarazel.de> writes:
On 2016-08-31 12:56:45 -0300, Alvaro Herrera wrote:
I was thinking that nextval could grab a shared buffer lock and release
immediately, just to ensure no one holds exclusive buffer lock
concurrently (which would be used for things like dropping one seq tuple
from the page, when a sequence is dropped); then control access to each
sequence tuple using LockDatabaseObject. This is a HW lock, heavier
than a buffer's LWLock, but it seems better than wasting a full 8kb for
each sequence.
That's going to go be a *lot* slower, I don't think that's ok. I've a
hard time worrying about the space waste here; especially considering
where we're coming from.
Improving on the space wastage is exactly the point IMO. If it's still
going to be 8k per sequence on disk (*and* in shared buffers, remember),
I'm not sure it's worth all the work to change things at all.
We're already dealing with taking a heavyweight lock for each sequence
(the relation AccessShareLock). I wonder whether it'd be possible to
repurpose that effort somehow.
Another idea would be to have nominally per-sequence LWLocks (or
spinlocks?) controlling nextval's nontransactional accesses to the catalog
rows, but to map those down to some fixed number of locks in a way similar
to the current fallback implementation for spinlocks, which maps them onto
a fixed number of semaphores. You'd trade off shared memory against
contention while choosing the underlying number of locks.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
On 2016-08-31 12:53:30 -0400, Tom Lane wrote:
Improving on the space wastage is exactly the point IMO. If it's still
going to be 8k per sequence on disk (*and* in shared buffers, remember),
I'm not sure it's worth all the work to change things at all.
A separate file is a heck lot more heavyweight than another 8 kb in an
existing file.
Another idea would be to have nominally per-sequence LWLocks (or
spinlocks?) controlling nextval's nontransactional accesses to the catalog
rows, but to map those down to some fixed number of locks in a way similar
to the current fallback implementation for spinlocks, which maps them onto
a fixed number of semaphores. You'd trade off shared memory against
contention while choosing the underlying number of locks.
If we could rely on spinlocks to actually be spinlocks, we could just
put the spinlock besides the actual state data... Not entirely pretty,
but probably pretty simple.
- Andres
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Andres Freund wrote:
Hi,
On 2016-08-31 12:53:30 -0400, Tom Lane wrote:
Improving on the space wastage is exactly the point IMO. If it's still
going to be 8k per sequence on disk (*and* in shared buffers, remember),
I'm not sure it's worth all the work to change things at all.A separate file is a heck lot more heavyweight than another 8 kb in an
existing file.
Yes, sure, we're still improving even if we stick to one-seq-per-bufpage,
but while we're at it, we could as well find a way to make it as best as
we can. And allowing multiple seqs per page seems a much better
situation, so let's try to get there.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2016-08-31 14:25:43 -0300, Alvaro Herrera wrote:
Andres Freund wrote:
Hi,
On 2016-08-31 12:53:30 -0400, Tom Lane wrote:
Improving on the space wastage is exactly the point IMO. If it's still
going to be 8k per sequence on disk (*and* in shared buffers, remember),
I'm not sure it's worth all the work to change things at all.A separate file is a heck lot more heavyweight than another 8 kb in an
existing file.Yes, sure, we're still improving even if we stick to one-seq-per-bufpage,
but while we're at it, we could as well find a way to make it as best as
we can. And allowing multiple seqs per page seems a much better
situation, so let's try to get there.
It's not really that simple. Having independent sequence rows closer
together will have its own performance cost. Suddenly independent
sequences will compete for the same page level lock, NUMA systems will
have to transfer the page/cacheline around even if it's independent
sequences being accessed in different backends, we'll have to take care
about cacheline padding...
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
andres@anarazel.de (Andres Freund) writes:
On 2016-08-31 14:25:43 -0300, Alvaro Herrera wrote:
Yes, sure, we're still improving even if we stick to one-seq-per-bufpage,
but while we're at it, we could as well find a way to make it as best as
we can. And allowing multiple seqs per page seems a much better
situation, so let's try to get there.
It's not really that simple. Having independent sequence rows closer
together will have its own performance cost.
You are ignoring the performance costs associated with eating 100x more
shared buffer space than necessary.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2016-08-31 13:59:48 -0400, Tom Lane wrote:
andres@anarazel.de (Andres Freund) writes:
On 2016-08-31 14:25:43 -0300, Alvaro Herrera wrote:
Yes, sure, we're still improving even if we stick to one-seq-per-bufpage,
but while we're at it, we could as well find a way to make it as best as
we can. And allowing multiple seqs per page seems a much better
situation, so let's try to get there.It's not really that simple. Having independent sequence rows closer
together will have its own performance cost.You are ignoring the performance costs associated with eating 100x more
shared buffer space than necessary.
I doubt that's measurable in any real-world scenario. You seldomly have
hundreds of thousands of sequences that you all select from at a high
rate.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Andres Freund <andres@anarazel.de> writes:
On 2016-08-31 13:59:48 -0400, Tom Lane wrote:
You are ignoring the performance costs associated with eating 100x more
shared buffer space than necessary.
I doubt that's measurable in any real-world scenario. You seldomly have
hundreds of thousands of sequences that you all select from at a high
rate.
If there are only a few sequences in the database, cross-sequence
contention is not going to be a big issue anyway. I think most of
the point of making this change at all is to make things work better
when you do have a boatload of sequences.
Also, we could probably afford to add enough dummy padding to the sequence
tuples so that they're each exactly 64 bytes, thereby having only one
or two active counters per cache line.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2016-08-31 14:23:41 -0400, Tom Lane wrote:
Andres Freund <andres@anarazel.de> writes:
On 2016-08-31 13:59:48 -0400, Tom Lane wrote:
You are ignoring the performance costs associated with eating 100x more
shared buffer space than necessary.I doubt that's measurable in any real-world scenario. You seldomly have
hundreds of thousands of sequences that you all select from at a high
rate.If there are only a few sequences in the database, cross-sequence
contention is not going to be a big issue anyway.
Isn't that *precisely* when it's going to matter? If you have 5 active
tables & sequences where the latter previously used independent locks,
and they'd now be contending on a single lock. If you have hundreds of
thousands of active sequences, I doubt individual page locks would
become a point of contention.
Andres
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Wed, Aug 31, 2016 at 3:01 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Uh, not as subtly as all that, because "select * from sequence" will
now return a different set of columns, which will flat out break a
lot of clients that use that method to get sequence properties.
So? Clients expect changes like this between major releases surely.
Subtle changes that cause silent breakage for end-users seems scarier
than unsubtle breakage that tool authors can fix.
On Wed, Aug 31, 2016 at 7:30 PM, Andres Freund <andres@anarazel.de> wrote:
Isn't that *precisely* when it's going to matter? If you have 5 active
tables & sequences where the latter previously used independent locks,
and they'd now be contending on a single lock. If you have hundreds of
thousands of active sequences, I doubt individual page locks would
become a point of contention.
I think even two sequences could be a point of contention if you, for
example, are using COPY to load data into two otherwise completely
independent tables in two separate processes.
But that just means the row needs to be padded out to a cache line,
no? Or was the concern about things like trying to pin the same page,
parse the same page header, follow nearby line pointers...? I'm not
sure how effective all that caching is today but it doesn't seem
impossible to imagine getting that all optimized away.
--
greg
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 4 September 2016 at 23:17, Greg Stark <stark@mit.edu> wrote:
On Wed, Aug 31, 2016 at 3:01 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Uh, not as subtly as all that, because "select * from sequence" will
now return a different set of columns, which will flat out break a
lot of clients that use that method to get sequence properties.So? Clients expect changes like this between major releases surely.
Subtle changes that cause silent breakage for end-users seems scarier
than unsubtle breakage that tool authors can fix.
Agreed; some change in the behaviour if SELECT * FROM sequence is
effectively part of this proposal. I was going to make the same
comment myself.
I think we should balance the problems caused by not having a sequence
catalog against the problems caused by changing the currently somewhat
broken situation.
I vote in favour of applying Peter's idea/patch, though if that is not
acceptable I don't think its worth spending further time on for any of
us, so if thats the case lets Return with Feedback and move on.
--
Simon Riggs http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Simon Riggs <simon@2ndquadrant.com> writes:
On 4 September 2016 at 23:17, Greg Stark <stark@mit.edu> wrote:
So? Clients expect changes like this between major releases surely.
Subtle changes that cause silent breakage for end-users seems scarier
than unsubtle breakage that tool authors can fix.
Agreed; some change in the behaviour if SELECT * FROM sequence is
effectively part of this proposal. I was going to make the same
comment myself.
Well, if we're going to blow off compatibility on that score, I suggest
that we blow it off all the way. Make sequences not be relations anymore,
and what you do instead of "SELECT * FROM sequence" is "SELECT * FROM
pg_sequences WHERE seqname = 'sequence'". Or more likely, since sequences
should still belong to schemas, we need a "regsequence" OID-alias type
like "regclass" and you do "SELECT * FROM pg_sequences WHERE oid =
'foo.bar'::regsequence".
The main problem I can see with this is that serial columns will
have default expressions that are written out as
"nextval('foo_f1_seq'::regclass)". I do not think we can afford to break
dumps containing that, but I'm not sure how to get the regclass cast
replaced with a regsequence cast.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On September 5, 2016 7:26:42 AM PDT, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Simon Riggs <simon@2ndquadrant.com> writes:
On 4 September 2016 at 23:17, Greg Stark <stark@mit.edu> wrote:
So? Clients expect changes like this between major releases surely.
Subtle changes that cause silent breakage for end-users seemsscarier
than unsubtle breakage that tool authors can fix.
Agreed; some change in the behaviour if SELECT * FROM sequence is
effectively part of this proposal. I was going to make the same
comment myself.Well, if we're going to blow off compatibility on that score, I suggest
that we blow it off all the way. Make sequences not be relations
anymore,
and what you do instead of "SELECT * FROM sequence" is "SELECT * FROM
pg_sequences WHERE seqname = 'sequence'". Or more likely, since
sequences
should still belong to schemas, we need a "regsequence" OID-alias type
like "regclass" and you do "SELECT * FROM pg_sequences WHERE oid =
'foo.bar'::regsequence".The main problem I can see with this is that serial columns will
have default expressions that are written out as
"nextval('foo_f1_seq'::regclass)". I do not think we can afford to
break
dumps containing that, but I'm not sure how to get the regclass cast
replaced with a regsequence cast.
Why not just continue having a pgclass entry, but no relfilenode?
--
Sent from my Android device with K-9 Mail. Please excuse my brevity.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Andres Freund <andres@anarazel.de> writes:
On September 5, 2016 7:26:42 AM PDT, Tom Lane <tgl@sss.pgh.pa.us> wrote:
The main problem I can see with this is that serial columns will have
default expressions that are written out as
"nextval('foo_f1_seq'::regclass)". I do not think we can afford to
break dumps containing that, but I'm not sure how to get the regclass
cast replaced with a regsequence cast.
Why not just continue having a pgclass entry, but no relfilenode?
Yeah, maybe. I was hoping to dispense with the pg_attribute rows, but
maybe that's not enough overhead to worry about.
In this viewpoint, we'd keep the sequence-specific data in a pg_sequence
catalog. pg_sequence rows would be extensions of the associated pg_class
rows in much the same way that pg_index rows extend the pg_class entries
for indexes. We should supply a view pg_sequences that performs the
implied join, and encourage users to select from that rather than directly
from pg_sequence (compare pg_indexes view).
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 9/5/16 10:35 PM, Tom Lane wrote:
In this viewpoint, we'd keep the sequence-specific data in a pg_sequence
catalog. pg_sequence rows would be extensions of the associated pg_class
rows in much the same way that pg_index rows extend the pg_class entries
for indexes. We should supply a view pg_sequences that performs the
implied join, and encourage users to select from that rather than directly
from pg_sequence (compare pg_indexes view).
Let's start with that. Here is a patch that adds a pg_sequences view in
the style of pg_tables, pg_indexes, etc. This seems useful independent
of anything else, but would give us more freedom to change things around
behind the scenes.
A slight naming wart: I added a function lastval(regclass) for internal
use to get a sequence's "last value". But we already have a public
function lastval(), which gets the most recent nextval() result of any
sequence. Those are two quite different things. I don't want to
abandon the term "last value" here, however, because that is what the
sequence relation uses internally, and also Oracle uses it in its system
views with the same semantics that I propose here. We could use a more
verbose name like sequence_last_value(regclass), perhaps.
lastval has been kept separate from pg_sequence_parameters, because if
we were to go ahead with a new catalog layout later, then
pg_sequence_parameters would become obsolescent while we would possibly
still need a lastval function.
The column names of the new view have been deliberately tuned to use a
more conventional style than the information schema while avoiding what
I would consider some past naming mistakes. (For example, I hate
"is_cycled", which reads like "sequence has wrapped around at least once
in the past").
Here are some similar views in other places:
https://docs.oracle.com/cd/B28359_01/server.111/b28320/statviews_2053.htm
https://www.ibm.com/support/knowledgecenter/en/SSEPGG_9.7.0/com.ibm.db2.luw.sql.ref.doc/doc/r0004203.html
https://msdn.microsoft.com/en-us/library/ff877934.aspx
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
0001-Add-pg_sequences-view.patchtext/x-patch; name=0001-Add-pg_sequences-view.patchDownload
From f74e1cc1f6ee4a56abc9f46c413c0af5086e1e40 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Fri, 9 Sep 2016 12:00:00 -0400
Subject: [PATCH] Add pg_sequences view
Like pg_tables, pg_views, and others, this view contains information
about sequences in a way that is independent of the system catalog
layout but more comprehensive than the information schema.
---
doc/src/sgml/catalogs.sgml | 90 ++++++++++++++++++++++++++++++++
src/backend/catalog/system_views.sql | 16 ++++++
src/backend/commands/sequence.c | 45 ++++++++++++++--
src/include/catalog/pg_proc.h | 4 +-
src/include/commands/sequence.h | 1 +
src/test/regress/expected/rules.out | 14 +++++
src/test/regress/expected/sequence.out | 16 ++++++
src/test/regress/expected/sequence_1.out | 16 ++++++
src/test/regress/sql/sequence.sql | 7 +++
9 files changed, 205 insertions(+), 4 deletions(-)
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 322d8d6..1c440e3 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -7379,6 +7379,11 @@ <title>System Views</title>
</row>
<row>
+ <entry><link linkend="view-pg-sequences"><structname>pg_sequences</structname></link></entry>
+ <entry>sequences</entry>
+ </row>
+
+ <row>
<entry><link linkend="view-pg-settings"><structname>pg_settings</structname></link></entry>
<entry>parameter settings</entry>
</row>
@@ -9114,6 +9119,91 @@ <title><structname>pg_seclabels</> Columns</title>
</table>
</sect1>
+ <sect1 id="view-pg-sequences">
+ <title><structname>pg_sequences</structname></title>
+
+ <indexterm zone="view-pg-sequences">
+ <primary>pg_sequences</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_sequences</structname> provides access to
+ useful information about each sequence in the database.
+ </para>
+
+ <table>
+ <title><structname>pg_sequences</> 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>schemaname</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.nspname</literal></entry>
+ <entry>Name of schema containing sequence</entry>
+ </row>
+ <row>
+ <entry><structfield>sequencename</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.relname</literal></entry>
+ <entry>Name of sequence</entry>
+ </row>
+ <row>
+ <entry><structfield>sequenceowner</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.rolname</literal></entry>
+ <entry>Name of sequence's owner</entry>
+ </row>
+ <row>
+ <entry><structfield>start_value</structfield></entry>
+ <entry><type>bigint</type></entry>
+ <entry></entry>
+ <entry>Start value of the sequence</entry>
+ </row>
+ <row>
+ <entry><structfield>min_value</structfield></entry>
+ <entry><type>bigint</type></entry>
+ <entry></entry>
+ <entry>Minimum value of the sequence</entry>
+ </row>
+ <row>
+ <entry><structfield>max_value</structfield></entry>
+ <entry><type>bigint</type></entry>
+ <entry></entry>
+ <entry>Maximum value of the sequence</entry>
+ </row>
+ <row>
+ <entry><structfield>increment_by</structfield></entry>
+ <entry><type>bigint</type></entry>
+ <entry></entry>
+ <entry>Increment value of the sequence</entry>
+ </row>
+ <row>
+ <entry><structfield>cycle</structfield></entry>
+ <entry><type>boolean</type></entry>
+ <entry></entry>
+ <entry>Whether the sequence cycles</entry>
+ </row>
+ <row>
+ <entry><structfield>cache_size</structfield></entry>
+ <entry><type>bigint</type></entry>
+ <entry></entry>
+ <entry>Cache size of the sequence</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
<sect1 id="view-pg-settings">
<title><structname>pg_settings</structname></title>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index ada2142..99a9b41 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -158,6 +158,22 @@ CREATE VIEW pg_indexes AS
LEFT JOIN pg_tablespace T ON (T.oid = I.reltablespace)
WHERE C.relkind IN ('r', 'm') AND I.relkind = 'i';
+CREATE OR REPLACE VIEW pg_sequences AS
+ SELECT
+ N.nspname AS schemaname,
+ C.relname AS sequencename,
+ pg_get_userbyid(C.relowner) AS sequenceowner,
+ p.start_value AS start_value,
+ p.minimum_value AS min_value,
+ p.maximum_value AS max_value,
+ p.increment AS increment_by,
+ p.cycle_option AS cycle,
+ p.cache_size AS cache_size,
+ lastval(C.oid) AS last_value
+ FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace),
+ LATERAL pg_sequence_parameters(C.oid) p
+ WHERE relkind = 'S';
+
CREATE VIEW pg_stats WITH (security_barrier) AS
SELECT
nspname AS schemaname,
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index fc3a8ee..4f3ca0e 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -835,6 +835,42 @@ lastval(PG_FUNCTION_ARGS)
}
/*
+ * Return the last value from the sequence
+ *
+ * XXX This has completely different semantics from lastval() without
+ * arguments.
+ */
+Datum
+lastval1(PG_FUNCTION_ARGS)
+{
+ Oid relid = PG_GETARG_OID(0);
+ SeqTable elm;
+ Relation seqrel;
+ Buffer buf;
+ HeapTupleData seqtuple;
+ Form_pg_sequence seq;
+ int64 result;
+
+ /* open and AccessShareLock sequence */
+ init_sequence(relid, &elm, &seqrel);
+
+ if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied for sequence %s",
+ RelationGetRelationName(seqrel))));
+
+ seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+
+ result = seq->last_value;
+
+ UnlockReleaseBuffer(buf);
+ relation_close(seqrel, NoLock);
+
+ PG_RETURN_INT64(result);
+}
+
+/*
* Main internal procedure that handles 2 & 3 arg forms of SETVAL.
*
* Note that the 3 arg version (which sets the is_called flag) is
@@ -1534,8 +1570,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
TupleDesc tupdesc;
- Datum values[5];
- bool isnull[5];
+ Datum values[6];
+ bool isnull[6];
SeqTable elm;
Relation seqrel;
Buffer buf;
@@ -1551,7 +1587,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
errmsg("permission denied for sequence %s",
RelationGetRelationName(seqrel))));
- tupdesc = CreateTemplateTupleDesc(5, false);
+ tupdesc = CreateTemplateTupleDesc(6, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
INT8OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value",
@@ -1562,6 +1598,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
INT8OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option",
BOOLOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size",
+ INT8OID, -1, 0);
BlessTupleDesc(tupdesc);
@@ -1574,6 +1612,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
values[2] = Int64GetDatum(seq->max_value);
values[3] = Int64GetDatum(seq->increment_by);
values[4] = BoolGetDatum(seq->is_cycled);
+ values[5] = Int64GetDatum(seq->cache_value);
UnlockReleaseBuffer(buf);
relation_close(seqrel, NoLock);
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index e2d08ba..1220bad 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1762,11 +1762,13 @@ DATA(insert OID = 1574 ( nextval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 2
DESCR("sequence next value");
DATA(insert OID = 1575 ( currval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ currval_oid _null_ _null_ _null_ ));
DESCR("sequence current value");
+DATA(insert OID = 4032 ( lastval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ lastval1 _null_ _null_ _null_ ));
+DESCR("sequence last value");
DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20 "2205 20" _null_ _null_ _null_ _null_ _null_ setval_oid _null_ _null_ _null_ ));
DESCR("set sequence value");
DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ ));
DESCR("set sequence value and is_called status");
-DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_));
+DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_));
DESCR("sequence parameters, for use by information schema");
DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ ));
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 392a626..0b115d6 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -71,6 +71,7 @@ extern Datum currval_oid(PG_FUNCTION_ARGS);
extern Datum setval_oid(PG_FUNCTION_ARGS);
extern Datum setval3_oid(PG_FUNCTION_ARGS);
extern Datum lastval(PG_FUNCTION_ARGS);
+extern Datum lastval1(PG_FUNCTION_ARGS);
extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 00700f2..0ce8b24 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1615,6 +1615,20 @@ UNION ALL
l.label
FROM (pg_shseclabel l
JOIN pg_authid rol ON (((l.classoid = rol.tableoid) AND (l.objoid = rol.oid))));
+pg_sequences| SELECT n.nspname AS schemaname,
+ c.relname AS sequencename,
+ pg_get_userbyid(c.relowner) AS sequenceowner,
+ p.start_value,
+ p.minimum_value AS min_value,
+ p.maximum_value AS max_value,
+ p.increment AS increment_by,
+ p.cycle_option AS cycle,
+ p.cache_size,
+ lastval((c.oid)::regclass) AS last_value
+ FROM (pg_class c
+ LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))),
+ LATERAL pg_sequence_parameters(c.oid) p(start_value, minimum_value, maximum_value, increment, cycle_option, cache_size)
+ WHERE (c.relkind = 'S'::"char");
pg_settings| SELECT a.name,
a.setting,
a.unit,
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 4ffbe92..5231c27 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -315,6 +315,22 @@ SELECT * FROM information_schema.sequences WHERE sequence_name IN
regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO
(6 rows)
+SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value
+FROM pg_sequences
+WHERE sequencename IN
+ ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
+ 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq')
+ ORDER BY sequencename ASC;
+ schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value
+------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------
+ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5
+ public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+(6 rows)
+
-- Test comments
COMMENT ON SEQUENCE asdf IS 'won''t work';
ERROR: relation "asdf" does not exist
diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out
index 05da2bf..977bf02 100644
--- a/src/test/regress/expected/sequence_1.out
+++ b/src/test/regress/expected/sequence_1.out
@@ -315,6 +315,22 @@ SELECT * FROM information_schema.sequences WHERE sequence_name IN
regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO
(6 rows)
+SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value
+FROM pg_sequences
+WHERE sequencename IN
+ ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
+ 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq')
+ ORDER BY sequencename ASC;
+ schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value
+------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------
+ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5
+ public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+(6 rows)
+
-- Test comments
COMMENT ON SEQUENCE asdf IS 'won''t work';
ERROR: relation "asdf" does not exist
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 98a2e7d..5333b20 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -144,6 +144,13 @@ CREATE SEQUENCE sequence_test2 START WITH 32;
'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq')
ORDER BY sequence_name ASC;
+SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value
+FROM pg_sequences
+WHERE sequencename IN
+ ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
+ 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq')
+ ORDER BY sequencename ASC;
+
-- Test comments
COMMENT ON SEQUENCE asdf IS 'won''t work';
COMMENT ON SEQUENCE sequence_test2 IS 'will work';
--
2.10.0
On Thu, Sep 1, 2016 at 12:00 AM, Andres Freund <andres@anarazel.de> wrote:
On 2016-08-31 14:23:41 -0400, Tom Lane wrote:
Andres Freund <andres@anarazel.de> writes:
On 2016-08-31 13:59:48 -0400, Tom Lane wrote:
You are ignoring the performance costs associated with eating 100x more
shared buffer space than necessary.I doubt that's measurable in any real-world scenario. You seldomly have
hundreds of thousands of sequences that you all select from at a high
rate.If there are only a few sequences in the database, cross-sequence
contention is not going to be a big issue anyway.Isn't that *precisely* when it's going to matter? If you have 5 active
tables & sequences where the latter previously used independent locks,
and they'd now be contending on a single lock.
I may be missing something here, but why would it contend on a lock,
as per locking scheme proposed by Alvaro, access to sequence object
will need a share lock on buffer page.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 2016-09-10 17:23:21 +0530, Amit Kapila wrote:
On Thu, Sep 1, 2016 at 12:00 AM, Andres Freund <andres@anarazel.de> wrote:
On 2016-08-31 14:23:41 -0400, Tom Lane wrote:
Andres Freund <andres@anarazel.de> writes:
On 2016-08-31 13:59:48 -0400, Tom Lane wrote:
You are ignoring the performance costs associated with eating 100x more
shared buffer space than necessary.I doubt that's measurable in any real-world scenario. You seldomly have
hundreds of thousands of sequences that you all select from at a high
rate.If there are only a few sequences in the database, cross-sequence
contention is not going to be a big issue anyway.Isn't that *precisely* when it's going to matter? If you have 5 active
tables & sequences where the latter previously used independent locks,
and they'd now be contending on a single lock.I may be missing something here, but why would it contend on a lock,
as per locking scheme proposed by Alvaro, access to sequence object
will need a share lock on buffer page.
To make checkpointing/bgwriter work correctly we need exclusive locks on
pages while writing..., or some new lock level preventing the page from
being written out, while "shared dirtying" locks are being held.
Andres
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Sun, Sep 11, 2016 at 12:39 AM, Andres Freund <andres@anarazel.de> wrote:
On 2016-09-10 17:23:21 +0530, Amit Kapila wrote:
I may be missing something here, but why would it contend on a lock,
as per locking scheme proposed by Alvaro, access to sequence object
will need a share lock on buffer page.To make checkpointing/bgwriter work correctly we need exclusive locks on
pages while writing..., or some new lock level preventing the page from
being written out, while "shared dirtying" locks are being held.
Right and I think you have a very valid concern, but if we think that
storing multiple sequences on a same page is a reasonable approach,
then we can invent some locking mechanism as indicated by you such
that two writes on same page won't block each other, but they will be
blocked with bgwriter/checkpointer.
--
With Regards,
Amit Kapila.
EnterpriseDB: http://www.enterprisedb.com
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Sun, Sep 11, 2016 at 2:15 PM, Amit Kapila <amit.kapila16@gmail.com> wrote:
On Sun, Sep 11, 2016 at 12:39 AM, Andres Freund <andres@anarazel.de> wrote:
On 2016-09-10 17:23:21 +0530, Amit Kapila wrote:
I may be missing something here, but why would it contend on a lock,
as per locking scheme proposed by Alvaro, access to sequence object
will need a share lock on buffer page.To make checkpointing/bgwriter work correctly we need exclusive locks on
pages while writing..., or some new lock level preventing the page from
being written out, while "shared dirtying" locks are being held.Right and I think you have a very valid concern, but if we think that
storing multiple sequences on a same page is a reasonable approach,
then we can invent some locking mechanism as indicated by you such
that two writes on same page won't block each other, but they will be
blocked with bgwriter/checkpointer.
This thread has died a couple of weeks back, so I am marking it as
returned with feedback by seeing the discussion that has been done.
Feel free to update the patch if you think that's not adapted.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 9/10/16 7:17 AM, Peter Eisentraut wrote:
Let's start with that. Here is a patch that adds a pg_sequences view in
the style of pg_tables, pg_indexes, etc. This seems useful independent
of anything else, but would give us more freedom to change things around
behind the scenes.
I have registered the pg_sequences view patch separately in the upcoming
commit fest: https://commitfest.postgresql.org/11/865/ (The posted patch
still applies, so no new patch.)
Attached is an updated patch for the pg_sequence catalog. I haven't
received an actual technical review in the last CF, so I'll move it
forward. However, if the pg_sequences view is accepted, the catalog
patch will of course need an update. So if someone is looking here with
limited time, review the view patch first.
As before, this patch requires the "sequences and pg_upgrade" patch in
order for pg_upgrade to work.
I think together these three changes will make sequence metadata
inspection and modification easier and more robust and will facilitate
future changes in this area.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
v2-0001-Add-pg_sequence-system-catalog.patchtext/x-patch; name=v2-0001-Add-pg_sequence-system-catalog.patchDownload
From bb8d2260d27ba8685844d6268550c8b6f11f195a Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Mon, 31 Oct 2016 12:00:00 -0400
Subject: [PATCH v2] 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.
---
src/backend/catalog/Makefile | 2 +-
src/backend/catalog/dependency.c | 3 +
src/backend/catalog/information_schema.sql | 13 +-
src/backend/commands/sequence.c | 375 +++++++++++++++-----------
src/backend/utils/cache/syscache.c | 12 +
src/include/catalog/indexing.h | 3 +
src/include/catalog/pg_sequence.h | 30 +++
src/include/commands/sequence.h | 29 +-
src/include/utils/syscache.h | 1 +
src/test/regress/expected/sanity_check.out | 1 +
src/test/regress/expected/sequence.out | 33 ++-
src/test/regress/expected/updatable_views.out | 93 +++----
src/test/regress/sql/sequence.sql | 8 +
src/test/regress/sql/updatable_views.sql | 2 +-
14 files changed, 358 insertions(+), 247 deletions(-)
create mode 100644 src/include/catalog/pg_sequence.h
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 1ce7610..cbf0d79 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -41,7 +41,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.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_collation.h pg_range.h pg_transform.h \
+ pg_collation.h pg_range.h pg_transform.h pg_sequence.h \
toasting.h indexing.h \
)
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 04d7840..8e1e1ac 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -66,6 +66,7 @@
#include "commands/proclang.h"
#include "commands/schemacmds.h"
#include "commands/seclabel.h"
+#include "commands/sequence.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
#include "nodes/nodeFuncs.h"
@@ -1155,6 +1156,8 @@ doDeletion(const ObjectAddress *object, int flags)
else
heap_drop_with_catalog(object->objectId);
}
+ if (relKind == RELKIND_SEQUENCE)
+ DeleteSequenceTuple(object->objectId);
break;
}
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index 00550eb..182d2d0 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -1535,15 +1535,16 @@ CREATE VIEW sequences AS
CAST(64 AS cardinal_number) AS numeric_precision,
CAST(2 AS cardinal_number) AS numeric_precision_radix,
CAST(0 AS cardinal_number) AS numeric_scale,
- CAST(p.start_value AS character_data) AS start_value,
- CAST(p.minimum_value AS character_data) AS minimum_value,
- CAST(p.maximum_value AS character_data) AS maximum_value,
- CAST(p.increment AS character_data) AS increment,
- CAST(CASE WHEN p.cycle_option 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
+ CAST(s.seqstart AS character_data) AS start_value,
+ CAST(s.seqmin AS character_data) AS minimum_value,
+ CAST(s.seqmax AS character_data) AS maximum_value,
+ CAST(s.seqincrement AS character_data) AS increment,
+ CAST(CASE WHEN s.seqcycle THEN 'YES' ELSE 'NO' END AS yes_or_no) AS cycle_option
+ FROM pg_namespace nc, pg_class c, pg_sequence s
WHERE c.relnamespace = nc.oid
AND c.relkind = 'S'
AND (NOT pg_is_other_temp_schema(nc.oid))
+ AND c.oid = s.seqrelid
AND (pg_has_role(c.relowner, 'USAGE')
OR has_sequence_privilege(c.oid, 'SELECT, UPDATE, USAGE') );
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index fc3a8ee..c510b8b 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -22,8 +22,10 @@
#include "access/xloginsert.h"
#include "access/xlogutils.h"
#include "catalog/dependency.h"
+#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
+#include "catalog/pg_sequence.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/sequence.h"
@@ -74,7 +76,7 @@ typedef struct SeqTableData
int64 cached; /* last value already cached for nextval */
/* if last != cached, we have not used up all the cached values */
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;
typedef SeqTableData *SeqTable;
@@ -92,10 +94,11 @@ static int64 nextval_internal(Oid relid);
static Relation open_share_lock(SeqTable seq);
static void create_seq_hashtable(void);
static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
- Buffer *buf, HeapTuple seqtuple);
+static Form_pg_sequence_data read_seq_tuple(Relation rel,
+ Buffer *buf, HeapTuple seqdatatuple);
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 process_owned_by(Relation seqrel, List *owned_by);
@@ -107,7 +110,8 @@ static void process_owned_by(Relation seqrel, List *owned_by);
ObjectAddress
DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
{
- FormData_pg_sequence new;
+ FormData_pg_sequence seqform;
+ FormData_pg_sequence_data seqdataform;
List *owned_by;
CreateStmt *stmt = makeNode(CreateStmt);
Oid seqoid;
@@ -117,8 +121,9 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
TupleDesc tupDesc;
Datum value[SEQ_COL_LASTCOL];
bool null[SEQ_COL_LASTCOL];
+ Datum pgs_values[Natts_pg_sequence];
+ bool pgs_nulls[Natts_pg_sequence];
int i;
- NameData name;
/* Unlogged sequences are not implemented -- not clear if useful. */
if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -145,7 +150,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
}
/* 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)
@@ -171,52 +176,16 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
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:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
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";
- 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);
+ value[i - 1] = Int64GetDatumFast(seqdataform.last_value);
break;
case SEQ_COL_LOG:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
coldef->colname = "log_cnt";
value[i - 1] = Int64GetDatum((int64) 0);
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:
coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
coldef->colname = "is_called";
@@ -251,6 +220,27 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
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;
}
@@ -271,10 +261,13 @@ ResetSequence(Oid seq_relid)
{
Relation seq_rel;
SeqTable elm;
- Form_pg_sequence seq;
+ Form_pg_sequence_data seq;
Buffer buf;
- HeapTupleData seqtuple;
+ HeapTupleData seqdatatuple;
HeapTuple tuple;
+ HeapTuple pgstuple;
+ Form_pg_sequence pgsform;
+ int64 startv;
/*
* Read the old sequence. This does a bit more work than really
@@ -282,12 +275,19 @@ ResetSequence(Oid seq_relid)
* indeed a sequence.
*/
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.
*/
- tuple = heap_copytuple(&seqtuple);
+ tuple = heap_copytuple(&seqdatatuple);
/* Now we're done with the old page */
UnlockReleaseBuffer(buf);
@@ -296,8 +296,8 @@ ResetSequence(Oid seq_relid)
* Modify the copied tuple to execute the restart (compare the RESTART
* action in AlterSequence)
*/
- seq = (Form_pg_sequence) GETSTRUCT(tuple);
- seq->last_value = seq->start_value;
+ seq = (Form_pg_sequence_data) GETSTRUCT(tuple);
+ seq->last_value = startv;
seq->is_called = false;
seq->log_cnt = 0;
@@ -410,11 +410,14 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
SeqTable elm;
Relation seqrel;
Buffer buf;
- HeapTupleData seqtuple;
- Form_pg_sequence seq;
- FormData_pg_sequence new;
+ HeapTupleData seqdatatuple;
+ Form_pg_sequence seqform;
+ Form_pg_sequence_data seqdata;
+ FormData_pg_sequence_data newseqdata;
List *owned_by;
ObjectAddress address;
+ Relation rel;
+ HeapTuple tuple;
/* Open and lock sequence. */
relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
@@ -434,13 +437,22 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
stmt->sequence->relname);
/* 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 */
- 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 */
- 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 */
/* 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 */
START_CRIT_SECTION();
- memcpy(seq, &new, sizeof(FormData_pg_sequence));
+ memcpy(seqdata, &newseqdata, sizeof(FormData_pg_sequence_data));
MarkBufferDirty(buf);
@@ -470,7 +482,7 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
xlrec.node = seqrel->rd_node;
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);
@@ -491,9 +503,30 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
relation_close(seqrel, NoLock);
+ simple_heap_update(rel, &tuple->t_self, tuple);
+ CatalogUpdateIndexes(rel, tuple);
+ heap_close(rel, RowExclusiveLock);
+
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
@@ -537,8 +570,10 @@ nextval_internal(Oid relid)
Relation seqrel;
Buffer buf;
Page page;
- HeapTupleData seqtuple;
- Form_pg_sequence seq;
+ HeapTuple pgstuple;
+ Form_pg_sequence pgsform;
+ HeapTupleData seqdatatuple;
+ Form_pg_sequence_data seq;
int64 incby,
maxv,
minv,
@@ -549,6 +584,7 @@ nextval_internal(Oid relid)
int64 result,
next,
rescnt = 0;
+ bool cycle;
bool logit = false;
/* open and AccessShareLock sequence */
@@ -582,15 +618,24 @@ nextval_internal(Oid relid)
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 */
- seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+ seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
page = BufferGetPage(buf);
+ elm->increment = incby;
last = next = result = seq->last_value;
- incby = seq->increment_by;
- maxv = seq->max_value;
- minv = seq->min_value;
- fetch = cache = seq->cache_value;
+ fetch = cache;
log = seq->log_cnt;
if (!seq->is_called)
@@ -641,7 +686,7 @@ nextval_internal(Oid relid)
{
if (rescnt > 0)
break; /* stop fetching */
- if (!seq->is_cycled)
+ if (!cycle)
{
char buf[100];
@@ -664,7 +709,7 @@ nextval_internal(Oid relid)
{
if (rescnt > 0)
break; /* stop fetching */
- if (!seq->is_cycled)
+ if (!cycle)
{
char buf[100];
@@ -747,7 +792,7 @@ nextval_internal(Oid relid)
xlrec.node = seqrel->rd_node;
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);
@@ -853,8 +898,12 @@ do_setval(Oid relid, int64 next, bool iscalled)
SeqTable elm;
Relation seqrel;
Buffer buf;
- HeapTupleData seqtuple;
- Form_pg_sequence seq;
+ HeapTupleData seqdatatuple;
+ Form_pg_sequence_data seq;
+ HeapTuple pgstuple;
+ Form_pg_sequence pgsform;
+ int64 maxv,
+ minv;
/* open and AccessShareLock sequence */
init_sequence(relid, &elm, &seqrel);
@@ -865,6 +914,14 @@ do_setval(Oid relid, int64 next, bool iscalled)
errmsg("permission denied for sequence %s",
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 */
if (!seqrel->rd_islocaltemp)
PreventCommandIfReadOnly("setval()");
@@ -877,17 +934,17 @@ do_setval(Oid relid, int64 next, bool iscalled)
PreventCommandIfParallelMode("setval()");
/* 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],
bufm[100],
bufx[100];
snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
- snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, minv);
+ snprintf(bufx, sizeof(bufx), INT64_FORMAT, maxv);
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
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;
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);
@@ -1064,7 +1121,7 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
elm->filenode = InvalidOid;
elm->lxid = InvalidLocalTransactionId;
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
*
* *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)
*
* Function's return value points to the data payload of the tuple
*/
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+static Form_pg_sequence_data
+read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple)
{
Page page;
ItemId lp;
sequence_magic *sm;
- Form_pg_sequence seq;
+ Form_pg_sequence_data seq;
*buf = ReadBuffer(rel, 0);
LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
@@ -1125,9 +1182,9 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
lp = PageGetItemId(page, FirstOffsetNumber);
Assert(ItemIdIsNormal(lp));
- /* Note we currently only bother to set these two fields of *seqtuple */
- seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- seqtuple->t_len = ItemIdGetLength(lp);
+ /* Note we currently only bother to set these two fields of *seqdatatuple */
+ seqdatatuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ seqdatatuple->t_len = ItemIdGetLength(lp);
/*
* 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
* this again if the update gets lost.
*/
- Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
- if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+ Assert(!(seqdatatuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+ if (HeapTupleHeaderGetRawXmax(seqdatatuple->t_data) != InvalidTransactionId)
{
- HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
- seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
- seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
+ HeapTupleHeaderSetXmax(seqdatatuple->t_data, InvalidTransactionId);
+ seqdatatuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+ seqdatatuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
MarkBufferDirtyHint(*buf, true);
}
- seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
-
- /* this is a handy place to update our copy of the increment */
- elm->increment = seq->increment_by;
+ seq = (Form_pg_sequence_data) GETSTRUCT(seqdatatuple);
return seq;
}
@@ -1164,7 +1218,8 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
*/
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)
{
DefElem *start_value = NULL;
DefElem *restart_value = NULL;
@@ -1263,69 +1318,69 @@ init_params(ParseState *pstate, List *options, bool isInit,
* would affect future nextval allocations.
*/
if (isInit)
- new->log_cnt = 0;
+ seqdataform->log_cnt = 0;
/* INCREMENT BY */
if (increment_by != NULL)
{
- new->increment_by = defGetInt64(increment_by);
- if (new->increment_by == 0)
+ seqform->seqincrement = defGetInt64(increment_by);
+ if (seqform->seqincrement == 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("INCREMENT must not be zero")));
- new->log_cnt = 0;
+ seqdataform->log_cnt = 0;
}
else if (isInit)
- new->increment_by = 1;
+ seqform->seqincrement = 1;
/* CYCLE */
if (is_cycled != NULL)
{
- new->is_cycled = intVal(is_cycled->arg);
- Assert(BoolIsValid(new->is_cycled));
- new->log_cnt = 0;
+ seqform->seqcycle = intVal(is_cycled->arg);
+ Assert(BoolIsValid(seqform->seqcycle));
+ seqdataform->log_cnt = 0;
}
else if (isInit)
- new->is_cycled = false;
+ seqform->seqcycle = false;
/* MAXVALUE (null arg means NO MAXVALUE) */
if (max_value != NULL && max_value->arg)
{
- new->max_value = defGetInt64(max_value);
- new->log_cnt = 0;
+ seqform->seqmax = defGetInt64(max_value);
+ seqdataform->log_cnt = 0;
}
else if (isInit || max_value != NULL)
{
- if (new->increment_by > 0)
- new->max_value = SEQ_MAXVALUE; /* ascending seq */
+ if (seqform->seqincrement > 0)
+ seqform->seqmax = SEQ_MAXVALUE; /* ascending seq */
else
- new->max_value = -1; /* descending seq */
- new->log_cnt = 0;
+ seqform->seqmax = -1; /* descending seq */
+ seqdataform->log_cnt = 0;
}
/* MINVALUE (null arg means NO MINVALUE) */
if (min_value != NULL && min_value->arg)
{
- new->min_value = defGetInt64(min_value);
- new->log_cnt = 0;
+ seqform->seqmin = defGetInt64(min_value);
+ seqdataform->log_cnt = 0;
}
else if (isInit || min_value != NULL)
{
- if (new->increment_by > 0)
- new->min_value = 1; /* ascending seq */
+ if (seqform->seqincrement > 0)
+ seqform->seqmin = 1; /* ascending seq */
else
- new->min_value = SEQ_MINVALUE; /* descending seq */
- new->log_cnt = 0;
+ seqform->seqmin = SEQ_MINVALUE; /* descending seq */
+ seqdataform->log_cnt = 0;
}
/* crosscheck min/max */
- if (new->min_value >= new->max_value)
+ if (seqform->seqmin >= seqform->seqmax)
{
char bufm[100],
bufx[100];
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
- snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin);
+ snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("MINVALUE (%s) must be less than MAXVALUE (%s)",
@@ -1334,35 +1389,35 @@ init_params(ParseState *pstate, List *options, bool isInit,
/* START WITH */
if (start_value != NULL)
- new->start_value = defGetInt64(start_value);
+ seqform->seqstart = defGetInt64(start_value);
else if (isInit)
{
- if (new->increment_by > 0)
- new->start_value = new->min_value; /* ascending seq */
+ if (seqform->seqincrement > 0)
+ seqform->seqstart = seqform->seqmin; /* ascending seq */
else
- new->start_value = new->max_value; /* descending seq */
+ seqform->seqstart = seqform->seqmax; /* descending seq */
}
/* crosscheck START */
- if (new->start_value < new->min_value)
+ if (seqform->seqstart < seqform->seqmin)
{
char bufs[100],
bufm[100];
- snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqform->seqstart);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin);
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)
+ if (seqform->seqstart > seqform->seqmax)
{
char bufs[100],
bufm[100];
- snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqform->seqstart);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmax);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
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->arg != NULL)
- new->last_value = defGetInt64(restart_value);
+ seqdataform->last_value = defGetInt64(restart_value);
else
- new->last_value = new->start_value;
- new->is_called = false;
- new->log_cnt = 0;
+ seqdataform->last_value = seqform->seqstart;
+ seqdataform->is_called = false;
+ seqdataform->log_cnt = 0;
}
else if (isInit)
{
- new->last_value = new->start_value;
- new->is_called = false;
+ seqdataform->last_value = seqform->seqstart;
+ seqdataform->is_called = false;
}
/* 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],
bufm[100];
- snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqdataform->last_value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
bufs, bufm)));
}
- if (new->last_value > new->max_value)
+ if (seqdataform->last_value > seqform->seqmax)
{
char bufs[100],
bufm[100];
- snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqdataform->last_value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmax);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
@@ -1414,21 +1469,21 @@ init_params(ParseState *pstate, List *options, bool isInit,
/* CACHE */
if (cache_value != NULL)
{
- new->cache_value = defGetInt64(cache_value);
- if (new->cache_value <= 0)
+ seqform->seqcache = defGetInt64(cache_value);
+ if (seqform->seqcache <= 0)
{
char buf[100];
- snprintf(buf, sizeof(buf), INT64_FORMAT, new->cache_value);
+ snprintf(buf, sizeof(buf), INT64_FORMAT, seqform->seqcache);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("CACHE (%s) must be greater than zero",
buf)));
}
- new->log_cnt = 0;
+ seqdataform->log_cnt = 0;
}
else if (isInit)
- new->cache_value = 1;
+ seqform->seqcache = 1;
}
/*
@@ -1527,7 +1582,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
pg_sequence_parameters(PG_FUNCTION_ARGS)
@@ -1536,20 +1591,14 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
TupleDesc tupdesc;
Datum values[5];
bool isnull[5];
- SeqTable elm;
- Relation seqrel;
- Buffer buf;
- HeapTupleData seqtuple;
- Form_pg_sequence seq;
-
- /* open and AccessShareLock sequence */
- init_sequence(relid, &elm, &seqrel);
+ HeapTuple pgstuple;
+ Form_pg_sequence pgsform;
if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied for sequence %s",
- RelationGetRelationName(seqrel))));
+ get_rel_name(relid))));
tupdesc = CreateTemplateTupleDesc(5, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1567,16 +1616,18 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
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[1] = Int64GetDatum(seq->min_value);
- values[2] = Int64GetDatum(seq->max_value);
- values[3] = Int64GetDatum(seq->increment_by);
- values[4] = BoolGetDatum(seq->is_cycled);
+ values[0] = Int64GetDatum(pgsform->seqstart);
+ values[1] = Int64GetDatum(pgsform->seqmin);
+ values[2] = Int64GetDatum(pgsform->seqmax);
+ values[3] = Int64GetDatum(pgsform->seqincrement);
+ values[4] = BoolGetDatum(pgsform->seqcycle);
- UnlockReleaseBuffer(buf);
- relation_close(seqrel, NoLock);
+ ReleaseSysCache(pgstuple);
return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
}
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 65ffe84..1a6205b 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -52,6 +52,7 @@
#include "catalog/pg_range.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_seclabel.h"
+#include "catalog/pg_sequence.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_shseclabel.h"
@@ -656,6 +657,17 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {SequenceRelationId, /* SEQRELID */
+ SequenceRelidIndexId,
+ 1,
+ {
+ Anum_pg_sequence_seqrelid,
+ 0,
+ 0,
+ 0
+ },
+ 32
+ },
{StatisticRelationId, /* STATRELATTINH */
StatisticRelidAttnumInhIndexId,
3,
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ca5eb3d..3ef880e 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -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));
#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));
#define SharedDependDependerIndexId 1232
DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h
new file mode 100644
index 0000000..2e24329
--- /dev/null
+++ b/src/include/catalog/pg_sequence.h
@@ -0,0 +1,30 @@
+#ifndef PG_SEQUENCE_H
+#define PG_SEQUENCE_H
+
+#include "catalog/genbki.h"
+
+#define SequenceRelationId 4032
+
+CATALOG(pg_sequence,4032) 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 */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 392a626..240a500 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -22,38 +22,24 @@
#include "storage/relfilenode.h"
-typedef struct FormData_pg_sequence
+typedef struct FormData_pg_sequence_data
{
- NameData sequence_name;
int64 last_value;
- int64 start_value;
- int64 increment_by;
- int64 max_value;
- int64 min_value;
- int64 cache_value;
int64 log_cnt;
- bool is_cycled;
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
*/
-#define SEQ_COL_NAME 1
-#define SEQ_COL_LASTVAL 2
-#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_LASTVAL 1
+#define SEQ_COL_LOG 2
+#define SEQ_COL_CALLED 3
-#define SEQ_COL_FIRSTCOL SEQ_COL_NAME
+#define SEQ_COL_FIRSTCOL SEQ_COL_LASTVAL
#define SEQ_COL_LASTCOL SEQ_COL_CALLED
/* XLOG stuff */
@@ -76,6 +62,7 @@ extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
extern ObjectAddress DefineSequence(ParseState *pstate, CreateSeqStmt *stmt);
extern ObjectAddress AlterSequence(ParseState *pstate, AlterSeqStmt *stmt);
+extern void DeleteSequenceTuple(Oid relid);
extern void ResetSequence(Oid seq_relid);
extern void ResetSequenceCaches(void);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 256615b..9c52be4 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -80,6 +80,7 @@ enum SysCacheIdentifier
REPLORIGIDENT,
REPLORIGNAME,
RULERELNAME,
+ SEQRELID,
STATRELATTINH,
TABLESPACEOID,
TRFOID,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index b1ebcf6..58edf2e 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -127,6 +127,7 @@ pg_range|t
pg_replication_origin|t
pg_rewrite|t
pg_seclabel|t
+pg_sequence|t
pg_shdepend|t
pg_shdescription|t
pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 4ffbe92..11541b6 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
CREATE SEQUENCE foo_seq;
ALTER TABLE foo_seq RENAME TO 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
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f
+ last_value | log_cnt | is_called
+------------+---------+-----------
+ 1 | 0 | f
(1 row)
SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
(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 | 31 | f | t
+ last_value | log_cnt | is_called
+------------+---------+-----------
+ 2 | 31 | t
(1 row)
DROP SEQUENCE foo_seq_new;
@@ -517,3 +517,24 @@ SELECT * FROM information_schema.sequences WHERE sequence_name IN
DROP USER regress_seq_user;
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;
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index f60991e..db3570f 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -86,55 +86,48 @@ SELECT table_name, column_name, is_updatable
FROM information_schema.columns
WHERE table_name LIKE E'r_\\_view%'
ORDER BY table_name, ordinal_position;
- table_name | column_name | is_updatable
-------------+---------------+--------------
- ro_view1 | a | NO
- ro_view1 | b | NO
- ro_view10 | a | NO
- ro_view11 | a | NO
- ro_view11 | b | NO
- ro_view12 | a | NO
- ro_view13 | a | NO
- ro_view13 | b | NO
- ro_view17 | a | NO
- ro_view17 | b | NO
- ro_view18 | a | NO
- ro_view19 | sequence_name | NO
- ro_view19 | last_value | NO
- ro_view19 | start_value | NO
- ro_view19 | increment_by | NO
- ro_view19 | max_value | NO
- ro_view19 | min_value | NO
- ro_view19 | cache_value | NO
- ro_view19 | log_cnt | NO
- ro_view19 | is_cycled | NO
- ro_view19 | is_called | NO
- ro_view2 | a | NO
- ro_view2 | b | NO
- ro_view20 | a | NO
- ro_view20 | b | NO
- ro_view20 | g | NO
- ro_view3 | ?column? | NO
- ro_view4 | count | NO
- ro_view5 | a | NO
- ro_view5 | rank | NO
- ro_view6 | a | NO
- ro_view6 | b | NO
- ro_view7 | a | NO
- ro_view7 | b | NO
- ro_view8 | a | NO
- ro_view8 | b | NO
- ro_view9 | a | NO
- ro_view9 | b | NO
- rw_view14 | ctid | NO
- rw_view14 | a | YES
- 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)
+ table_name | column_name | is_updatable
+------------+-------------+--------------
+ ro_view1 | a | NO
+ ro_view1 | b | NO
+ ro_view10 | a | NO
+ ro_view11 | a | NO
+ ro_view11 | b | NO
+ ro_view12 | a | NO
+ ro_view13 | a | NO
+ ro_view13 | b | NO
+ ro_view17 | a | NO
+ ro_view17 | b | NO
+ ro_view18 | a | NO
+ ro_view19 | last_value | NO
+ ro_view19 | log_cnt | NO
+ ro_view19 | is_called | NO
+ ro_view2 | a | NO
+ ro_view2 | b | NO
+ ro_view20 | a | NO
+ ro_view20 | b | NO
+ ro_view20 | g | NO
+ ro_view3 | ?column? | NO
+ ro_view4 | count | NO
+ ro_view5 | a | NO
+ ro_view5 | rank | NO
+ ro_view6 | a | NO
+ ro_view6 | b | NO
+ ro_view7 | a | NO
+ ro_view7 | b | NO
+ ro_view8 | a | NO
+ ro_view8 | b | NO
+ ro_view9 | a | NO
+ ro_view9 | b | NO
+ rw_view14 | ctid | NO
+ rw_view14 | a | YES
+ rw_view14 | b | YES
+ rw_view15 | a | YES
+ rw_view15 | upper | NO
+ rw_view16 | a | YES
+ rw_view16 | b | YES
+ rw_view16 | aa | YES
+(39 rows)
-- Read-only views
DELETE FROM ro_view1;
@@ -327,7 +320,7 @@ DELETE FROM 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.
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"
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.
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 98a2e7d..4dcb806 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -262,3 +262,11 @@ CREATE SEQUENCE seq3;
DROP USER regress_seq_user;
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;
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index 03c3f9d..f008479 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -95,7 +95,7 @@ CREATE VIEW ro_view20 AS SELECT a, b, generate_series(1, a) g FROM base_tbl; --
-- Read-only views
INSERT INTO ro_view17 VALUES (3, 'ROW 3');
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);
DROP TABLE base_tbl CASCADE;
--
2.10.2
Review of the pg_sequences view.
This seems like a useful addition to me, making life easier for
administrators and monitoring tools. While there is already a view in
information_schema it is missing cache_size and last_value.
= Functional review
- The patch applies and passes the test suite without any issue.
- A worry is that it might get a bit confusing to have both the future
catalog pg_sequence and the view pg_sequences.
- I think it would be useful to include is_cycled in the view.
- When creating a temporary sequences and then running "SELECT * FROM
pg_sequences" in another session I get the following error.
ERROR: cannot access temporary tables of other sessions
- Shouldn't last_value be NULL directly after we have created the
sequence but nobody has called nextval() yet?
- I noticed that last_value includes the cached values, but that also
seems to me like the correct thing to do.
- I do not like the name of the new function, lastval(regclass). I think
like you suggested it would be better with something more verbose.
sequence_lastval()? sequence_last_value()?
= Code
- There is an XXX comment still in the code. It is about the name of the
lastval1() function.
= Documentation
- The documentation does not mention the last_value column.
- The extra empty line after "</table>" does not fit with the formatting
of the rest of the SGML file.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 11/8/16 6:43 PM, Andreas Karlsson wrote:
- A worry is that it might get a bit confusing to have both the future
catalog pg_sequence and the view pg_sequences.
We already have this in other cases: pg_index/pg_indexes,
pg_user_mapping/pg_user_mappings. It's an established naming system by now.
- I think it would be useful to include is_cycled in the view.
It's there under the name "cycle".
- When creating a temporary sequences and then running "SELECT * FROM
pg_sequences" in another session I get the following error.ERROR: cannot access temporary tables of other sessions
Fixed that by adding pg_is_other_temp_schema() to the view definition.
We use that in the information schema but not in the system views so
far. That might be worth looking into.
- Shouldn't last_value be NULL directly after we have created the
sequence but nobody has called nextval() yet?- I noticed that last_value includes the cached values, but that also
seems to me like the correct thing to do.
The documentation now emphasizes that this is the value stored on disk.
This matches what Oracle does.
- I do not like the name of the new function, lastval(regclass). I think
like you suggested it would be better with something more verbose.
sequence_lastval()? sequence_last_value()?
changed
- There is an XXX comment still in the code. It is about the name of the
lastval1() function.
fixed
- The documentation does not mention the last_value column.
fixed
- The extra empty line after "</table>" does not fit with the formatting
of the rest of the SGML file.
fixed
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
v2-0001-Add-pg_sequences-view.patchtext/x-patch; name=v2-0001-Add-pg_sequences-view.patchDownload
From 189910cc0f470f11ec1f76073acb7b91258c76fd Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Wed, 9 Nov 2016 12:00:00 -0500
Subject: [PATCH v2] Add pg_sequences view
Like pg_tables, pg_views, and others, this view contains information
about sequences in a way that is independent of the system catalog
layout but more comprehensive than the information schema.
---
doc/src/sgml/catalogs.sgml | 97 ++++++++++++++++++++++++++++++++
src/backend/catalog/system_views.sql | 17 ++++++
src/backend/commands/sequence.c | 44 ++++++++++++++-
src/include/catalog/pg_proc.h | 4 +-
src/include/commands/sequence.h | 1 +
src/test/regress/expected/rules.out | 14 +++++
src/test/regress/expected/sequence.out | 16 ++++++
src/test/regress/expected/sequence_1.out | 16 ++++++
src/test/regress/sql/sequence.sql | 7 +++
9 files changed, 212 insertions(+), 4 deletions(-)
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index bac169a..fcc9038 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -7395,6 +7395,11 @@ <title>System Views</title>
</row>
<row>
+ <entry><link linkend="view-pg-sequences"><structname>pg_sequences</structname></link></entry>
+ <entry>sequences</entry>
+ </row>
+
+ <row>
<entry><link linkend="view-pg-settings"><structname>pg_settings</structname></link></entry>
<entry>parameter settings</entry>
</row>
@@ -9135,6 +9140,98 @@ <title><structname>pg_seclabels</> Columns</title>
</table>
</sect1>
+ <sect1 id="view-pg-sequences">
+ <title><structname>pg_sequences</structname></title>
+
+ <indexterm zone="view-pg-sequences">
+ <primary>pg_sequences</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_sequences</structname> provides access to
+ useful information about each sequence in the database.
+ </para>
+
+ <table>
+ <title><structname>pg_sequences</> 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>schemaname</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.nspname</literal></entry>
+ <entry>Name of schema containing sequence</entry>
+ </row>
+ <row>
+ <entry><structfield>sequencename</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.relname</literal></entry>
+ <entry>Name of sequence</entry>
+ </row>
+ <row>
+ <entry><structfield>sequenceowner</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.rolname</literal></entry>
+ <entry>Name of sequence's owner</entry>
+ </row>
+ <row>
+ <entry><structfield>start_value</structfield></entry>
+ <entry><type>bigint</type></entry>
+ <entry></entry>
+ <entry>Start value of the sequence</entry>
+ </row>
+ <row>
+ <entry><structfield>min_value</structfield></entry>
+ <entry><type>bigint</type></entry>
+ <entry></entry>
+ <entry>Minimum value of the sequence</entry>
+ </row>
+ <row>
+ <entry><structfield>max_value</structfield></entry>
+ <entry><type>bigint</type></entry>
+ <entry></entry>
+ <entry>Maximum value of the sequence</entry>
+ </row>
+ <row>
+ <entry><structfield>increment_by</structfield></entry>
+ <entry><type>bigint</type></entry>
+ <entry></entry>
+ <entry>Increment value of the sequence</entry>
+ </row>
+ <row>
+ <entry><structfield>cycle</structfield></entry>
+ <entry><type>boolean</type></entry>
+ <entry></entry>
+ <entry>Whether the sequence cycles</entry>
+ </row>
+ <row>
+ <entry><structfield>cache_size</structfield></entry>
+ <entry><type>bigint</type></entry>
+ <entry></entry>
+ <entry>Cache size of the sequence</entry>
+ </row>
+ <row>
+ <entry><structfield>last_value</structfield></entry>
+ <entry><type>bigint</type></entry>
+ <entry></entry>
+ <entry>The last sequence value written to disk. If caching is used,
+ this value can be greater than the last value handed out from the
+ sequence.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
<sect1 id="view-pg-settings">
<title><structname>pg_settings</structname></title>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index ada2142..e011af1 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -158,6 +158,23 @@ CREATE VIEW pg_indexes AS
LEFT JOIN pg_tablespace T ON (T.oid = I.reltablespace)
WHERE C.relkind IN ('r', 'm') AND I.relkind = 'i';
+CREATE OR REPLACE VIEW pg_sequences AS
+ SELECT
+ N.nspname AS schemaname,
+ C.relname AS sequencename,
+ pg_get_userbyid(C.relowner) AS sequenceowner,
+ p.start_value AS start_value,
+ p.minimum_value AS min_value,
+ p.maximum_value AS max_value,
+ p.increment AS increment_by,
+ p.cycle_option AS cycle,
+ p.cache_size AS cache_size,
+ pg_sequence_last_value(C.oid) AS last_value
+ FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace),
+ LATERAL pg_sequence_parameters(C.oid) p
+ WHERE NOT pg_is_other_temp_schema(N.oid)
+ AND relkind = 'S';
+
CREATE VIEW pg_stats WITH (security_barrier) AS
SELECT
nspname AS schemaname,
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index fc3a8ee..71c3f92 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1534,8 +1534,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
{
Oid relid = PG_GETARG_OID(0);
TupleDesc tupdesc;
- Datum values[5];
- bool isnull[5];
+ Datum values[6];
+ bool isnull[6];
SeqTable elm;
Relation seqrel;
Buffer buf;
@@ -1551,7 +1551,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
errmsg("permission denied for sequence %s",
RelationGetRelationName(seqrel))));
- tupdesc = CreateTemplateTupleDesc(5, false);
+ tupdesc = CreateTemplateTupleDesc(6, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
INT8OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "minimum_value",
@@ -1562,6 +1562,8 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
INT8OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "cycle_option",
BOOLOID, -1, 0);
+ TupleDescInitEntry(tupdesc, (AttrNumber) 6, "cache_size",
+ INT8OID, -1, 0);
BlessTupleDesc(tupdesc);
@@ -1574,6 +1576,7 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
values[2] = Int64GetDatum(seq->max_value);
values[3] = Int64GetDatum(seq->increment_by);
values[4] = BoolGetDatum(seq->is_cycled);
+ values[5] = Int64GetDatum(seq->cache_value);
UnlockReleaseBuffer(buf);
relation_close(seqrel, NoLock);
@@ -1581,6 +1584,41 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
}
+/*
+ * Return the last value from the sequence
+ *
+ * Note: This has a completely different meaning than lastval().
+ */
+Datum
+pg_sequence_last_value(PG_FUNCTION_ARGS)
+{
+ Oid relid = PG_GETARG_OID(0);
+ SeqTable elm;
+ Relation seqrel;
+ Buffer buf;
+ HeapTupleData seqtuple;
+ Form_pg_sequence seq;
+ int64 result;
+
+ /* open and AccessShareLock sequence */
+ init_sequence(relid, &elm, &seqrel);
+
+ if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_USAGE) != ACLCHECK_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied for sequence %s",
+ RelationGetRelationName(seqrel))));
+
+ seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+
+ result = seq->last_value;
+
+ UnlockReleaseBuffer(buf);
+ relation_close(seqrel, NoLock);
+
+ PG_RETURN_INT64(result);
+}
+
void
seq_redo(XLogReaderState *record)
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 17ec71d..047a1ce 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1763,8 +1763,10 @@ DATA(insert OID = 1576 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 2 0 20
DESCR("set sequence value");
DATA(insert OID = 1765 ( setval PGNSP PGUID 12 1 0 0 0 f f f f t f v u 3 0 20 "2205 20 16" _null_ _null_ _null_ _null_ _null_ setval3_oid _null_ _null_ _null_ ));
DESCR("set sequence value and is_called status");
-DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_));
+DATA(insert OID = 3078 ( pg_sequence_parameters PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 2249 "26" "{26,20,20,20,20,16,20}" "{i,o,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option,cache_size}" _null_ _null_ pg_sequence_parameters _null_ _null_ _null_));
DESCR("sequence parameters, for use by information schema");
+DATA(insert OID = 4032 ( pg_sequence_last_value PGNSP PGUID 12 1 0 0 0 f f f f t f v u 1 0 20 "2205" _null_ _null_ _null_ _null_ _null_ pg_sequence_last_value _null_ _null_ _null_ ));
+DESCR("sequence last value");
DATA(insert OID = 1579 ( varbit_in PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ ));
DESCR("I/O");
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 392a626..6695bbe 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -73,6 +73,7 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
extern Datum lastval(PG_FUNCTION_ARGS);
extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Datum pg_sequence_last_value(PG_FUNCTION_ARGS);
extern ObjectAddress DefineSequence(ParseState *pstate, CreateSeqStmt *stmt);
extern ObjectAddress AlterSequence(ParseState *pstate, AlterSeqStmt *stmt);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 5e2962c..031e8c2 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1615,6 +1615,20 @@ UNION ALL
l.label
FROM (pg_shseclabel l
JOIN pg_authid rol ON (((l.classoid = rol.tableoid) AND (l.objoid = rol.oid))));
+pg_sequences| SELECT n.nspname AS schemaname,
+ c.relname AS sequencename,
+ pg_get_userbyid(c.relowner) AS sequenceowner,
+ p.start_value,
+ p.minimum_value AS min_value,
+ p.maximum_value AS max_value,
+ p.increment AS increment_by,
+ p.cycle_option AS cycle,
+ p.cache_size,
+ pg_sequence_last_value((c.oid)::regclass) AS last_value
+ FROM (pg_class c
+ LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))),
+ LATERAL pg_sequence_parameters(c.oid) p(start_value, minimum_value, maximum_value, increment, cycle_option, cache_size)
+ WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char"));
pg_settings| SELECT a.name,
a.setting,
a.unit,
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index 4ffbe92..5231c27 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -315,6 +315,22 @@ SELECT * FROM information_schema.sequences WHERE sequence_name IN
regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO
(6 rows)
+SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value
+FROM pg_sequences
+WHERE sequencename IN
+ ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
+ 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq')
+ ORDER BY sequencename ASC;
+ schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value
+------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------
+ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5
+ public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+(6 rows)
+
-- Test comments
COMMENT ON SEQUENCE asdf IS 'won''t work';
ERROR: relation "asdf" does not exist
diff --git a/src/test/regress/expected/sequence_1.out b/src/test/regress/expected/sequence_1.out
index 05da2bf..977bf02 100644
--- a/src/test/regress/expected/sequence_1.out
+++ b/src/test/regress/expected/sequence_1.out
@@ -315,6 +315,22 @@ SELECT * FROM information_schema.sequences WHERE sequence_name IN
regression | public | serialtest2_f6_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO
(6 rows)
+SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value
+FROM pg_sequences
+WHERE sequencename IN
+ ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
+ 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq')
+ ORDER BY sequencename ASC;
+ schemaname | sequencename | start_value | min_value | max_value | increment_by | cycle | cache_size | last_value
+------------+--------------------+-------------+-----------+---------------------+--------------+-------+------------+------------
+ public | sequence_test2 | 32 | 5 | 36 | 4 | t | 1 | 5
+ public | serialtest2_f2_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f3_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f4_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f5_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+ public | serialtest2_f6_seq | 1 | 1 | 9223372036854775807 | 1 | f | 1 | 2
+(6 rows)
+
-- Test comments
COMMENT ON SEQUENCE asdf IS 'won''t work';
ERROR: relation "asdf" does not exist
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index 98a2e7d..5333b20 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -144,6 +144,13 @@ CREATE SEQUENCE sequence_test2 START WITH 32;
'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq')
ORDER BY sequence_name ASC;
+SELECT schemaname, sequencename, start_value, min_value, max_value, increment_by, cycle, cache_size, last_value
+FROM pg_sequences
+WHERE sequencename IN
+ ('sequence_test2', 'serialtest2_f2_seq', 'serialtest2_f3_seq',
+ 'serialtest2_f4_seq', 'serialtest2_f5_seq', 'serialtest2_f6_seq')
+ ORDER BY sequencename ASC;
+
-- Test comments
COMMENT ON SEQUENCE asdf IS 'won''t work';
COMMENT ON SEQUENCE sequence_test2 IS 'will work';
--
2.10.2
On 11/10/2016 05:29 AM, Peter Eisentraut wrote:
On 11/8/16 6:43 PM, Andreas Karlsson wrote:
- A worry is that it might get a bit confusing to have both the future
catalog pg_sequence and the view pg_sequences.We already have this in other cases: pg_index/pg_indexes,
pg_user_mapping/pg_user_mappings. It's an established naming system by now.
Then I am fine with it.
- I think it would be useful to include is_cycled in the view.
It's there under the name "cycle".
You are right, my bad, I managed to confuse myself.
- Shouldn't last_value be NULL directly after we have created the
sequence but nobody has called nextval() yet?- I noticed that last_value includes the cached values, but that also
seems to me like the correct thing to do.The documentation now emphasizes that this is the value stored on disk.
This matches what Oracle does.
We also store is_called on disk, I think that if is_called is false then
last_value should be NULL. Either that or we should add is_called.
I will give the patch another review some time this week when i can find
the time.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 11/10/2016 06:27 AM, Andreas Karlsson wrote:
On 11/10/2016 05:29 AM, Peter Eisentraut wrote:
On 11/8/16 6:43 PM, Andreas Karlsson wrote:
- Shouldn't last_value be NULL directly after we have created the
sequence but nobody has called nextval() yet?- I noticed that last_value includes the cached values, but that also
seems to me like the correct thing to do.The documentation now emphasizes that this is the value stored on disk.
This matches what Oracle does.We also store is_called on disk, I think that if is_called is false then
last_value should be NULL. Either that or we should add is_called.I will give the patch another review some time this week when i can find
the time.
Other than my comment above about is_called and last_value I think the
patch looks great.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Review for pg_sequence catalog
I like this change since it moves all the parts which should be
transactional to the system catalogs while keeping the only
non-transactional stuff in the sequence relations.
There was some discussion upthread about more compact representations
for the sequences, but I feel that is a separate issue mostly unrelated
to this patch.
I might play around more with it but it seems to work well so far.
As pointed out by Peter this patch also requires the changes to
pg_upgrade. I have not looked at those patches.
= Functional review
- The patch applies and compiles and seems to work fine after some quick
manual testing.
- The pg_dump tests fails due to the pg_dump code not being updated. I
have attached a patch which fixes this.
= Benchmarks
I was a bit worried that the extra syscache lookups might slow down
nextval(), but I got a measurable speed up on a very simple workload
which consisted of only calls to nextval() to one sequence. The speedup
was about 10% on my machine.
= Code
The changes to the code looks generally good.
@@ -1155,6 +1156,8 @@ doDeletion(const ObjectAddress *object, int flags) else heap_drop_with_catalog(object->objectId); } + if (relKind == RELKIND_SEQUENCE) + DeleteSequenceTuple(object->objectId); break; }
I think it might be cleaner here to put this as a "else if" just like
"relKind == RELKIND_INDEX".
= Documentation
The patch does not update catalogs.sgml which it should do.
Andreas
Attachments:
add-pg_sequence-system-catalog-pg_dump.patchtext/x-patch; name=add-pg_sequence-system-catalog-pg_dump.patchDownload
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index ee1f673..a272ad3 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -15115,7 +15115,27 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE);
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,
"SELECT sequence_name, "
On 11/11/16 12:53 PM, Andreas Karlsson wrote:
Other than my comment above about is_called and last_value I think the
patch looks great.
I have made that change and committed the patch. Thanks.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 11/11/16 10:06 PM, Andreas Karlsson wrote:
As pointed out by Peter this patch also requires the changes to
pg_upgrade. I have not looked at those patches.
The required changes to pg_upgrade have been committed, so that will
work now.
- The pg_dump tests fails due to the pg_dump code not being updated. I
have attached a patch which fixes this.
fixed
I was a bit worried that the extra syscache lookups might slow down
nextval(), but I got a measurable speed up on a very simple workload
which consisted of only calls to nextval() to one sequence. The speedup
was about 10% on my machine.
I have done a fair amount of performance testing and the results were
usually in the neighborhood of 1%-2% slower or faster, nothing really
clear. But running nextval by itself in a tight loop isn't really a
normal use. The important thing is the concurrency behavior.
@@ -1155,6 +1156,8 @@ doDeletion(const ObjectAddress *object, int flags) else heap_drop_with_catalog(object->objectId); } + if (relKind == RELKIND_SEQUENCE) + DeleteSequenceTuple(object->objectId); break; }I think it might be cleaner here to put this as a "else if" just like
"relKind == RELKIND_INDEX".
The sequence tuple has to be deleted in addition to the heap drop. I
have added a comment to make the clearer.
The patch does not update catalogs.sgml which it should do.
added
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
v3-0001-Add-pg_sequence-system-catalog.patchtext/x-patch; name=v3-0001-Add-pg_sequence-system-catalog.patchDownload
From 3a0680ec57e29d5b8b498253975d3451b99a2c8d Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Wed, 30 Nov 2016 12:00:00 -0500
Subject: [PATCH v3] 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.
---
doc/src/sgml/catalogs.sgml | 89 +++++-
src/backend/catalog/Makefile | 2 +-
src/backend/catalog/dependency.c | 6 +
src/backend/catalog/information_schema.sql | 13 +-
src/backend/catalog/system_views.sql | 16 +-
src/backend/commands/sequence.c | 381 +++++++++++++++-----------
src/backend/utils/cache/syscache.c | 12 +
src/bin/pg_dump/pg_dump.c | 22 +-
src/include/catalog/indexing.h | 3 +
src/include/catalog/pg_sequence.h | 30 ++
src/include/commands/sequence.h | 29 +-
src/include/utils/syscache.h | 1 +
src/test/regress/expected/rules.out | 18 +-
src/test/regress/expected/sanity_check.out | 1 +
src/test/regress/expected/sequence.out | 33 ++-
src/test/regress/expected/updatable_views.out | 93 +++----
src/test/regress/sql/sequence.sql | 8 +
src/test/regress/sql/updatable_views.sql | 2 +-
18 files changed, 490 insertions(+), 269 deletions(-)
create mode 100644 src/include/catalog/pg_sequence.h
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 561e228..76d115b 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -256,6 +256,11 @@ <title>System Catalogs</title>
</row>
<row>
+ <entry><link linkend="catalog-pg-sequence"><structname>pg_sequence</structname></link></entry>
+ <entry>information about sequences</entry>
+ </row>
+
+ <row>
<entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
<entry>dependencies on shared objects</entry>
</row>
@@ -1541,7 +1546,8 @@ <title><structname>pg_class</structname></title>
The catalog <structname>pg_class</structname> catalogs tables and most
everything else that has columns or is otherwise similar to a
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</>.
Below, when we mean all of these
kinds of objects we speak of <quote>relations</quote>. Not all
@@ -5453,6 +5459,87 @@ <title><structname>pg_seclabel</structname> Columns</title>
</table>
</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">
<title><structname>pg_shdepend</structname></title>
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 1ce7610..cbf0d79 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -41,7 +41,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.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_collation.h pg_range.h pg_transform.h \
+ pg_collation.h pg_range.h pg_transform.h pg_sequence.h \
toasting.h indexing.h \
)
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index f71d80f..cd88d1a 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -66,6 +66,7 @@
#include "commands/proclang.h"
#include "commands/schemacmds.h"
#include "commands/seclabel.h"
+#include "commands/sequence.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
#include "nodes/nodeFuncs.h"
@@ -1154,6 +1155,11 @@ doDeletion(const ObjectAddress *object, int flags)
else
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;
}
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index 00550eb..182d2d0 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -1535,15 +1535,16 @@ CREATE VIEW sequences AS
CAST(64 AS cardinal_number) AS numeric_precision,
CAST(2 AS cardinal_number) AS numeric_precision_radix,
CAST(0 AS cardinal_number) AS numeric_scale,
- CAST(p.start_value AS character_data) AS start_value,
- CAST(p.minimum_value AS character_data) AS minimum_value,
- CAST(p.maximum_value AS character_data) AS maximum_value,
- CAST(p.increment AS character_data) AS increment,
- CAST(CASE WHEN p.cycle_option 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
+ CAST(s.seqstart AS character_data) AS start_value,
+ CAST(s.seqmin AS character_data) AS minimum_value,
+ CAST(s.seqmax AS character_data) AS maximum_value,
+ CAST(s.seqincrement AS character_data) AS increment,
+ CAST(CASE WHEN s.seqcycle THEN 'YES' ELSE 'NO' END AS yes_or_no) AS cycle_option
+ FROM pg_namespace nc, pg_class c, pg_sequence s
WHERE c.relnamespace = nc.oid
AND c.relkind = 'S'
AND (NOT pg_is_other_temp_schema(nc.oid))
+ AND c.oid = s.seqrelid
AND (pg_has_role(c.relowner, 'USAGE')
OR has_sequence_privilege(c.oid, 'SELECT, UPDATE, USAGE') );
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index e011af1..167b636 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -163,15 +163,15 @@ CREATE OR REPLACE VIEW pg_sequences AS
N.nspname AS schemaname,
C.relname AS sequencename,
pg_get_userbyid(C.relowner) AS sequenceowner,
- p.start_value AS start_value,
- p.minimum_value AS min_value,
- p.maximum_value AS max_value,
- p.increment AS increment_by,
- p.cycle_option AS cycle,
- p.cache_size AS cache_size,
+ S.seqstart AS start_value,
+ S.seqmin AS min_value,
+ S.seqmax AS max_value,
+ S.seqincrement AS increment_by,
+ S.seqcycle AS cycle,
+ S.seqcache AS cache_size,
pg_sequence_last_value(C.oid) AS last_value
- FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace),
- LATERAL pg_sequence_parameters(C.oid) p
+ FROM pg_sequence S JOIN pg_class C ON (C.oid = S.seqrelid)
+ LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE NOT pg_is_other_temp_schema(N.oid)
AND relkind = 'S';
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 7e37108..7727a46 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -22,8 +22,10 @@
#include "access/xloginsert.h"
#include "access/xlogutils.h"
#include "catalog/dependency.h"
+#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
+#include "catalog/pg_sequence.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/sequence.h"
@@ -74,7 +76,7 @@ typedef struct SeqTableData
int64 cached; /* last value already cached for nextval */
/* if last != cached, we have not used up all the cached values */
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;
typedef SeqTableData *SeqTable;
@@ -92,10 +94,11 @@ static int64 nextval_internal(Oid relid);
static Relation open_share_lock(SeqTable seq);
static void create_seq_hashtable(void);
static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
-static Form_pg_sequence read_seq_tuple(SeqTable elm, Relation rel,
- Buffer *buf, HeapTuple seqtuple);
+static Form_pg_sequence_data read_seq_tuple(Relation rel,
+ Buffer *buf, HeapTuple seqdatatuple);
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 process_owned_by(Relation seqrel, List *owned_by);
@@ -107,7 +110,8 @@ static void process_owned_by(Relation seqrel, List *owned_by);
ObjectAddress
DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
{
- FormData_pg_sequence new;
+ FormData_pg_sequence seqform;
+ FormData_pg_sequence_data seqdataform;
List *owned_by;
CreateStmt *stmt = makeNode(CreateStmt);
Oid seqoid;
@@ -117,8 +121,9 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
TupleDesc tupDesc;
Datum value[SEQ_COL_LASTCOL];
bool null[SEQ_COL_LASTCOL];
+ Datum pgs_values[Natts_pg_sequence];
+ bool pgs_nulls[Natts_pg_sequence];
int i;
- NameData name;
/* Unlogged sequences are not implemented -- not clear if useful. */
if (seq->sequence->relpersistence == RELPERSISTENCE_UNLOGGED)
@@ -145,7 +150,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
}
/* 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)
@@ -171,52 +176,16 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
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:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
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";
- 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);
+ value[i - 1] = Int64GetDatumFast(seqdataform.last_value);
break;
case SEQ_COL_LOG:
coldef->typeName = makeTypeNameFromOid(INT8OID, -1);
coldef->colname = "log_cnt";
value[i - 1] = Int64GetDatum((int64) 0);
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:
coldef->typeName = makeTypeNameFromOid(BOOLOID, -1);
coldef->colname = "is_called";
@@ -251,6 +220,27 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq)
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;
}
@@ -271,10 +261,13 @@ ResetSequence(Oid seq_relid)
{
Relation seq_rel;
SeqTable elm;
- Form_pg_sequence seq;
+ Form_pg_sequence_data seq;
Buffer buf;
- HeapTupleData seqtuple;
+ HeapTupleData seqdatatuple;
HeapTuple tuple;
+ HeapTuple pgstuple;
+ Form_pg_sequence pgsform;
+ int64 startv;
/*
* Read the old sequence. This does a bit more work than really
@@ -282,12 +275,19 @@ ResetSequence(Oid seq_relid)
* indeed a sequence.
*/
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.
*/
- tuple = heap_copytuple(&seqtuple);
+ tuple = heap_copytuple(&seqdatatuple);
/* Now we're done with the old page */
UnlockReleaseBuffer(buf);
@@ -296,8 +296,8 @@ ResetSequence(Oid seq_relid)
* Modify the copied tuple to execute the restart (compare the RESTART
* action in AlterSequence)
*/
- seq = (Form_pg_sequence) GETSTRUCT(tuple);
- seq->last_value = seq->start_value;
+ seq = (Form_pg_sequence_data) GETSTRUCT(tuple);
+ seq->last_value = startv;
seq->is_called = false;
seq->log_cnt = 0;
@@ -410,11 +410,14 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
SeqTable elm;
Relation seqrel;
Buffer buf;
- HeapTupleData seqtuple;
- Form_pg_sequence seq;
- FormData_pg_sequence new;
+ HeapTupleData seqdatatuple;
+ Form_pg_sequence seqform;
+ Form_pg_sequence_data seqdata;
+ FormData_pg_sequence_data newseqdata;
List *owned_by;
ObjectAddress address;
+ Relation rel;
+ HeapTuple tuple;
/* Open and lock sequence. */
relid = RangeVarGetRelid(stmt->sequence, AccessShareLock, stmt->missing_ok);
@@ -434,13 +437,22 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
stmt->sequence->relname);
/* 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 */
- 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 */
- 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 */
/* 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 */
START_CRIT_SECTION();
- memcpy(seq, &new, sizeof(FormData_pg_sequence));
+ memcpy(seqdata, &newseqdata, sizeof(FormData_pg_sequence_data));
MarkBufferDirty(buf);
@@ -470,7 +482,7 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
xlrec.node = seqrel->rd_node;
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);
@@ -491,9 +503,30 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt)
relation_close(seqrel, NoLock);
+ simple_heap_update(rel, &tuple->t_self, tuple);
+ CatalogUpdateIndexes(rel, tuple);
+ heap_close(rel, RowExclusiveLock);
+
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
@@ -537,8 +570,10 @@ nextval_internal(Oid relid)
Relation seqrel;
Buffer buf;
Page page;
- HeapTupleData seqtuple;
- Form_pg_sequence seq;
+ HeapTuple pgstuple;
+ Form_pg_sequence pgsform;
+ HeapTupleData seqdatatuple;
+ Form_pg_sequence_data seq;
int64 incby,
maxv,
minv,
@@ -549,6 +584,7 @@ nextval_internal(Oid relid)
int64 result,
next,
rescnt = 0;
+ bool cycle;
bool logit = false;
/* open and AccessShareLock sequence */
@@ -582,15 +618,24 @@ nextval_internal(Oid relid)
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 */
- seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+ seq = read_seq_tuple(seqrel, &buf, &seqdatatuple);
page = BufferGetPage(buf);
+ elm->increment = incby;
last = next = result = seq->last_value;
- incby = seq->increment_by;
- maxv = seq->max_value;
- minv = seq->min_value;
- fetch = cache = seq->cache_value;
+ fetch = cache;
log = seq->log_cnt;
if (!seq->is_called)
@@ -641,7 +686,7 @@ nextval_internal(Oid relid)
{
if (rescnt > 0)
break; /* stop fetching */
- if (!seq->is_cycled)
+ if (!cycle)
{
char buf[100];
@@ -664,7 +709,7 @@ nextval_internal(Oid relid)
{
if (rescnt > 0)
break; /* stop fetching */
- if (!seq->is_cycled)
+ if (!cycle)
{
char buf[100];
@@ -747,7 +792,7 @@ nextval_internal(Oid relid)
xlrec.node = seqrel->rd_node;
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);
@@ -853,8 +898,12 @@ do_setval(Oid relid, int64 next, bool iscalled)
SeqTable elm;
Relation seqrel;
Buffer buf;
- HeapTupleData seqtuple;
- Form_pg_sequence seq;
+ HeapTupleData seqdatatuple;
+ Form_pg_sequence_data seq;
+ HeapTuple pgstuple;
+ Form_pg_sequence pgsform;
+ int64 maxv,
+ minv;
/* open and AccessShareLock sequence */
init_sequence(relid, &elm, &seqrel);
@@ -865,6 +914,14 @@ do_setval(Oid relid, int64 next, bool iscalled)
errmsg("permission denied for sequence %s",
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 */
if (!seqrel->rd_islocaltemp)
PreventCommandIfReadOnly("setval()");
@@ -877,17 +934,17 @@ do_setval(Oid relid, int64 next, bool iscalled)
PreventCommandIfParallelMode("setval()");
/* 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],
bufm[100],
bufx[100];
snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);
- snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, minv);
+ snprintf(bufx, sizeof(bufx), INT64_FORMAT, maxv);
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
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;
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);
@@ -1064,7 +1121,7 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
elm->filenode = InvalidOid;
elm->lxid = InvalidLocalTransactionId;
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
*
* *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)
*
* Function's return value points to the data payload of the tuple
*/
-static Form_pg_sequence
-read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
+static Form_pg_sequence_data
+read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple)
{
Page page;
ItemId lp;
sequence_magic *sm;
- Form_pg_sequence seq;
+ Form_pg_sequence_data seq;
*buf = ReadBuffer(rel, 0);
LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
@@ -1125,9 +1182,9 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
lp = PageGetItemId(page, FirstOffsetNumber);
Assert(ItemIdIsNormal(lp));
- /* Note we currently only bother to set these two fields of *seqtuple */
- seqtuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
- seqtuple->t_len = ItemIdGetLength(lp);
+ /* Note we currently only bother to set these two fields of *seqdatatuple */
+ seqdatatuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ seqdatatuple->t_len = ItemIdGetLength(lp);
/*
* 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
* this again if the update gets lost.
*/
- Assert(!(seqtuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
- if (HeapTupleHeaderGetRawXmax(seqtuple->t_data) != InvalidTransactionId)
+ Assert(!(seqdatatuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
+ if (HeapTupleHeaderGetRawXmax(seqdatatuple->t_data) != InvalidTransactionId)
{
- HeapTupleHeaderSetXmax(seqtuple->t_data, InvalidTransactionId);
- seqtuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
- seqtuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
+ HeapTupleHeaderSetXmax(seqdatatuple->t_data, InvalidTransactionId);
+ seqdatatuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+ seqdatatuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
MarkBufferDirtyHint(*buf, true);
}
- seq = (Form_pg_sequence) GETSTRUCT(seqtuple);
-
- /* this is a handy place to update our copy of the increment */
- elm->increment = seq->increment_by;
+ seq = (Form_pg_sequence_data) GETSTRUCT(seqdatatuple);
return seq;
}
@@ -1164,7 +1218,8 @@ read_seq_tuple(SeqTable elm, Relation rel, Buffer *buf, HeapTuple seqtuple)
*/
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)
{
DefElem *start_value = NULL;
DefElem *restart_value = NULL;
@@ -1263,69 +1318,69 @@ init_params(ParseState *pstate, List *options, bool isInit,
* would affect future nextval allocations.
*/
if (isInit)
- new->log_cnt = 0;
+ seqdataform->log_cnt = 0;
/* INCREMENT BY */
if (increment_by != NULL)
{
- new->increment_by = defGetInt64(increment_by);
- if (new->increment_by == 0)
+ seqform->seqincrement = defGetInt64(increment_by);
+ if (seqform->seqincrement == 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("INCREMENT must not be zero")));
- new->log_cnt = 0;
+ seqdataform->log_cnt = 0;
}
else if (isInit)
- new->increment_by = 1;
+ seqform->seqincrement = 1;
/* CYCLE */
if (is_cycled != NULL)
{
- new->is_cycled = intVal(is_cycled->arg);
- Assert(BoolIsValid(new->is_cycled));
- new->log_cnt = 0;
+ seqform->seqcycle = intVal(is_cycled->arg);
+ Assert(BoolIsValid(seqform->seqcycle));
+ seqdataform->log_cnt = 0;
}
else if (isInit)
- new->is_cycled = false;
+ seqform->seqcycle = false;
/* MAXVALUE (null arg means NO MAXVALUE) */
if (max_value != NULL && max_value->arg)
{
- new->max_value = defGetInt64(max_value);
- new->log_cnt = 0;
+ seqform->seqmax = defGetInt64(max_value);
+ seqdataform->log_cnt = 0;
}
else if (isInit || max_value != NULL)
{
- if (new->increment_by > 0)
- new->max_value = SEQ_MAXVALUE; /* ascending seq */
+ if (seqform->seqincrement > 0)
+ seqform->seqmax = SEQ_MAXVALUE; /* ascending seq */
else
- new->max_value = -1; /* descending seq */
- new->log_cnt = 0;
+ seqform->seqmax = -1; /* descending seq */
+ seqdataform->log_cnt = 0;
}
/* MINVALUE (null arg means NO MINVALUE) */
if (min_value != NULL && min_value->arg)
{
- new->min_value = defGetInt64(min_value);
- new->log_cnt = 0;
+ seqform->seqmin = defGetInt64(min_value);
+ seqdataform->log_cnt = 0;
}
else if (isInit || min_value != NULL)
{
- if (new->increment_by > 0)
- new->min_value = 1; /* ascending seq */
+ if (seqform->seqincrement > 0)
+ seqform->seqmin = 1; /* ascending seq */
else
- new->min_value = SEQ_MINVALUE; /* descending seq */
- new->log_cnt = 0;
+ seqform->seqmin = SEQ_MINVALUE; /* descending seq */
+ seqdataform->log_cnt = 0;
}
/* crosscheck min/max */
- if (new->min_value >= new->max_value)
+ if (seqform->seqmin >= seqform->seqmax)
{
char bufm[100],
bufx[100];
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
- snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin);
+ snprintf(bufx, sizeof(bufx), INT64_FORMAT, seqform->seqmax);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("MINVALUE (%s) must be less than MAXVALUE (%s)",
@@ -1334,35 +1389,35 @@ init_params(ParseState *pstate, List *options, bool isInit,
/* START WITH */
if (start_value != NULL)
- new->start_value = defGetInt64(start_value);
+ seqform->seqstart = defGetInt64(start_value);
else if (isInit)
{
- if (new->increment_by > 0)
- new->start_value = new->min_value; /* ascending seq */
+ if (seqform->seqincrement > 0)
+ seqform->seqstart = seqform->seqmin; /* ascending seq */
else
- new->start_value = new->max_value; /* descending seq */
+ seqform->seqstart = seqform->seqmax; /* descending seq */
}
/* crosscheck START */
- if (new->start_value < new->min_value)
+ if (seqform->seqstart < seqform->seqmin)
{
char bufs[100],
bufm[100];
- snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqform->seqstart);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin);
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)
+ if (seqform->seqstart > seqform->seqmax)
{
char bufs[100],
bufm[100];
- snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqform->seqstart);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmax);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
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->arg != NULL)
- new->last_value = defGetInt64(restart_value);
+ seqdataform->last_value = defGetInt64(restart_value);
else
- new->last_value = new->start_value;
- new->is_called = false;
- new->log_cnt = 0;
+ seqdataform->last_value = seqform->seqstart;
+ seqdataform->is_called = false;
+ seqdataform->log_cnt = 0;
}
else if (isInit)
{
- new->last_value = new->start_value;
- new->is_called = false;
+ seqdataform->last_value = seqform->seqstart;
+ seqdataform->is_called = false;
}
/* 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],
bufm[100];
- snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqdataform->last_value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmin);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
bufs, bufm)));
}
- if (new->last_value > new->max_value)
+ if (seqdataform->last_value > seqform->seqmax)
{
char bufs[100],
bufm[100];
- snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
- snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, seqdataform->last_value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, seqform->seqmax);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
@@ -1414,21 +1469,21 @@ init_params(ParseState *pstate, List *options, bool isInit,
/* CACHE */
if (cache_value != NULL)
{
- new->cache_value = defGetInt64(cache_value);
- if (new->cache_value <= 0)
+ seqform->seqcache = defGetInt64(cache_value);
+ if (seqform->seqcache <= 0)
{
char buf[100];
- snprintf(buf, sizeof(buf), INT64_FORMAT, new->cache_value);
+ snprintf(buf, sizeof(buf), INT64_FORMAT, seqform->seqcache);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("CACHE (%s) must be greater than zero",
buf)));
}
- new->log_cnt = 0;
+ seqdataform->log_cnt = 0;
}
else if (isInit)
- new->cache_value = 1;
+ seqform->seqcache = 1;
}
/*
@@ -1527,7 +1582,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
pg_sequence_parameters(PG_FUNCTION_ARGS)
@@ -1536,20 +1591,14 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
TupleDesc tupdesc;
Datum values[6];
bool isnull[6];
- SeqTable elm;
- Relation seqrel;
- Buffer buf;
- HeapTupleData seqtuple;
- Form_pg_sequence seq;
-
- /* open and AccessShareLock sequence */
- init_sequence(relid, &elm, &seqrel);
+ HeapTuple pgstuple;
+ Form_pg_sequence pgsform;
if (pg_class_aclcheck(relid, GetUserId(), ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied for sequence %s",
- RelationGetRelationName(seqrel))));
+ get_rel_name(relid))));
tupdesc = CreateTemplateTupleDesc(6, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "start_value",
@@ -1569,17 +1618,19 @@ pg_sequence_parameters(PG_FUNCTION_ARGS)
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[1] = Int64GetDatum(seq->min_value);
- values[2] = Int64GetDatum(seq->max_value);
- values[3] = Int64GetDatum(seq->increment_by);
- values[4] = BoolGetDatum(seq->is_cycled);
- values[5] = Int64GetDatum(seq->cache_value);
+ values[0] = Int64GetDatum(pgsform->seqstart);
+ values[1] = Int64GetDatum(pgsform->seqmin);
+ values[2] = Int64GetDatum(pgsform->seqmax);
+ values[3] = Int64GetDatum(pgsform->seqincrement);
+ values[4] = BoolGetDatum(pgsform->seqcycle);
+ values[5] = Int64GetDatum(pgsform->seqcache);
- UnlockReleaseBuffer(buf);
- relation_close(seqrel, NoLock);
+ ReleaseSysCache(pgstuple);
return HeapTupleGetDatum(heap_form_tuple(tupdesc, values, isnull));
}
@@ -1597,7 +1648,7 @@ pg_sequence_last_value(PG_FUNCTION_ARGS)
Relation seqrel;
Buffer buf;
HeapTupleData seqtuple;
- Form_pg_sequence seq;
+ Form_pg_sequence_data seq;
bool is_called;
int64 result;
@@ -1610,7 +1661,7 @@ pg_sequence_last_value(PG_FUNCTION_ARGS)
errmsg("permission denied for sequence %s",
RelationGetRelationName(seqrel))));
- seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+ seq = read_seq_tuple(seqrel, &buf, &seqtuple);
is_called = seq->is_called;
result = seq->last_value;
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 65ffe84..1a6205b 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -52,6 +52,7 @@
#include "catalog/pg_range.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_seclabel.h"
+#include "catalog/pg_sequence.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_shseclabel.h"
@@ -656,6 +657,17 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {SequenceRelationId, /* SEQRELID */
+ SequenceRelidIndexId,
+ 1,
+ {
+ Anum_pg_sequence_seqrelid,
+ 0,
+ 0,
+ 0
+ },
+ 32
+ },
{StatisticRelationId, /* STATRELATTINH */
StatisticRelidAttnumInhIndexId,
3,
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 2ff60b9..ad607a1 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -15175,7 +15175,27 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
snprintf(bufm, sizeof(bufm), INT64_FORMAT, SEQ_MINVALUE);
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,
"SELECT sequence_name, "
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ca5eb3d..3ef880e 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -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));
#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));
#define SharedDependDependerIndexId 1232
DECLARE_INDEX(pg_shdepend_reference_index, 1233, on pg_shdepend using btree(refclassid oid_ops, refobjid oid_ops));
diff --git a/src/include/catalog/pg_sequence.h b/src/include/catalog/pg_sequence.h
new file mode 100644
index 0000000..3bcda6b
--- /dev/null
+++ b/src/include/catalog/pg_sequence.h
@@ -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 */
diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h
index 6695bbe..1fd75b2 100644
--- a/src/include/commands/sequence.h
+++ b/src/include/commands/sequence.h
@@ -22,38 +22,24 @@
#include "storage/relfilenode.h"
-typedef struct FormData_pg_sequence
+typedef struct FormData_pg_sequence_data
{
- NameData sequence_name;
int64 last_value;
- int64 start_value;
- int64 increment_by;
- int64 max_value;
- int64 min_value;
- int64 cache_value;
int64 log_cnt;
- bool is_cycled;
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
*/
-#define SEQ_COL_NAME 1
-#define SEQ_COL_LASTVAL 2
-#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_LASTVAL 1
+#define SEQ_COL_LOG 2
+#define SEQ_COL_CALLED 3
-#define SEQ_COL_FIRSTCOL SEQ_COL_NAME
+#define SEQ_COL_FIRSTCOL SEQ_COL_LASTVAL
#define SEQ_COL_LASTCOL SEQ_COL_CALLED
/* XLOG stuff */
@@ -77,6 +63,7 @@ extern Datum pg_sequence_last_value(PG_FUNCTION_ARGS);
extern ObjectAddress DefineSequence(ParseState *pstate, CreateSeqStmt *stmt);
extern ObjectAddress AlterSequence(ParseState *pstate, AlterSeqStmt *stmt);
+extern void DeleteSequenceTuple(Oid relid);
extern void ResetSequence(Oid seq_relid);
extern void ResetSequenceCaches(void);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 256615b..9c52be4 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -80,6 +80,7 @@ enum SysCacheIdentifier
REPLORIGIDENT,
REPLORIGNAME,
RULERELNAME,
+ SEQRELID,
STATRELATTINH,
TABLESPACEOID,
TRFOID,
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 031e8c2..7d93125 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1618,16 +1618,16 @@ UNION ALL
pg_sequences| SELECT n.nspname AS schemaname,
c.relname AS sequencename,
pg_get_userbyid(c.relowner) AS sequenceowner,
- p.start_value,
- p.minimum_value AS min_value,
- p.maximum_value AS max_value,
- p.increment AS increment_by,
- p.cycle_option AS cycle,
- p.cache_size,
+ s.seqstart AS start_value,
+ s.seqmin AS min_value,
+ s.seqmax AS max_value,
+ s.seqincrement AS increment_by,
+ s.seqcycle AS cycle,
+ s.seqcache AS cache_size,
pg_sequence_last_value((c.oid)::regclass) AS last_value
- FROM (pg_class c
- LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))),
- LATERAL pg_sequence_parameters(c.oid) p(start_value, minimum_value, maximum_value, increment, cycle_option, cache_size)
+ FROM ((pg_sequence s
+ JOIN pg_class c ON ((c.oid = s.seqrelid)))
+ LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace)))
WHERE ((NOT pg_is_other_temp_schema(n.oid)) AND (c.relkind = 'S'::"char"));
pg_settings| SELECT a.name,
a.setting,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index b1ebcf6..58edf2e 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -127,6 +127,7 @@ pg_range|t
pg_replication_origin|t
pg_rewrite|t
pg_seclabel|t
+pg_sequence|t
pg_shdepend|t
pg_shdescription|t
pg_shseclabel|t
diff --git a/src/test/regress/expected/sequence.out b/src/test/regress/expected/sequence.out
index c5413e0..a2bdd30 100644
--- a/src/test/regress/expected/sequence.out
+++ b/src/test/regress/expected/sequence.out
@@ -173,9 +173,9 @@ DROP SEQUENCE sequence_test;
CREATE SEQUENCE foo_seq;
ALTER TABLE foo_seq RENAME TO 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
----------------+------------+-------------+--------------+---------------------+-----------+-------------+---------+-----------+-----------
- foo_seq | 1 | 1 | 1 | 9223372036854775807 | 1 | 1 | 0 | f | f
+ last_value | log_cnt | is_called
+------------+---------+-----------
+ 1 | 0 | f
(1 row)
SELECT nextval('foo_seq_new');
@@ -191,9 +191,9 @@ SELECT nextval('foo_seq_new');
(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 | 31 | f | t
+ last_value | log_cnt | is_called
+------------+---------+-----------
+ 2 | 31 | t
(1 row)
DROP SEQUENCE foo_seq_new;
@@ -536,3 +536,24 @@ SELECT * FROM information_schema.sequences WHERE sequence_name IN
DROP USER regress_seq_user;
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;
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 79ddbde..8201d77 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -86,55 +86,48 @@ SELECT table_name, column_name, is_updatable
FROM information_schema.columns
WHERE table_name LIKE E'r_\\_view%'
ORDER BY table_name, ordinal_position;
- table_name | column_name | is_updatable
-------------+---------------+--------------
- ro_view1 | a | NO
- ro_view1 | b | NO
- ro_view10 | a | NO
- ro_view11 | a | NO
- ro_view11 | b | NO
- ro_view12 | a | NO
- ro_view13 | a | NO
- ro_view13 | b | NO
- ro_view17 | a | NO
- ro_view17 | b | NO
- ro_view18 | a | NO
- ro_view19 | sequence_name | NO
- ro_view19 | last_value | NO
- ro_view19 | start_value | NO
- ro_view19 | increment_by | NO
- ro_view19 | max_value | NO
- ro_view19 | min_value | NO
- ro_view19 | cache_value | NO
- ro_view19 | log_cnt | NO
- ro_view19 | is_cycled | NO
- ro_view19 | is_called | NO
- ro_view2 | a | NO
- ro_view2 | b | NO
- ro_view20 | a | NO
- ro_view20 | b | NO
- ro_view20 | g | NO
- ro_view3 | ?column? | NO
- ro_view4 | count | NO
- ro_view5 | a | NO
- ro_view5 | rank | NO
- ro_view6 | a | NO
- ro_view6 | b | NO
- ro_view7 | a | NO
- ro_view7 | b | NO
- ro_view8 | a | NO
- ro_view8 | b | NO
- ro_view9 | a | NO
- ro_view9 | b | NO
- rw_view14 | ctid | NO
- rw_view14 | a | YES
- 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)
+ table_name | column_name | is_updatable
+------------+-------------+--------------
+ ro_view1 | a | NO
+ ro_view1 | b | NO
+ ro_view10 | a | NO
+ ro_view11 | a | NO
+ ro_view11 | b | NO
+ ro_view12 | a | NO
+ ro_view13 | a | NO
+ ro_view13 | b | NO
+ ro_view17 | a | NO
+ ro_view17 | b | NO
+ ro_view18 | a | NO
+ ro_view19 | last_value | NO
+ ro_view19 | log_cnt | NO
+ ro_view19 | is_called | NO
+ ro_view2 | a | NO
+ ro_view2 | b | NO
+ ro_view20 | a | NO
+ ro_view20 | b | NO
+ ro_view20 | g | NO
+ ro_view3 | ?column? | NO
+ ro_view4 | count | NO
+ ro_view5 | a | NO
+ ro_view5 | rank | NO
+ ro_view6 | a | NO
+ ro_view6 | b | NO
+ ro_view7 | a | NO
+ ro_view7 | b | NO
+ ro_view8 | a | NO
+ ro_view8 | b | NO
+ ro_view9 | a | NO
+ ro_view9 | b | NO
+ rw_view14 | ctid | NO
+ rw_view14 | a | YES
+ rw_view14 | b | YES
+ rw_view15 | a | YES
+ rw_view15 | upper | NO
+ rw_view16 | a | YES
+ rw_view16 | b | YES
+ rw_view16 | aa | YES
+(39 rows)
-- Read-only views
DELETE FROM ro_view1;
@@ -327,7 +320,7 @@ DELETE FROM 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.
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"
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.
diff --git a/src/test/regress/sql/sequence.sql b/src/test/regress/sql/sequence.sql
index e2b6b63..a79330e 100644
--- a/src/test/regress/sql/sequence.sql
+++ b/src/test/regress/sql/sequence.sql
@@ -273,3 +273,11 @@ CREATE SEQUENCE seq3;
DROP USER regress_seq_user;
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;
diff --git a/src/test/regress/sql/updatable_views.sql b/src/test/regress/sql/updatable_views.sql
index 03c3f9d..f008479 100644
--- a/src/test/regress/sql/updatable_views.sql
+++ b/src/test/regress/sql/updatable_views.sql
@@ -95,7 +95,7 @@ CREATE VIEW ro_view20 AS SELECT a, b, generate_series(1, a) g FROM base_tbl; --
-- Read-only views
INSERT INTO ro_view17 VALUES (3, 'ROW 3');
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);
DROP TABLE base_tbl CASCADE;
--
2.10.2
I think this patch looks good now so I am setting it to ready for committer.
I like the idea of the patch and I think that while this change will
break some tools which look at the sequence relations I think the
advantages are worth it (for example making more sequence DDL respecting
MVCC).
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Fri, Dec 2, 2016 at 1:47 PM, Andreas Karlsson <andreas@proxel.se> wrote:
I think this patch looks good now so I am setting it to ready for
committer.I like the idea of the patch and I think that while this change will break
some tools which look at the sequence relations I think the advantages are
worth it (for example making more sequence DDL respecting MVCC).
Moved to next CF with the same status (ready for committer).
Regards,
Hari Babu
Fujitsu Australia
On 12/1/16 9:47 PM, Andreas Karlsson wrote:
I think this patch looks good now so I am setting it to ready for committer.
committed, thanks
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Tue, Dec 20, 2016 at 7:14 PM, Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:
On 12/1/16 9:47 PM, Andreas Karlsson wrote:
I think this patch looks good now so I am setting it to ready for committer.
committed, thanks
The regression tests for hot standby check fails since it uses the
following statement:
-select min_value as sequence_min_value from hsseq;
which is no longer supported I guess. It should be modified as following:
select min_value as sequence_min_value from pg_sequences where
sequencename = 'hsseq';
Attached is a patch which reflects the above changes.
--
Thanks & Regards,
Kuntal Ghosh
EnterpriseDB: http://www.enterprisedb.com
Attachments:
fix_sequence_test_in_standbycheck.sqltext/x-sql; charset=US-ASCII; name=fix_sequence_test_in_standbycheck.sqlDownload
diff --git a/src/test/regress/expected/hs_standby_allowed.out b/src/test/regress/expected/hs_standby_allowed.out
index c26c982..487b30e 100644
--- a/src/test/regress/expected/hs_standby_allowed.out
+++ b/src/test/regress/expected/hs_standby_allowed.out
@@ -26,7 +26,7 @@ COPY hs1 TO '/tmp/copy_test';
\! cat /tmp/copy_test
1
-- Access sequence directly
-select min_value as sequence_min_value from hsseq;
+select min_value as sequence_min_value from pg_sequences where sequencename = 'hsseq';
sequence_min_value
--------------------
1
diff --git a/src/test/regress/sql/hs_standby_allowed.sql b/src/test/regress/sql/hs_standby_allowed.sql
index 7fc2214..26fb65c 100644
--- a/src/test/regress/sql/hs_standby_allowed.sql
+++ b/src/test/regress/sql/hs_standby_allowed.sql
@@ -16,7 +16,7 @@ COPY hs1 TO '/tmp/copy_test';
\! cat /tmp/copy_test
-- Access sequence directly
-select min_value as sequence_min_value from hsseq;
+select min_value as sequence_min_value from pg_sequences where sequencename = 'hsseq';
-- Transactions
On 1/3/17 7:23 AM, Kuntal Ghosh wrote:
The regression tests for hot standby check fails since it uses the
following statement:
-select min_value as sequence_min_value from hsseq;
which is no longer supported I guess. It should be modified as following:
select min_value as sequence_min_value from pg_sequences where
sequencename = 'hsseq';Attached is a patch which reflects the above changes.
Fixed, thanks.
I made a note to self to port this test to the TAP framework.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 01/03/2017 03:30 PM, Peter Eisentraut wrote:
On 1/3/17 7:23 AM, Kuntal Ghosh wrote:
The regression tests for hot standby check fails since it uses the
following statement:
-select min_value as sequence_min_value from hsseq;
which is no longer supported I guess. It should be modified as following:
select min_value as sequence_min_value from pg_sequences where
sequencename = 'hsseq';Attached is a patch which reflects the above changes.
Fixed, thanks.
I made a note to self to port this test to the TAP framework.
Hm, doesn't this change the intent of the test case? As I read the test
it seems to make sure that we are allowed to do a read from a sequence
relation on the slave. If so I think it should be changed to something
like the below.
select is_called from hsseq;
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 1/3/17 1:17 PM, Andreas Karlsson wrote:
Hm, doesn't this change the intent of the test case? As I read the test
it seems to make sure that we are allowed to do a read from a sequence
relation on the slave. If so I think it should be changed to something
like the below.select is_called from hsseq;
You're right. Fixed that way.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers