sequence.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,123 行 · 第 1/2 页

C
1,123
字号
	END_CRIT_SECTION();	LockBuffer(buf, BUFFER_LOCK_UNLOCK);	WriteBuffer(buf);	relation_close(seqrel, NoLock);	PG_RETURN_INT64(result);}Datumcurrval(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(sequence, &elm, &seqrel);	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK)		ereport(ERROR,				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),				 errmsg("permission denied for sequence %s",						sequence->relname)));	if (elm->increment == 0)	/* nextval/read_info were not called */		ereport(ERROR,				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),				 errmsg("currval of sequence \"%s\" 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 voiddo_setval(RangeVar *sequence, int64 next, bool iscalled){	SeqTable	elm;	Relation	seqrel;	Buffer		buf;	Form_pg_sequence seq;	/* open and AccessShareLock sequence */	init_sequence(sequence, &elm, &seqrel);	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)		ereport(ERROR,				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),				 errmsg("permission denied for sequence %s",						sequence->relname)));	/* lock page' buffer and read tuple */	seq = read_info(elm, seqrel, &buf);	if ((next < seq->min_value) || (next > seq->max_value))	{		char		bufv[100],					bufm[100],					bufx[100];		snprintf(bufv, sizeof(bufv), INT64_FORMAT, next);		snprintf(bufm, sizeof(bufm), INT64_FORMAT, seq->min_value);		snprintf(bufx, sizeof(bufx), INT64_FORMAT, seq->max_value);		ereport(ERROR,				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",						bufv, sequence->relname, bufm, bufx)));	}	/* save info in local cache */	elm->last = next;			/* last returned number */	elm->cached = next;			/* last cached number (forget cached								 * values) */	START_CRIT_SECTION();	/* XLOG stuff */	if (!seqrel->rd_istemp)	{		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]);		/* set values that will be saved in xlog */		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. */Datumsetval(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. */Datumsetval_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 voidinit_sequence(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)		ereport(ERROR,				(errcode(ERRCODE_WRONG_OBJECT_TYPE),				 errmsg("\"%s\" is not a sequence",						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)			ereport(ERROR,					(errcode(ERRCODE_OUT_OF_MEMORY),					 errmsg("out of memory")));		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_sequenceread_info(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, "invalid number of blocks in sequence \"%s\"",			 RelationGetRelationName(rel));	*buf = ReadBuffer(rel, 0);	if (!BufferIsValid(*buf))		elog(ERROR, "ReadBuffer failed");	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);	page = (PageHeader) BufferGetPage(*buf);	sm = (sequence_magic *) PageGetSpecialPointer(page);	if (sm->magic != SEQ_MAGIC)		elog(ERROR, "bad magic number in sequence \"%s\": %08X",			 RelationGetRelationName(rel), 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;}/* * init_params: process the options list of CREATE or ALTER SEQUENCE, * and store the values into appropriate fields of *new. * * If isInit is true, fill any unspecified options with default values; * otherwise, do not change existing options that aren't explicitly overridden. */static voidinit_params(List *options, Form_pg_sequence new, bool isInit){	DefElem    *last_value = NULL;	DefElem    *increment_by = NULL;	DefElem    *max_value = NULL;	DefElem    *min_value = NULL;	DefElem    *cache_value = NULL;	DefElem    *is_cycled = NULL;	List	   *option;	foreach(option, options)	{		DefElem    *defel = (DefElem *) lfirst(option);		if (strcmp(defel->defname, "increment") == 0)		{			if (increment_by)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("conflicting or redundant options")));			increment_by = defel;		}		/*		 * start is for a new sequence restart is for alter		 */		else if (strcmp(defel->defname, "start") == 0 ||				 strcmp(defel->defname, "restart") == 0)		{			if (last_value)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("conflicting or redundant options")));			last_value = defel;		}		else if (strcmp(defel->defname, "maxvalue") == 0)		{			if (max_value)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("conflicting or redundant options")));			max_value = defel;		}		else if (strcmp(defel->defname, "minvalue") == 0)		{			if (min_value)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("conflicting or redundant options")));			min_value = defel;		}		else if (strcmp(defel->defname, "cache") == 0)		{			if (cache_value)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("conflicting or redundant options")));			cache_value = defel;		}		else if (strcmp(defel->defname, "cycle") == 0)		{			if (is_cycled)				ereport(ERROR,						(errcode(ERRCODE_SYNTAX_ERROR),						 errmsg("conflicting or redundant options")));			is_cycled = defel;		}		else			elog(ERROR, "option \"%s\" not recognized",				 defel->defname);	}	/* INCREMENT BY */	if (increment_by != (DefElem *) NULL)	{		new->increment_by = defGetInt64(increment_by);		if (new->increment_by == 0)			ereport(ERROR,					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),					 errmsg("INCREMENT must not be zero")));	}	else if (isInit)		new->increment_by = 1;	/* CYCLE */	if (is_cycled != (DefElem *) NULL)	{		new->is_cycled = intVal(is_cycled->arg);		Assert(new->is_cycled == false || new->is_cycled == true);	}	else if (isInit)		new->is_cycled = false;	/* MAXVALUE (null arg means NO MAXVALUE) */	if (max_value != (DefElem *) NULL && max_value->arg)	{		new->max_value = defGetInt64(max_value);	}	else if (isInit || max_value != (DefElem *) NULL)	{		if (new->increment_by > 0)			new->max_value = SEQ_MAXVALUE;		/* ascending seq */		else			new->max_value = -1;				/* descending seq */	}	/* MINVALUE (null arg means NO MINVALUE) */	if (min_value != (DefElem *) NULL && min_value->arg)	{		new->min_value = defGetInt64(min_value);	}	else if (isInit || min_value != (DefElem *) NULL)	{		if (new->increment_by > 0)			new->min_value = 1;					/* ascending seq */		else			new->min_value = SEQ_MINVALUE;		/* descending seq */	}	/* crosscheck min/max */	if (new->min_value >= new->max_value)	{		char		bufm[100],					bufx[100];		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);		snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value);		ereport(ERROR,				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),				 errmsg("MINVALUE (%s) must be less than MAXVALUE (%s)",						bufm, bufx)));	}	/* START WITH */	if (last_value != (DefElem *) NULL)	{		new->last_value = defGetInt64(last_value);		new->is_called = false;		new->log_cnt = 1;	}	else if (isInit)	{		if (new->increment_by > 0)			new->last_value = new->min_value;	/* ascending seq */		else			new->last_value = new->max_value;	/* descending seq */		new->is_called = false;		new->log_cnt = 1;	}	/* crosscheck */	if (new->last_value < new->min_value)	{		char		bufs[100],					bufm[100];		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);		ereport(ERROR,				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),			  errmsg("START value (%s) can't be less than MINVALUE (%s)",					 bufs, bufm)));	}	if (new->last_value > new->max_value)	{		char		bufs[100],					bufm[100];		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);		ereport(ERROR,				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),		   errmsg("START value (%s) can't be greater than MAXVALUE (%s)",				  bufs, bufm)));	}	/* CACHE */	if (cache_value != (DefElem *) NULL)	{		new->cache_value = defGetInt64(cache_value);		if (new->cache_value <= 0)		{			char		buf[100];			snprintf(buf, sizeof(buf), INT64_FORMAT, new->cache_value);			ereport(ERROR,					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),					 errmsg("CACHE (%s) must be greater than zero",							buf)));		}	}	else if (isInit)		new->cache_value = 1;}voidseq_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);}voidseq_undo(XLogRecPtr lsn, XLogRecord *record){}voidseq_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);}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?