📄 sequence.c
字号:
/*------------------------------------------------------------------------- * * sequence.c * PostgreSQL sequences support code. * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.125.2.1 2005/11/22 18:23:07 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"#include "utils/resowner.h"#include "utils/syscache.h"/* * 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 0x1717typedef 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 *//* * last_used_seq is updated by nextval() to point to the last used * sequence. */static SeqTableData *last_used_seq = NULL;static int64 nextval_internal(Oid relid);static void acquire_share_lock(Relation seqrel, SeqTable seq);static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);static Form_pg_sequence read_info(SeqTable elm, Relation rel, Buffer *buf);static void init_params(List *options, Form_pg_sequence new, bool isInit);static void do_setval(Oid relid, int64 next, bool iscalled);/* * DefineSequence * Creates a new sequence relation */voidDefineSequence(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 all option values */ init_params(seq->options, &new, true); /* * 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->inhcount = 0; coldef->is_local = true; coldef->is_not_null = true; coldef->raw_default = NULL; coldef->cooked_default = NULL; coldef->constraints = NIL; coldef->support = NULL; 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 = MUST_NOT_HAVE_OIDS; stmt->oncommit = ONCOMMIT_NOOP; stmt->tablespacename = NULL; 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); 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; } /* XLOG stuff */ if (!rel->rd_istemp) { 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].data = (char *) &xlrec; rdata[0].len = sizeof(xl_seq_rec); rdata[0].buffer = InvalidBuffer; rdata[0].next = &(rdata[1]); rdata[1].data = (char *) tuple->t_data; rdata[1].len = tuple->t_len; rdata[1].buffer = InvalidBuffer; rdata[1].next = NULL; recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata); PageSetLSN(page, recptr); PageSetTLI(page, ThisTimeLineID); } END_CRIT_SECTION(); LockBuffer(buf, BUFFER_LOCK_UNLOCK); WriteBuffer(buf); heap_close(rel, NoLock);}/* * AlterSequence * * Modify the definition of a sequence relation */voidAlterSequence(AlterSeqStmt *stmt){ Oid relid; SeqTable elm; Relation seqrel; Buffer buf; Page page; Form_pg_sequence seq; FormData_pg_sequence new; /* open and AccessShareLock sequence */ relid = RangeVarGetRelid(stmt->sequence, false); init_sequence(relid, &elm, &seqrel); /* allow ALTER to sequence owner only */ if (!pg_class_ownercheck(elm->relid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, stmt->sequence->relname); /* lock page' buffer and read tuple into new sequence structure */ seq = read_info(elm, seqrel, &buf); page = BufferGetPage(buf); /* Copy old values of options into workspace */ memcpy(&new, seq, sizeof(FormData_pg_sequence)); /* Check and set new values */ init_params(stmt->options, &new, false); /* Now okay to update the on-disk tuple */ memcpy(seq, &new, sizeof(FormData_pg_sequence)); /* Clear local cache so that we don't think we have cached numbers */ elm->last = new.last_value; /* last returned number */ elm->cached = new.last_value; /* last cached number (forget cached * values) */ START_CRIT_SECTION(); /* XLOG stuff */ if (!seqrel->rd_istemp) { xl_seq_rec xlrec; XLogRecPtr recptr; XLogRecData rdata[2]; xlrec.node = seqrel->rd_node; rdata[0].data = (char *) &xlrec; rdata[0].len = sizeof(xl_seq_rec); rdata[0].buffer = InvalidBuffer; rdata[0].next = &(rdata[1]); rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper; rdata[1].len = ((PageHeader) page)->pd_special - ((PageHeader) page)->pd_upper; rdata[1].buffer = InvalidBuffer; rdata[1].next = NULL; recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata); PageSetLSN(page, recptr); PageSetTLI(page, ThisTimeLineID); } END_CRIT_SECTION(); LockBuffer(buf, BUFFER_LOCK_UNLOCK); WriteBuffer(buf); relation_close(seqrel, NoLock);}/* * Note: nextval with a text argument is no longer exported as a pg_proc * entry, but we keep it around to ease porting of C code that may have * called the function directly. */Datumnextval(PG_FUNCTION_ARGS){ text *seqin = PG_GETARG_TEXT_P(0); RangeVar *sequence; Oid relid; sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin)); relid = RangeVarGetRelid(sequence, false); PG_RETURN_INT64(nextval_internal(relid));}Datumnextval_oid(PG_FUNCTION_ARGS){ Oid relid = PG_GETARG_OID(0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -