From e8647c45d66ab61bc36ae254f10727ac30e5344f Mon Sep 17 00:00:00 2001 From: "Vadim B. Mikheev" Date: Wed, 2 Apr 1997 03:51:23 +0000 Subject: [PATCH] Sequence numbers generators code. --- src/backend/commands/Makefile | 4 +- src/backend/commands/sequence.c | 552 ++++++++++++++++++++++++++++++++ 2 files changed, 554 insertions(+), 2 deletions(-) create mode 100644 src/backend/commands/sequence.c diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile index 2886af835d..036ece80d1 100644 --- a/src/backend/commands/Makefile +++ b/src/backend/commands/Makefile @@ -4,7 +4,7 @@ # Makefile for commands # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.2 1996/11/03 23:57:17 scrappy Exp $ +# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.3 1997/04/02 03:51:23 vadim Exp $ # #------------------------------------------------------------------------- @@ -19,7 +19,7 @@ CFLAGS+=$(INCLUDE_OPT) OBJS = async.o creatinh.o command.o copy.o defind.o define.o \ purge.o remove.o rename.o vacuum.o version.o view.o cluster.o \ - recipe.o explain.o + recipe.o explain.o sequence.o all: SUBSYS.o diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c new file mode 100644 index 0000000000..7ebba3eace --- /dev/null +++ b/src/backend/commands/sequence.c @@ -0,0 +1,552 @@ +/*------------------------------------------------------------------------- + * + * sequence.c-- + * PostgreSQL sequences support code. + * + *------------------------------------------------------------------------- + */ +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SEQ_MAGIC 0x1717 + +#define SEQ_MAXVALUE ((int4)0x7FFFFFFF) +#define SEQ_MINVALUE -(SEQ_MAXVALUE) + +bool ItsSequenceCreation = false; + +typedef struct FormData_pg_sequence { + NameData sequence_name; + int4 last_value; + int4 increment_by; + int4 max_value; + int4 min_value; + int4 cache_value; + char is_cycled; + char is_called; +} FormData_pg_sequence; + +typedef FormData_pg_sequence *SequenceTupleForm; + +typedef struct sequence_magic { + uint32 magic; +} sequence_magic; + +typedef struct SeqTableData { + char *name; + Oid relid; + Relation rel; + int4 cached; + int4 last; + int4 increment; + struct SeqTableData *next; +} SeqTableData; + +typedef SeqTableData *SeqTable; + +static SeqTable seqtab = NULL; + +static SeqTable init_sequence (char *caller, char *name); +static SequenceTupleForm read_info (char * caller, SeqTable elm, Buffer * buf); +static void init_params (CreateSeqStmt *seq, SequenceTupleForm new); +static int get_param (DefElem *def); + +/* + * DefineSequence -- + * Creates a new sequence relation + */ +void +DefineSequence (CreateSeqStmt *seq) +{ + FormData_pg_sequence new; + CreateStmt *stmt = makeNode (CreateStmt); + ColumnDef *coldef; + TypeName *typnam; + Relation rel; + Buffer buf; + PageHeader page; + sequence_magic *sm; + HeapTuple tuple; + TupleDesc tupDesc; + Datum value[SEQ_COL_LASTCOL]; + char null[SEQ_COL_LASTCOL]; + int i; + + /* Check and set values */ + init_params (seq, &new); + + /* + * Create relation (and fill null[] & value[]) + */ + stmt->tableElts = NIL; + for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++) + { + typnam = makeNode(TypeName); + typnam->setof = FALSE; + typnam->arrayBounds = NULL; + coldef = makeNode(ColumnDef); + coldef->typename = typnam; + null[i-1] = ' '; + + switch (i) + { + case SEQ_COL_NAME: + typnam->name = "name"; + coldef->colname = "sequence_name"; + value[i-1] = PointerGetDatum (seq->seqname); + break; + case SEQ_COL_LASTVAL: + typnam->name = "int4"; + coldef->colname = "last_value"; + value[i-1] = Int32GetDatum (new.last_value); + break; + case SEQ_COL_INCBY: + typnam->name = "int4"; + coldef->colname = "increment_by"; + value[i-1] = Int32GetDatum (new.increment_by); + break; + case SEQ_COL_MAXVALUE: + typnam->name = "int4"; + coldef->colname = "max_value"; + value[i-1] = Int32GetDatum (new.max_value); + break; + case SEQ_COL_MINVALUE: + typnam->name = "int4"; + coldef->colname = "min_value"; + value[i-1] = Int32GetDatum (new.min_value); + break; + case SEQ_COL_CACHE: + typnam->name = "int4"; + coldef->colname = "cache_value"; + value[i-1] = Int32GetDatum (new.cache_value); + break; + case SEQ_COL_CYCLE: + typnam->name = "char"; + coldef->colname = "is_cycled"; + value[i-1] = CharGetDatum (new.is_cycled); + break; + case SEQ_COL_CALLED: + typnam->name = "char"; + coldef->colname = "is_called"; + value[i-1] = CharGetDatum ('f'); + break; + } + stmt->tableElts = lappend (stmt->tableElts, coldef); + } + + stmt->relname = seq->seqname; + stmt->archiveLoc = -1; /* default */ + stmt->archiveType = ARCH_NONE; + stmt->inhRelnames = NIL; + + ItsSequenceCreation = true; /* hack */ + + DefineRelation (stmt); + + /* Xact abort calls CloseSequences, which turns ItsSequenceCreation off */ + ItsSequenceCreation = false; /* hack */ + + rel = heap_openr (seq->seqname); + Assert ( RelationIsValid (rel) ); + + RelationSetLockForWrite (rel); + + tupDesc = RelationGetTupleDescriptor(rel); + + Assert ( RelationGetNumberOfBlocks (rel) == 0 ); + buf = ReadBuffer (rel, P_NEW); + + if ( !BufferIsValid (buf) ) + elog (WARN, "DefineSequence: ReadBuffer failed"); + + page = (PageHeader) BufferGetPage (buf); + + PageInit((Page)page, BufferGetPageSize(buf), sizeof(sequence_magic)); + sm = (sequence_magic *) PageGetSpecialPointer (page); + sm->magic = SEQ_MAGIC; + + /* Now - form & insert sequence tuple */ + tuple = heap_formtuple (tupDesc, value, null); + heap_insert (rel, tuple); + + if ( WriteBuffer (buf) == STATUS_ERROR ) + elog (WARN, "DefineSequence: WriteBuffer failed"); + + RelationUnsetLockForWrite (rel); + heap_close (rel); + + return; + +} + + +int4 +nextval (struct varlena * seqin) +{ + char *seqname = textout(seqin); + SeqTable elm; + Buffer buf; + SequenceTupleForm seq; + ItemPointerData iptr; + int4 incby, maxv, minv, cache; + int4 result, next, rescnt = 0; + + /* open and WIntentLock sequence */ + elm = init_sequence ("nextval", seqname); + + if ( elm->last != elm->cached ) /* some numbers were cached */ + { + elm->last += elm->increment; + return (elm->last); + } + + seq = read_info ("nextval", elm, &buf); /* lock page and read tuple */ + + next = result = seq->last_value; + incby = seq->increment_by; + maxv = seq->max_value; + minv = seq->min_value; + cache = seq->cache_value; + + if ( seq->is_called != 't' ) + rescnt++; /* last_value if not called */ + + while ( rescnt < cache ) /* try to fetch cache numbers */ + { + /* + * Check MAXVALUE for ascending sequences + * and MINVALUE for descending sequences + */ + if ( incby > 0 ) /* ascending sequence */ + { + if ( ( maxv >= 0 && next > maxv - incby) || + ( maxv < 0 && next + incby > maxv ) ) + { + if ( rescnt > 0 ) + break; /* stop caching */ + if ( seq->is_cycled != 't' ) + elog (WARN, "%s.nextval: got MAXVALUE (%d)", + seqname, maxv); + next = minv; + } + else + next += incby; + } + else /* descending sequence */ + { + if ( ( minv < 0 && next < minv - incby ) || + ( minv >= 0 && next + incby < minv ) ) + { + if ( rescnt > 0 ) + break; /* stop caching */ + if ( seq->is_cycled != 't' ) + elog (WARN, "%s.nextval: got MINVALUE (%d)", + seqname, minv); + next = maxv; + } + else + next += incby; + } + rescnt++; /* got result */ + if ( rescnt == 1 ) /* if it's first one - */ + result = next; /* it's what to return */ + } + + /* save info in local cache */ + elm->last = result; /* last returned number */ + elm->cached = next; /* last cached number */ + + /* save info in sequence relation */ + seq->last_value = next; /* last fetched number */ + seq->is_called = 't'; + + if ( WriteBuffer (buf) == STATUS_ERROR ) + elog (WARN, "%s.nextval: WriteBuffer failed", elm->name); + + ItemPointerSet(&iptr, 0, FirstOffsetNumber); + RelationUnsetSingleWLockPage (elm->rel, &iptr); + + return (result); + +} + + +int4 +currval (struct varlena * seqin) +{ + char *seqname = textout(seqin); + SeqTable elm; + Buffer buf; + SequenceTupleForm seq; + ItemPointerData iptr; + int4 result; + + /* open and WIntentLock sequence */ + elm = init_sequence ("currval", seqname); + + if ( elm->last != elm->cached ) /* some numbers were cached */ + { + return (elm->last); /* return last returned by nextval */ + } + + seq = read_info ("currval", elm, &buf); + + if ( seq->is_called != 't' ) + { + elog (WARN, "%s.currval: yet undefined (%s.nextval never called)", + seqname, seqname); + } + result = seq->last_value; + + if ( ReleaseBuffer (buf) == STATUS_ERROR ) + elog (WARN, "%s.currval: ReleaseBuffer failed", seqname); + + ItemPointerSet(&iptr, 0, FirstOffsetNumber); + RelationUnsetSingleWLockPage (elm->rel, &iptr); + + return (result); + +} + +static SequenceTupleForm +read_info (char * caller, SeqTable elm, Buffer * buf) +{ + ItemPointerData iptr; + PageHeader page; + ItemId lp; + HeapTuple tuple; + sequence_magic *sm; + SequenceTupleForm seq; + + ItemPointerSet(&iptr, 0, FirstOffsetNumber); + RelationSetSingleWLockPage (elm->rel, &iptr); + + if ( RelationGetNumberOfBlocks (elm->rel) != 1 ) + elog (WARN, "%s.%s: invalid number of blocks in sequence", + elm->name, caller); + + *buf = ReadBuffer (elm->rel, 0); + if ( !BufferIsValid (*buf) ) + elog (WARN, "%s.%s: ReadBuffer failed", elm->name, caller); + + page = (PageHeader) BufferGetPage (*buf); + sm = (sequence_magic *) PageGetSpecialPointer (page); + + if ( sm->magic != SEQ_MAGIC ) + elog (WARN, "%s.%s: bad magic (%08X)", elm->name, caller, sm->magic); + + lp = PageGetItemId (page, FirstOffsetNumber); + Assert (ItemIdIsUsed (lp)); + tuple = (HeapTuple) PageGetItem ((Page) page, lp); + + seq = (SequenceTupleForm) GETSTRUCT(tuple); + + elm->increment = seq->increment_by; + + return (seq); + +} + + +static SeqTable +init_sequence (char * caller, char * name) +{ + SeqTable elm, priv = (SeqTable) NULL; + SeqTable temp; + + for (elm = seqtab; elm != (SeqTable) NULL; ) + { + if ( strcmp (elm->name, name) == 0 ) + break; + priv = elm; + elm = elm->next; + } + + if ( elm == (SeqTable) NULL ) /* not found */ + { + temp = (SeqTable) malloc (sizeof(SeqTableData)); + temp->name = malloc (strlen(name) + 1); + strcpy (temp->name, name); + temp->rel = (Relation) NULL; + temp->cached = temp->last = temp->increment = 0; + temp->next = (SeqTable) NULL; + } + else /* found */ + { + if ( elm->rel != (Relation) NULL) /* already opened */ + return (elm); + temp = elm; + } + + temp->rel = heap_openr (name); + + if ( ! RelationIsValid (temp->rel) ) + elog (WARN, "%s.%s: sequence does not exist", name, caller); + + RelationSetWIntentLock (temp->rel); + + if ( temp->rel->rd_rel->relkind != RELKIND_SEQUENCE ) + elog (WARN, "%s.%s: %s is not sequence !", name, caller, name); + + if ( elm != (SeqTable) NULL ) /* we opened sequence from our */ + { /* SeqTable - check relid ! */ + if ( RelationGetRelationId (elm->rel) != elm->relid ) + { + elog (NOTICE, "%s.%s: sequence was re-created", + name, caller, name); + elm->cached = elm->last = elm->increment = 0; + elm->relid = RelationGetRelationId (elm->rel); + } + } + else + { + elm = temp; + elm->relid = RelationGetRelationId (elm->rel); + if ( seqtab == (SeqTable) NULL ) + seqtab = elm; + else + priv->next = elm; + } + + return (elm); + +} + + +/* + * CloseSequences -- + * is calling by xact mgr at commit/abort. + */ +void +CloseSequences (void) +{ + SeqTable elm; + Relation rel; + + ItsSequenceCreation = false; + + for (elm = seqtab; elm != (SeqTable) NULL; ) + { + if ( elm->rel != (Relation) NULL ) /* opened in current xact */ + { + rel = elm->rel; + elm->rel = (Relation) NULL; + RelationUnsetWIntentLock (rel); + heap_close (rel); + } + elm = elm->next; + } + + return; + +} + + +static void +init_params (CreateSeqStmt *seq, SequenceTupleForm new) +{ + DefElem *last_value = NULL; + DefElem *increment_by = NULL; + DefElem *max_value = NULL; + DefElem *min_value = NULL; + DefElem *cache_value = NULL; + List *option; + + new->is_cycled = 'f'; + foreach (option, seq->options) + { + DefElem *defel = (DefElem *)lfirst(option); + + if ( !strcasecmp(defel->defname, "increment") ) + increment_by = defel; + else if ( !strcasecmp(defel->defname, "start") ) + last_value = defel; + else if ( !strcasecmp(defel->defname, "maxvalue") ) + max_value = defel; + else if ( !strcasecmp(defel->defname, "minvalue") ) + min_value = defel; + else if ( !strcasecmp(defel->defname, "cache") ) + cache_value = defel; + else if ( !strcasecmp(defel->defname, "cycle") ) + { + if ( defel->arg != (Node*) NULL ) + elog (WARN, "DefineSequence: CYCLE ??"); + new->is_cycled = 't'; + } + else + elog (WARN, "DefineSequence: option \"%s\" not recognized", + defel->defname); + } + + if ( increment_by == (DefElem*) NULL ) /* INCREMENT BY */ + new->increment_by = 1; + else if ( ( new->increment_by = get_param (increment_by) ) == 0 ) + elog (WARN, "DefineSequence: can't INCREMENT by 0"); + + if ( max_value == (DefElem*) NULL ) /* MAXVALUE */ + if ( new->increment_by > 0 ) + new->max_value = SEQ_MAXVALUE; /* ascending seq */ + else + new->max_value = -1; /* descending seq */ + else + new->max_value = get_param (max_value); + + if ( min_value == (DefElem*) NULL ) /* MINVALUE */ + if ( new->increment_by > 0 ) + new->min_value = 1; /* ascending seq */ + else + new->min_value = SEQ_MINVALUE; /* descending seq */ + else + new->min_value = get_param (min_value); + + if ( new->min_value >= new->max_value ) + elog (WARN, "DefineSequence: MINVALUE (%d) can't be >= MAXVALUE (%d)", + new->min_value, new->max_value); + + if ( last_value == (DefElem*) NULL ) /* START WITH */ + if ( new->increment_by > 0 ) + new->last_value = new->min_value; /* ascending seq */ + else + new->last_value = new->max_value; /* descending seq */ + else + new->last_value = get_param (last_value); + + if ( new->last_value < new->min_value ) + elog (WARN, "DefineSequence: START value (%d) can't be < MINVALUE (%d)", + new->last_value, new->min_value); + if ( new->last_value > new->max_value ) + elog (WARN, "DefineSequence: START value (%d) can't be > MAXVALUE (%d)", + new->last_value, new->max_value); + + if ( cache_value == (DefElem*) NULL ) /* CACHE */ + new->cache_value = 1; + else if ( ( new->cache_value = get_param (cache_value) ) <= 0 ) + elog (WARN, "DefineSequence: CACHE (%d) can't be <= 0", + new->cache_value); + +} + + +static int +get_param (DefElem *def) +{ + if ( def->arg == (Node*) NULL ) + elog (WARN, "DefineSequence: \"%s\" value unspecified", def->defname); + + if ( nodeTag (def->arg) == T_Integer ) + return (intVal (def->arg)); + + elog (WARN, "DefineSequence: \"%s\" is to be integer", def->defname); + return (-1); +}