1
0
mirror of https://github.com/postgres/postgres.git synced 2025-04-21 12:05:57 +03:00
Tom Lane eabc714a91 Reimplement parsing and storage of default expressions and constraint
expressions in CREATE TABLE.  There is no longer an emasculated expression
syntax for these things; it's full a_expr for constraints, and b_expr
for defaults (unfortunately the fact that NOT NULL is a part of the
column constraint syntax causes a shift/reduce conflict if you try a_expr.
Oh well --- at least parenthesized boolean expressions work now).  Also,
stored expression for a column default is not pre-coerced to the column
type; we rely on transformInsertStatement to do that when the default is
actually used.  This means "f1 datetime default 'now'" behaves the way
people usually expect it to.
BTW, all the support code is now there to implement ALTER TABLE ADD
CONSTRAINT and ALTER TABLE ADD COLUMN with a default value.  I didn't
actually teach ALTER TABLE to call it, but it wouldn't be much work.
1999-10-03 23:55:40 +00:00

573 lines
13 KiB
C

/*-------------------------------------------------------------------------
*
* sequence.c
* PostgreSQL sequences support code.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "commands/creatinh.h"
#include "commands/sequence.h"
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#define SEQ_MAGIC 0x1717
#define SEQ_MAXVALUE ((int4)0x7FFFFFFF)
#define SEQ_MINVALUE -(SEQ_MAXVALUE)
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 *Form_pg_sequence;
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 Form_pg_sequence read_info(char *caller, SeqTable elm, Buffer *buf);
static void init_params(CreateSeqStmt *seq, Form_pg_sequence 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;
NameData name;
/* 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;
typnam->typmod = -1;
coldef = makeNode(ColumnDef);
coldef->typename = typnam;
coldef->raw_default = NULL;
coldef->cooked_default = NULL;
coldef->is_not_null = false;
null[i - 1] = ' ';
switch (i)
{
case SEQ_COL_NAME:
typnam->name = "name";
coldef->colname = "sequence_name";
namestrcpy(&name, seq->seqname);
value[i - 1] = NameGetDatum(&name);
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->inhRelnames = NIL;
stmt->constraints = NIL;
DefineRelation(stmt, RELKIND_SEQUENCE);
rel = heap_openr(seq->seqname, AccessExclusiveLock);
tupDesc = RelationGetDescr(rel);
Assert(RelationGetNumberOfBlocks(rel) == 0);
buf = ReadBuffer(rel, P_NEW);
if (!BufferIsValid(buf))
elog(ERROR, "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(ERROR, "DefineSequence: WriteBuffer failed");
heap_close(rel, AccessExclusiveLock);
}
int4
nextval(struct varlena * seqin)
{
char *seqname = textout(seqin);
SeqTable elm;
Buffer buf;
Form_pg_sequence seq;
int4 incby,
maxv,
minv,
cache;
int4 result,
next,
rescnt = 0;
/* open and AccessShareLock sequence */
elm = init_sequence("nextval", seqname);
pfree(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' buffer 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(ERROR, "%s.nextval: got MAXVALUE (%d)",
elm->name, 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(ERROR, "%s.nextval: got MINVALUE (%d)",
elm->name, 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';
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
if (WriteBuffer(buf) == STATUS_ERROR)
elog(ERROR, "%s.nextval: WriteBuffer failed", elm->name);
return result;
}
int4
currval(struct varlena * seqin)
{
char *seqname = textout(seqin);
SeqTable elm;
int4 result;
/* open and AccessShareLock sequence */
elm = init_sequence("currval", seqname);
pfree(seqname);
if (elm->increment == 0) /* nextval/read_info were not called */
elog(ERROR, "%s.currval is not yet defined in this session", elm->name);
result = elm->last;
return result;
}
int4
setval(struct varlena * seqin, int4 next)
{
char *seqname = textout(seqin);
SeqTable elm;
Buffer buf;
Form_pg_sequence seq;
#ifndef NO_SECURITY
if (pg_aclcheck(seqname, getpgusername(), ACL_WR) != ACLCHECK_OK)
elog(ERROR, "%s.setval: you don't have permissions to set sequence %s",
seqname, seqname);
#endif
/* open and AccessShareLock sequence */
elm = init_sequence("setval", seqname);
seq = read_info("setval", elm, &buf); /* lock page' buffer and
* read tuple */
if (seq->cache_value != 1)
{
elog(ERROR, "%s.setval: can't set value of sequence %s, cache != 1",
seqname, seqname);
}
if ((next < seq->min_value) || (next > seq->max_value))
{
elog(ERROR, "%s.setval: value %d is of of bounds (%d,%d)",
seqname, next, seq->min_value, seq->max_value);
}
/* save info in local cache */
elm->last = next; /* 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';
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
if (WriteBuffer(buf) == STATUS_ERROR)
elog(ERROR, "%s.settval: WriteBuffer failed", seqname);
return next;
}
static Form_pg_sequence
read_info(char *caller, SeqTable elm, Buffer *buf)
{
PageHeader page;
ItemId lp;
HeapTupleData tuple;
sequence_magic *sm;
Form_pg_sequence seq;
if (RelationGetNumberOfBlocks(elm->rel) != 1)
elog(ERROR, "%s.%s: invalid number of blocks in sequence",
elm->name, caller);
*buf = ReadBuffer(elm->rel, 0);
if (!BufferIsValid(*buf))
elog(ERROR, "%s.%s: ReadBuffer failed", elm->name, caller);
LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
page = (PageHeader) BufferGetPage(*buf);
sm = (sequence_magic *) PageGetSpecialPointer(page);
if (sm->magic != SEQ_MAGIC)
elog(ERROR, "%s.%s: bad magic (%08X)", elm->name, caller, sm->magic);
lp = PageGetItemId(page, FirstOffsetNumber);
Assert(ItemIdIsUsed(lp));
tuple.t_data = (HeapTupleHeader) PageGetItem((Page) page, lp);
seq = (Form_pg_sequence) 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, AccessShareLock);
if (temp->rel->rd_rel->relkind != RELKIND_SEQUENCE)
elog(ERROR, "%s.%s: %s is not sequence !", name, caller, name);
if (elm != (SeqTable) NULL) /* we opened sequence from our */
{ /* SeqTable - check relid ! */
if (RelationGetRelid(elm->rel) != elm->relid)
{
elog(NOTICE, "%s.%s: sequence was re-created",
name, caller, name);
elm->cached = elm->last = elm->increment = 0;
elm->relid = RelationGetRelid(elm->rel);
}
}
else
{
elm = temp;
elm->relid = RelationGetRelid(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;
for (elm = seqtab; elm != (SeqTable) NULL; elm = elm->next)
{
if (elm->rel != (Relation) NULL) /* opened in current xact */
{
rel = elm->rel;
elm->rel = (Relation) NULL;
heap_close(rel, AccessShareLock);
}
}
}
static void
init_params(CreateSeqStmt *seq, Form_pg_sequence 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(ERROR, "DefineSequence: CYCLE ??");
new->is_cycled = 't';
}
else
elog(ERROR, "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(ERROR, "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(ERROR, "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(ERROR, "DefineSequence: START value (%d) can't be < MINVALUE (%d)",
new->last_value, new->min_value);
if (new->last_value > new->max_value)
elog(ERROR, "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(ERROR, "DefineSequence: CACHE (%d) can't be <= 0",
new->cache_value);
}
static int
get_param(DefElem *def)
{
if (def->arg == (Node *) NULL)
elog(ERROR, "DefineSequence: \"%s\" value unspecified", def->defname);
if (nodeTag(def->arg) == T_Integer)
return intVal(def->arg);
elog(ERROR, "DefineSequence: \"%s\" is to be integer", def->defname);
return -1;
}