mirror of https://github.com/postgres/postgres
parent
1a3c7371e8
commit
e8647c45d6
@ -0,0 +1,552 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* sequence.c-- |
||||
* PostgreSQL sequences support code. |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
|
||||
#include <postgres.h> |
||||
|
||||
#include <storage/bufmgr.h> |
||||
#include <storage/bufpage.h> |
||||
#include <storage/lmgr.h> |
||||
#include <access/heapam.h> |
||||
#include <nodes/parsenodes.h> |
||||
#include <commands/creatinh.h> |
||||
#include <commands/sequence.h> |
||||
#include <utils/builtins.h> |
||||
|
||||
#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); |
||||
} |
Loading…
Reference in new issue