diff options
Diffstat (limited to 'src/backend/commands/sequence.c')
-rw-r--r-- | src/backend/commands/sequence.c | 889 |
1 files changed, 0 insertions, 889 deletions
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c deleted file mode 100644 index 1d013612da4..00000000000 --- a/src/backend/commands/sequence.c +++ /dev/null @@ -1,889 +0,0 @@ -/*------------------------------------------------------------------------- - * - * sequence.c - * PostgreSQL sequences support code. - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/sequence.c,v 1.82 2002/06/20 20:29:27 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include "access/heapam.h" -#include "catalog/namespace.h" -#include "catalog/pg_type.h" -#include "commands/defrem.h" -#include "commands/tablecmds.h" -#include "commands/sequence.h" -#include "miscadmin.h" -#include "utils/acl.h" -#include "utils/builtins.h" - - -#ifndef INT64_IS_BUSTED -#ifdef HAVE_LL_CONSTANTS -#define SEQ_MAXVALUE ((int64) 0x7FFFFFFFFFFFFFFFLL) -#else -#define SEQ_MAXVALUE ((int64) 0x7FFFFFFFFFFFFFFF) -#endif -#else /* INT64_IS_BUSTED */ -#define SEQ_MAXVALUE ((int64) 0x7FFFFFFF) -#endif /* INT64_IS_BUSTED */ - -#define SEQ_MINVALUE (-SEQ_MAXVALUE) - -/* - * We don't want to log each fetching of a value from a sequence, - * so we pre-log a few fetches in advance. In the event of - * crash we can lose as much as we pre-logged. - */ -#define SEQ_LOG_VALS 32 - -/* - * The "special area" of a sequence's buffer page looks like this. - */ -#define SEQ_MAGIC 0x1717 - -typedef struct sequence_magic -{ - uint32 magic; -} sequence_magic; - -/* - * We store a SeqTable item for every sequence we have touched in the current - * session. This is needed to hold onto nextval/currval state. (We can't - * rely on the relcache, since it's only, well, a cache, and may decide to - * discard entries.) - * - * XXX We use linear search to find pre-existing SeqTable entries. This is - * good when only a small number of sequences are touched in a session, but - * would suck with many different sequences. Perhaps use a hashtable someday. - */ -typedef struct SeqTableData -{ - struct SeqTableData *next; /* link to next SeqTable object */ - Oid relid; /* pg_class OID of this sequence */ - TransactionId xid; /* xact in which we last did a seq op */ - int64 last; /* value last returned by nextval */ - 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 */ -} SeqTableData; - -typedef SeqTableData *SeqTable; - -static SeqTable seqtab = NULL; /* Head of list of SeqTable items */ - - -static void init_sequence(const char *caller, RangeVar *relation, - SeqTable *p_elm, Relation *p_rel); -static Form_pg_sequence read_info(const char *caller, SeqTable elm, - Relation rel, Buffer *buf); -static void init_params(CreateSeqStmt *seq, Form_pg_sequence new); -static void do_setval(RangeVar *sequence, int64 next, bool iscalled); - -/* - * DefineSequence - * Creates a new sequence relation - */ -void -DefineSequence(CreateSeqStmt *seq) -{ - FormData_pg_sequence new; - CreateStmt *stmt = makeNode(CreateStmt); - Oid seqoid; - 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++) - { - ColumnDef *coldef; - TypeName *typnam; - - typnam = makeNode(TypeName); - typnam->setof = FALSE; - typnam->arrayBounds = NIL; - 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->typeid = NAMEOID; - coldef->colname = "sequence_name"; - namestrcpy(&name, seq->sequence->relname); - value[i - 1] = NameGetDatum(&name); - break; - case SEQ_COL_LASTVAL: - typnam->typeid = INT8OID; - coldef->colname = "last_value"; - value[i - 1] = Int64GetDatumFast(new.last_value); - break; - case SEQ_COL_INCBY: - typnam->typeid = INT8OID; - coldef->colname = "increment_by"; - value[i - 1] = Int64GetDatumFast(new.increment_by); - break; - case SEQ_COL_MAXVALUE: - typnam->typeid = INT8OID; - coldef->colname = "max_value"; - value[i - 1] = Int64GetDatumFast(new.max_value); - break; - case SEQ_COL_MINVALUE: - typnam->typeid = INT8OID; - coldef->colname = "min_value"; - value[i - 1] = Int64GetDatumFast(new.min_value); - break; - case SEQ_COL_CACHE: - typnam->typeid = INT8OID; - coldef->colname = "cache_value"; - value[i - 1] = Int64GetDatumFast(new.cache_value); - break; - case SEQ_COL_LOG: - typnam->typeid = INT8OID; - coldef->colname = "log_cnt"; - value[i - 1] = Int64GetDatum((int64) 1); - break; - case SEQ_COL_CYCLE: - typnam->typeid = BOOLOID; - coldef->colname = "is_cycled"; - value[i - 1] = BoolGetDatum(new.is_cycled); - break; - case SEQ_COL_CALLED: - typnam->typeid = BOOLOID; - coldef->colname = "is_called"; - value[i - 1] = BoolGetDatum(false); - break; - } - stmt->tableElts = lappend(stmt->tableElts, coldef); - } - - stmt->relation = seq->sequence; - stmt->inhRelations = NIL; - stmt->constraints = NIL; - stmt->hasoids = false; - - seqoid = DefineRelation(stmt, RELKIND_SEQUENCE); - - rel = heap_open(seqoid, AccessExclusiveLock); - tupDesc = RelationGetDescr(rel); - - /* Initialize first page of relation with special magic number */ - - buf = ReadBuffer(rel, P_NEW); - - if (!BufferIsValid(buf)) - elog(ERROR, "DefineSequence: ReadBuffer failed"); - - Assert(BufferGetBlockNumber(buf) == 0); - - page = (PageHeader) BufferGetPage(buf); - - PageInit((Page) page, BufferGetPageSize(buf), sizeof(sequence_magic)); - sm = (sequence_magic *) PageGetSpecialPointer(page); - sm->magic = SEQ_MAGIC; - - /* hack: ensure heap_insert will insert on the just-created page */ - rel->rd_targblock = 0; - - /* Now form & insert sequence tuple */ - tuple = heap_formtuple(tupDesc, value, null); - simple_heap_insert(rel, tuple); - - Assert(ItemPointerGetOffsetNumber(&(tuple->t_self)) == FirstOffsetNumber); - - /* - * Two special hacks here: - * - * 1. Since VACUUM does not process sequences, we have to force the tuple - * to have xmin = FrozenTransactionId now. Otherwise it would become - * invisible to SELECTs after 2G transactions. It is okay to do this - * because if the current transaction aborts, no other xact will ever - * examine the sequence tuple anyway. - * - * 2. Even though heap_insert emitted a WAL log record, we have to emit - * an XLOG_SEQ_LOG record too, since (a) the heap_insert record will - * not have the right xmin, and (b) REDO of the heap_insert record - * would re-init page and sequence magic number would be lost. This - * means two log records instead of one :-( - */ - LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); - START_CRIT_SECTION(); - - { - /* - * Note that the "tuple" structure is still just a local tuple record - * created by heap_formtuple; its t_data pointer doesn't point at the - * disk buffer. To scribble on the disk buffer we need to fetch the - * item pointer. But do the same to the local tuple, since that will - * be the source for the WAL log record, below. - */ - ItemId itemId; - Item item; - - itemId = PageGetItemId((Page) page, FirstOffsetNumber); - item = PageGetItem((Page) page, itemId); - - HeapTupleHeaderSetXmin((HeapTupleHeader) item, FrozenTransactionId); - ((HeapTupleHeader) item)->t_infomask |= HEAP_XMIN_COMMITTED; - - HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId); - tuple->t_data->t_infomask |= HEAP_XMIN_COMMITTED; - } - - { - xl_seq_rec xlrec; - XLogRecPtr recptr; - XLogRecData rdata[2]; - Form_pg_sequence newseq = (Form_pg_sequence) GETSTRUCT(tuple); - - /* We do not log first nextval call, so "advance" sequence here */ - /* Note we are scribbling on local tuple, not the disk buffer */ - newseq->is_called = true; - newseq->log_cnt = 0; - - xlrec.node = rel->rd_node; - rdata[0].buffer = InvalidBuffer; - rdata[0].data = (char *) &xlrec; - rdata[0].len = sizeof(xl_seq_rec); - rdata[0].next = &(rdata[1]); - - rdata[1].buffer = InvalidBuffer; - rdata[1].data = (char *) tuple->t_data; - rdata[1].len = tuple->t_len; - rdata[1].next = NULL; - - recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata); - - PageSetLSN(page, recptr); - PageSetSUI(page, ThisStartUpID); - } - END_CRIT_SECTION(); - - LockBuffer(buf, BUFFER_LOCK_UNLOCK); - WriteBuffer(buf); - heap_close(rel, NoLock); -} - - -Datum -nextval(PG_FUNCTION_ARGS) -{ - text *seqin = PG_GETARG_TEXT_P(0); - RangeVar *sequence; - SeqTable elm; - Relation seqrel; - Buffer buf; - Page page; - Form_pg_sequence seq; - int64 incby, - maxv, - minv, - cache, - log, - fetch, - last; - int64 result, - next, - rescnt = 0; - bool logit = false; - - sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin, - "nextval")); - - /* open and AccessShareLock sequence */ - init_sequence("nextval", sequence, &elm, &seqrel); - - if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK) - elog(ERROR, "%s.nextval: you don't have permissions to set sequence %s", - sequence->relname, sequence->relname); - - if (elm->last != elm->cached) /* some numbers were cached */ - { - elm->last += elm->increment; - relation_close(seqrel, NoLock); - PG_RETURN_INT64(elm->last); - } - - /* lock page' buffer and read tuple */ - seq = read_info("nextval", elm, seqrel, &buf); - page = BufferGetPage(buf); - - last = next = result = seq->last_value; - incby = seq->increment_by; - maxv = seq->max_value; - minv = seq->min_value; - fetch = cache = seq->cache_value; - log = seq->log_cnt; - - if (!seq->is_called) - { - rescnt++; /* last_value if not called */ - fetch--; - log--; - } - - /* - * Decide whether we should emit a WAL log record. If so, force up - * the fetch count to grab SEQ_LOG_VALS more values than we actually - * need to cache. (These will then be usable without logging.) - * - * If this is the first nextval after a checkpoint, we must force - * a new WAL record to be written anyway, else replay starting from the - * checkpoint would fail to advance the sequence past the logged - * values. In this case we may as well fetch extra values. - */ - if (log < fetch) - { - /* forced log to satisfy local demand for values */ - fetch = log = fetch + SEQ_LOG_VALS; - logit = true; - } - else - { - XLogRecPtr redoptr = GetRedoRecPtr(); - - if (XLByteLE(PageGetLSN(page), redoptr)) - { - /* last update of seq was before checkpoint */ - fetch = log = fetch + SEQ_LOG_VALS; - logit = true; - } - } - - while (fetch) /* try to fetch cache [+ log ] 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 fetching */ - if (!seq->is_cycled) - elog(ERROR, "%s.nextval: reached MAXVALUE (" INT64_FORMAT ")", - sequence->relname, 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 fetching */ - if (!seq->is_cycled) - elog(ERROR, "%s.nextval: reached MINVALUE (" INT64_FORMAT ")", - sequence->relname, minv); - next = maxv; - } - else - next += incby; - } - fetch--; - if (rescnt < cache) - { - log--; - rescnt++; - last = next; - if (rescnt == 1) /* if it's first result - */ - result = next; /* it's what to return */ - } - } - - log -= fetch; /* adjust for any unfetched numbers */ - Assert(log >= 0); - - /* save info in local cache */ - elm->last = result; /* last returned number */ - elm->cached = last; /* last fetched number */ - - START_CRIT_SECTION(); - if (logit) - { - xl_seq_rec xlrec; - XLogRecPtr recptr; - XLogRecData rdata[2]; - - xlrec.node = seqrel->rd_node; - rdata[0].buffer = InvalidBuffer; - rdata[0].data = (char *) &xlrec; - rdata[0].len = sizeof(xl_seq_rec); - rdata[0].next = &(rdata[1]); - - seq->last_value = next; - seq->is_called = true; - seq->log_cnt = 0; - rdata[1].buffer = InvalidBuffer; - rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper; - rdata[1].len = ((PageHeader) page)->pd_special - - ((PageHeader) page)->pd_upper; - rdata[1].next = NULL; - - recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata); - - PageSetLSN(page, recptr); - PageSetSUI(page, ThisStartUpID); - } - - /* update on-disk data */ - seq->last_value = last; /* last fetched number */ - seq->is_called = true; - seq->log_cnt = log; /* how much is logged */ - END_CRIT_SECTION(); - - LockBuffer(buf, BUFFER_LOCK_UNLOCK); - - WriteBuffer(buf); - - relation_close(seqrel, NoLock); - - PG_RETURN_INT64(result); -} - -Datum -currval(PG_FUNCTION_ARGS) -{ - text *seqin = PG_GETARG_TEXT_P(0); - RangeVar *sequence; - SeqTable elm; - Relation seqrel; - int64 result; - - sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin, - "currval")); - - /* open and AccessShareLock sequence */ - init_sequence("currval", sequence, &elm, &seqrel); - - if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK) - elog(ERROR, "%s.currval: you don't have permissions to read sequence %s", - sequence->relname, sequence->relname); - - if (elm->increment == 0) /* nextval/read_info were not called */ - elog(ERROR, "%s.currval is not yet defined in this session", - sequence->relname); - - result = elm->last; - - 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 - * only for use in pg_dump, and setting the is_called flag may not - * work if multiple users are attached to the database and referencing - * the sequence (unlikely if pg_dump is restoring it). - * - * It is necessary to have the 3 arg version so that pg_dump can - * restore the state of a sequence exactly during data-only restores - - * it is the only way to clear the is_called flag in an existing - * sequence. - */ -static void -do_setval(RangeVar *sequence, int64 next, bool iscalled) -{ - SeqTable elm; - Relation seqrel; - Buffer buf; - Form_pg_sequence seq; - - /* open and AccessShareLock sequence */ - init_sequence("setval", sequence, &elm, &seqrel); - - if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK) - elog(ERROR, "%s.setval: you don't have permissions to set sequence %s", - sequence->relname, sequence->relname); - - /* lock page' buffer and read tuple */ - seq = read_info("setval", elm, seqrel, &buf); - - if ((next < seq->min_value) || (next > seq->max_value)) - elog(ERROR, "%s.setval: value " INT64_FORMAT " is out of bounds (" INT64_FORMAT "," INT64_FORMAT ")", - sequence->relname, next, seq->min_value, seq->max_value); - - /* save info in local cache */ - elm->last = next; /* last returned number */ - elm->cached = next; /* last cached number (forget cached - * values) */ - - START_CRIT_SECTION(); - { - xl_seq_rec xlrec; - XLogRecPtr recptr; - XLogRecData rdata[2]; - Page page = BufferGetPage(buf); - - xlrec.node = seqrel->rd_node; - rdata[0].buffer = InvalidBuffer; - rdata[0].data = (char *) &xlrec; - rdata[0].len = sizeof(xl_seq_rec); - rdata[0].next = &(rdata[1]); - - seq->last_value = next; - seq->is_called = true; - seq->log_cnt = 0; - rdata[1].buffer = InvalidBuffer; - rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper; - rdata[1].len = ((PageHeader) page)->pd_special - - ((PageHeader) page)->pd_upper; - rdata[1].next = NULL; - - recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata); - - PageSetLSN(page, recptr); - PageSetSUI(page, ThisStartUpID); - } - /* save info in sequence relation */ - seq->last_value = next; /* last fetched number */ - seq->is_called = iscalled; - seq->log_cnt = (iscalled) ? 0 : 1; - END_CRIT_SECTION(); - - LockBuffer(buf, BUFFER_LOCK_UNLOCK); - - WriteBuffer(buf); - - relation_close(seqrel, NoLock); -} - -/* - * Implement the 2 arg setval procedure. - * See do_setval for discussion. - */ -Datum -setval(PG_FUNCTION_ARGS) -{ - text *seqin = PG_GETARG_TEXT_P(0); - int64 next = PG_GETARG_INT64(1); - RangeVar *sequence; - - sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin, - "setval")); - - do_setval(sequence, next, true); - - PG_RETURN_INT64(next); -} - -/* - * Implement the 3 arg setval procedure. - * See do_setval for discussion. - */ -Datum -setval_and_iscalled(PG_FUNCTION_ARGS) -{ - text *seqin = PG_GETARG_TEXT_P(0); - int64 next = PG_GETARG_INT64(1); - bool iscalled = PG_GETARG_BOOL(2); - RangeVar *sequence; - - sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin, - "setval")); - - do_setval(sequence, next, iscalled); - - PG_RETURN_INT64(next); -} - - -/* - * Given a relation name, open and lock the sequence. p_elm and p_rel are - * output parameters. - */ -static void -init_sequence(const char *caller, RangeVar *relation, - SeqTable *p_elm, Relation *p_rel) -{ - Oid relid = RangeVarGetRelid(relation, false); - TransactionId thisxid = GetCurrentTransactionId(); - SeqTable elm; - Relation seqrel; - - /* Look to see if we already have a seqtable entry for relation */ - for (elm = seqtab; elm != NULL; elm = elm->next) - { - if (elm->relid == relid) - break; - } - - /* - * Open the sequence relation, acquiring AccessShareLock if we don't - * already have a lock in the current xact. - */ - if (elm == NULL || elm->xid != thisxid) - seqrel = relation_open(relid, AccessShareLock); - else - seqrel = relation_open(relid, NoLock); - - if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE) - elog(ERROR, "%s.%s: %s is not a sequence", - relation->relname, caller, relation->relname); - - /* - * Allocate new seqtable entry if we didn't find one. - * - * NOTE: seqtable entries remain in the list for the life of a backend. - * If the sequence itself is deleted then the entry becomes wasted memory, - * but it's small enough that this should not matter. - */ - if (elm == NULL) - { - /* - * Time to make a new seqtable entry. These entries live as long - * as the backend does, so we use plain malloc for them. - */ - elm = (SeqTable) malloc(sizeof(SeqTableData)); - if (elm == NULL) - elog(ERROR, "Memory exhausted in init_sequence"); - elm->relid = relid; - /* increment is set to 0 until we do read_info (see currval) */ - elm->last = elm->cached = elm->increment = 0; - elm->next = seqtab; - seqtab = elm; - } - - /* Flag that we have a lock in the current xact. */ - elm->xid = thisxid; - - *p_elm = elm; - *p_rel = seqrel; -} - - -/* Given an opened relation, lock the page buffer and find the tuple */ -static Form_pg_sequence -read_info(const char *caller, SeqTable elm, - Relation rel, Buffer *buf) -{ - PageHeader page; - ItemId lp; - HeapTupleData tuple; - sequence_magic *sm; - Form_pg_sequence seq; - - if (rel->rd_nblocks > 1) - elog(ERROR, "%s.%s: invalid number of blocks in sequence", - RelationGetRelationName(rel), caller); - - *buf = ReadBuffer(rel, 0); - if (!BufferIsValid(*buf)) - elog(ERROR, "%s.%s: ReadBuffer failed", - RelationGetRelationName(rel), 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)", - RelationGetRelationName(rel), 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 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 = false; - foreach(option, seq->options) - { - DefElem *defel = (DefElem *) lfirst(option); - - if (strcmp(defel->defname, "increment") == 0) - increment_by = defel; - else if (strcmp(defel->defname, "start") == 0) - last_value = defel; - else if (strcmp(defel->defname, "maxvalue") == 0) - max_value = defel; - else if (strcmp(defel->defname, "minvalue") == 0) - min_value = defel; - else if (strcmp(defel->defname, "cache") == 0) - cache_value = defel; - else if (strcmp(defel->defname, "cycle") == 0) - { - if (defel->arg != (Node *) NULL) - elog(ERROR, "DefineSequence: CYCLE ??"); - new->is_cycled = true; - } - 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 = defGetInt64(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 = defGetInt64(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 = defGetInt64(min_value); - - if (new->min_value >= new->max_value) - elog(ERROR, "DefineSequence: MINVALUE (" INT64_FORMAT ") can't be >= MAXVALUE (" INT64_FORMAT ")", - 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 = defGetInt64(last_value); - - if (new->last_value < new->min_value) - elog(ERROR, "DefineSequence: START value (" INT64_FORMAT ") can't be < MINVALUE (" INT64_FORMAT ")", - new->last_value, new->min_value); - if (new->last_value > new->max_value) - elog(ERROR, "DefineSequence: START value (" INT64_FORMAT ") can't be > MAXVALUE (" INT64_FORMAT ")", - new->last_value, new->max_value); - - if (cache_value == (DefElem *) NULL) /* CACHE */ - new->cache_value = 1; - else if ((new->cache_value = defGetInt64(cache_value)) <= 0) - elog(ERROR, "DefineSequence: CACHE (" INT64_FORMAT ") can't be <= 0", - new->cache_value); - -} - - -void -seq_redo(XLogRecPtr lsn, XLogRecord *record) -{ - uint8 info = record->xl_info & ~XLR_INFO_MASK; - Relation reln; - Buffer buffer; - Page page; - char *item; - Size itemsz; - xl_seq_rec *xlrec = (xl_seq_rec *) XLogRecGetData(record); - sequence_magic *sm; - - if (info != XLOG_SEQ_LOG) - elog(PANIC, "seq_redo: unknown op code %u", info); - - reln = XLogOpenRelation(true, RM_SEQ_ID, xlrec->node); - if (!RelationIsValid(reln)) - return; - - buffer = XLogReadBuffer(true, reln, 0); - if (!BufferIsValid(buffer)) - elog(PANIC, "seq_redo: can't read block of %u/%u", - xlrec->node.tblNode, xlrec->node.relNode); - - page = (Page) BufferGetPage(buffer); - - /* Always reinit the page and reinstall the magic number */ - /* See comments in DefineSequence */ - PageInit((Page) page, BufferGetPageSize(buffer), sizeof(sequence_magic)); - sm = (sequence_magic *) PageGetSpecialPointer(page); - sm->magic = SEQ_MAGIC; - - item = (char *) xlrec + sizeof(xl_seq_rec); - itemsz = record->xl_len - sizeof(xl_seq_rec); - itemsz = MAXALIGN(itemsz); - if (PageAddItem(page, (Item) item, itemsz, - FirstOffsetNumber, LP_USED) == InvalidOffsetNumber) - elog(PANIC, "seq_redo: failed to add item to page"); - - PageSetLSN(page, lsn); - PageSetSUI(page, ThisStartUpID); - UnlockAndWriteBuffer(buffer); -} - -void -seq_undo(XLogRecPtr lsn, XLogRecord *record) -{ -} - -void -seq_desc(char *buf, uint8 xl_info, char *rec) -{ - uint8 info = xl_info & ~XLR_INFO_MASK; - xl_seq_rec *xlrec = (xl_seq_rec *) rec; - - if (info == XLOG_SEQ_LOG) - strcat(buf, "log: "); - else - { - strcat(buf, "UNKNOWN"); - return; - } - - sprintf(buf + strlen(buf), "node %u/%u", - xlrec->node.tblNode, xlrec->node.relNode); -} |