sequence.c 28.4 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * sequence.c
4
 *	  PostgreSQL sequences support code.
5
 *
B
Bruce Momjian 已提交
6
 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7 8 9 10
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
11
 *	  $Header: /cvsroot/pgsql/src/backend/commands/sequence.c,v 1.103 2003/09/25 06:57:58 petere Exp $
12
 *
13 14
 *-------------------------------------------------------------------------
 */
15
#include "postgres.h"
16

17
#include "access/heapam.h"
18
#include "catalog/namespace.h"
19
#include "catalog/pg_type.h"
20
#include "commands/defrem.h"
21
#include "commands/tablecmds.h"
22
#include "commands/sequence.h"
B
Bruce Momjian 已提交
23
#include "miscadmin.h"
24
#include "utils/acl.h"
B
Bruce Momjian 已提交
25
#include "utils/builtins.h"
26

V
Vadim B. Mikheev 已提交
27
/*
28
 * We don't want to log each fetching of a value from a sequence,
V
Vadim B. Mikheev 已提交
29 30 31
 * so we pre-log a few fetches in advance. In the event of
 * crash we can lose as much as we pre-logged.
 */
B
Bruce Momjian 已提交
32
#define SEQ_LOG_VALS	32
33

34 35 36 37 38
/*
 * The "special area" of a sequence's buffer page looks like this.
 */
#define SEQ_MAGIC	  0x1717

39 40
typedef struct sequence_magic
{
41
	uint32		magic;
42
} sequence_magic;
43

44 45 46 47 48 49
/*
 * 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.)
 *
B
Bruce Momjian 已提交
50
 * XXX We use linear search to find pre-existing SeqTable entries.	This is
51 52 53
 * 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.
 */
54 55
typedef struct SeqTableData
{
56 57 58 59 60 61 62
	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 */
63
} SeqTableData;
64 65 66

typedef SeqTableData *SeqTable;

67
static SeqTable seqtab = NULL;	/* Head of list of SeqTable items */
68

69

70
static void init_sequence(RangeVar *relation,
B
Bruce Momjian 已提交
71
			  SeqTable *p_elm, Relation *p_rel);
72 73
static Form_pg_sequence read_info(SeqTable elm, Relation rel, Buffer *buf);
static void init_params(List *options, Form_pg_sequence new);
74
static void do_setval(RangeVar *sequence, int64 next, bool iscalled);
75 76

/*
B
Bruce Momjian 已提交
77
 * DefineSequence
78
 *				Creates a new sequence relation
79 80
 */
void
81
DefineSequence(CreateSeqStmt *seq)
82
{
83
	FormData_pg_sequence new;
84
	CreateStmt *stmt = makeNode(CreateStmt);
85
	Oid			seqoid;
86 87 88
	Relation	rel;
	Buffer		buf;
	PageHeader	page;
89
	sequence_magic *sm;
90 91 92 93 94
	HeapTuple	tuple;
	TupleDesc	tupDesc;
	Datum		value[SEQ_COL_LASTCOL];
	char		null[SEQ_COL_LASTCOL];
	int			i;
95
	NameData	name;
96

B
Bruce Momjian 已提交
97
	/* Values are NULL (or false) by default */
B
Bruce Momjian 已提交
98 99
	new.last_value = 0;
	new.increment_by = 0;
B
Bruce Momjian 已提交
100
	new.max_value = 0;
B
Bruce Momjian 已提交
101 102
	new.min_value = 0;
	new.cache_value = 0;
B
Bruce Momjian 已提交
103
	new.is_cycled = false;
B
Bruce Momjian 已提交
104

105
	/* Check and set values */
106
	init_params(seq->options, &new);
107 108

	/*
109
	 * Create relation (and fill *null & *value)
110 111 112
	 */
	stmt->tableElts = NIL;
	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
113
	{
114 115 116
		ColumnDef  *coldef;
		TypeName   *typnam;

117 118
		typnam = makeNode(TypeName);
		typnam->setof = FALSE;
119
		typnam->arrayBounds = NIL;
B
Bruce Momjian 已提交
120
		typnam->typmod = -1;
121

122 123
		coldef = makeNode(ColumnDef);
		coldef->typename = typnam;
124 125
		coldef->inhcount = 0;
		coldef->is_local = true;
126
		coldef->is_not_null = true;
127 128
		coldef->raw_default = NULL;
		coldef->cooked_default = NULL;
129 130 131
		coldef->constraints = NIL;
		coldef->support = NULL;

132 133 134 135
		null[i - 1] = ' ';

		switch (i)
		{
136
			case SEQ_COL_NAME:
137
				typnam->typeid = NAMEOID;
138
				coldef->colname = "sequence_name";
139
				namestrcpy(&name, seq->sequence->relname);
140
				value[i - 1] = NameGetDatum(&name);
141 142
				break;
			case SEQ_COL_LASTVAL:
143
				typnam->typeid = INT8OID;
144
				coldef->colname = "last_value";
145
				value[i - 1] = Int64GetDatumFast(new.last_value);
146 147
				break;
			case SEQ_COL_INCBY:
148
				typnam->typeid = INT8OID;
149
				coldef->colname = "increment_by";
150
				value[i - 1] = Int64GetDatumFast(new.increment_by);
151 152
				break;
			case SEQ_COL_MAXVALUE:
153
				typnam->typeid = INT8OID;
154
				coldef->colname = "max_value";
155
				value[i - 1] = Int64GetDatumFast(new.max_value);
156 157
				break;
			case SEQ_COL_MINVALUE:
158
				typnam->typeid = INT8OID;
159
				coldef->colname = "min_value";
160
				value[i - 1] = Int64GetDatumFast(new.min_value);
161 162
				break;
			case SEQ_COL_CACHE:
163
				typnam->typeid = INT8OID;
164
				coldef->colname = "cache_value";
165
				value[i - 1] = Int64GetDatumFast(new.cache_value);
166
				break;
V
Vadim B. Mikheev 已提交
167
			case SEQ_COL_LOG:
168
				typnam->typeid = INT8OID;
V
Vadim B. Mikheev 已提交
169
				coldef->colname = "log_cnt";
170
				value[i - 1] = Int64GetDatum((int64) 1);
V
Vadim B. Mikheev 已提交
171
				break;
172
			case SEQ_COL_CYCLE:
173
				typnam->typeid = BOOLOID;
174
				coldef->colname = "is_cycled";
175
				value[i - 1] = BoolGetDatum(new.is_cycled);
176 177
				break;
			case SEQ_COL_CALLED:
178
				typnam->typeid = BOOLOID;
179
				coldef->colname = "is_called";
180
				value[i - 1] = BoolGetDatum(false);
181
				break;
182 183 184 185
		}
		stmt->tableElts = lappend(stmt->tableElts, coldef);
	}

186 187
	stmt->relation = seq->sequence;
	stmt->inhRelations = NIL;
188
	stmt->constraints = NIL;
189
	stmt->hasoids = false;
190
	stmt->oncommit = ONCOMMIT_NOOP;
191

192
	seqoid = DefineRelation(stmt, RELKIND_SEQUENCE);
193

194
	rel = heap_open(seqoid, AccessExclusiveLock);
195
	tupDesc = RelationGetDescr(rel);
196

197 198
	/* Initialize first page of relation with special magic number */

199 200 201
	buf = ReadBuffer(rel, P_NEW);

	if (!BufferIsValid(buf))
202
		elog(ERROR, "ReadBuffer failed");
203

204 205
	Assert(BufferGetBlockNumber(buf) == 0);

206 207 208 209 210 211
	page = (PageHeader) BufferGetPage(buf);

	PageInit((Page) page, BufferGetPageSize(buf), sizeof(sequence_magic));
	sm = (sequence_magic *) PageGetSpecialPointer(page);
	sm->magic = SEQ_MAGIC;

212 213 214
	/* hack: ensure heap_insert will insert on the just-created page */
	rel->rd_targblock = 0;

215
	/* Now form & insert sequence tuple */
216
	tuple = heap_formtuple(tupDesc, value, null);
217
	simple_heap_insert(rel, tuple);
218

219 220
	Assert(ItemPointerGetOffsetNumber(&(tuple->t_self)) == FirstOffsetNumber);

221
	/*
222 223 224
	 * Two special hacks here:
	 *
	 * 1. Since VACUUM does not process sequences, we have to force the tuple
B
Bruce Momjian 已提交
225 226
	 * to have xmin = FrozenTransactionId now.	Otherwise it would become
	 * invisible to SELECTs after 2G transactions.	It is okay to do this
227 228 229 230 231 232
	 * 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
B
Bruce Momjian 已提交
233
	 * would re-init page and sequence magic number would be lost.	This
234
	 * means two log records instead of one :-(
235
	 */
236
	LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
237

238
	START_CRIT_SECTION();
239 240 241

	{
		/*
B
Bruce Momjian 已提交
242 243 244 245 246 247
		 * 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.
248 249 250 251 252 253 254
		 */
		ItemId		itemId;
		Item		item;

		itemId = PageGetItemId((Page) page, FirstOffsetNumber);
		item = PageGetItem((Page) page, itemId);

255
		HeapTupleHeaderSetXmin((HeapTupleHeader) item, FrozenTransactionId);
256 257
		((HeapTupleHeader) item)->t_infomask |= HEAP_XMIN_COMMITTED;

258
		HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId);
259 260 261
		tuple->t_data->t_infomask |= HEAP_XMIN_COMMITTED;
	}

262 263
	/* XLOG stuff */
	if (!rel->rd_istemp)
264
	{
265 266 267 268
		xl_seq_rec	xlrec;
		XLogRecPtr	recptr;
		XLogRecData rdata[2];
		Form_pg_sequence newseq = (Form_pg_sequence) GETSTRUCT(tuple);
269 270

		/* We do not log first nextval call, so "advance" sequence here */
271
		/* Note we are scribbling on local tuple, not the disk buffer */
272
		newseq->is_called = true;
273 274 275 276 277 278 279 280 281
		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;
282
		rdata[1].data = (char *) tuple->t_data;
283 284 285 286 287 288 289 290
		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);
	}
291

292
	END_CRIT_SECTION();
293

294
	LockBuffer(buf, BUFFER_LOCK_UNLOCK);
295 296
	WriteBuffer(buf);
	heap_close(rel, NoLock);
297 298
}

B
Bruce Momjian 已提交
299 300 301
/*
 * AlterSequence
 *
B
Bruce Momjian 已提交
302
 * Modify the defition of a sequence relation
B
Bruce Momjian 已提交
303 304
 */
void
305
AlterSequence(AlterSeqStmt *stmt)
B
Bruce Momjian 已提交
306 307 308 309 310 311 312 313 314
{
	SeqTable	elm;
	Relation	seqrel;
	Buffer		buf;
	Page		page;
	Form_pg_sequence seq;
	FormData_pg_sequence new;

	/* open and AccessShareLock sequence */
315
	init_sequence(stmt->sequence, &elm, &seqrel);
B
Bruce Momjian 已提交
316

317
	/* allow DROP to sequence owner only */
B
Bruce Momjian 已提交
318
	if (!pg_class_ownercheck(elm->relid, GetUserId()))
319 320
		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
					   stmt->sequence->relname);
B
Bruce Momjian 已提交
321 322

	/* lock page' buffer and read tuple into new sequence structure */
323
	seq = read_info(elm, seqrel, &buf);
B
Bruce Momjian 已提交
324 325 326
	page = BufferGetPage(buf);

	new.increment_by = seq->increment_by;
B
Bruce Momjian 已提交
327
	new.max_value = seq->max_value;
B
Bruce Momjian 已提交
328 329 330 331 332 333
	new.min_value = seq->min_value;
	new.cache_value = seq->cache_value;
	new.is_cycled = seq->is_cycled;
	new.last_value = seq->last_value;

	/* Check and set values */
334
	init_params(stmt->options, &new);
B
Bruce Momjian 已提交
335 336 337 338 339 340 341 342 343 344 345 346 347

	seq->increment_by = new.increment_by;
	seq->max_value = new.max_value;
	seq->min_value = new.min_value;
	seq->cache_value = new.cache_value;
	seq->is_cycled = new.is_cycled;
	if (seq->last_value != new.last_value)
	{
		seq->last_value = new.last_value;
		seq->is_called = false;
		seq->log_cnt = 1;
	}

348
	/* save info in local cache */
B
Bruce Momjian 已提交
349 350 351
	elm->last = new.last_value; /* last returned number */
	elm->cached = new.last_value;		/* last cached number (forget
										 * cached values) */
352

B
Bruce Momjian 已提交
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
	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].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 *) 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);
	}

	END_CRIT_SECTION();

	LockBuffer(buf, BUFFER_LOCK_UNLOCK);

	WriteBuffer(buf);

	relation_close(seqrel, NoLock);
}

389

390 391
Datum
nextval(PG_FUNCTION_ARGS)
392
{
393
	text	   *seqin = PG_GETARG_TEXT_P(0);
394
	RangeVar   *sequence;
395
	SeqTable	elm;
396
	Relation	seqrel;
397
	Buffer		buf;
398
	Page		page;
399
	Form_pg_sequence seq;
400
	int64		incby,
401 402
				maxv,
				minv,
V
Vadim B. Mikheev 已提交
403 404 405 406
				cache,
				log,
				fetch,
				last;
407
	int64		result,
408 409
				next,
				rescnt = 0;
V
Vadim B. Mikheev 已提交
410
	bool		logit = false;
411

412
	sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
B
Bruce Momjian 已提交
413
															 "nextval"));
414

V
Vadim B. Mikheev 已提交
415
	/* open and AccessShareLock sequence */
416
	init_sequence(sequence, &elm, &seqrel);
417

418
	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
419 420
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
421
				 errmsg("permission denied for sequence %s",
422
						sequence->relname)));
423 424 425 426

	if (elm->last != elm->cached)		/* some numbers were cached */
	{
		elm->last += elm->increment;
427
		relation_close(seqrel, NoLock);
428
		PG_RETURN_INT64(elm->last);
429
	}
430

431
	/* lock page' buffer and read tuple */
432
	seq = read_info(elm, seqrel, &buf);
433
	page = BufferGetPage(buf);
434

V
Vadim B. Mikheev 已提交
435
	last = next = result = seq->last_value;
436 437 438
	incby = seq->increment_by;
	maxv = seq->max_value;
	minv = seq->min_value;
V
Vadim B. Mikheev 已提交
439 440
	fetch = cache = seq->cache_value;
	log = seq->log_cnt;
441

442
	if (!seq->is_called)
V
Vadim B. Mikheev 已提交
443
	{
444
		rescnt++;				/* last_value if not called */
V
Vadim B. Mikheev 已提交
445 446 447
		fetch--;
		log--;
	}
448

449
	/*
B
Bruce Momjian 已提交
450
	 * Decide whether we should emit a WAL log record.	If so, force up
451 452 453
	 * the fetch count to grab SEQ_LOG_VALS more values than we actually
	 * need to cache.  (These will then be usable without logging.)
	 *
B
Bruce Momjian 已提交
454 455
	 * 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
456
	 * checkpoint would fail to advance the sequence past the logged
B
Bruce Momjian 已提交
457
	 * values.	In this case we may as well fetch extra values.
458
	 */
V
Vadim B. Mikheev 已提交
459 460
	if (log < fetch)
	{
461 462
		/* forced log to satisfy local demand for values */
		fetch = log = fetch + SEQ_LOG_VALS;
V
Vadim B. Mikheev 已提交
463 464
		logit = true;
	}
465 466 467 468 469 470 471 472 473 474 475
	else
	{
		XLogRecPtr	redoptr = GetRedoRecPtr();

		if (XLByteLE(PageGetLSN(page), redoptr))
		{
			/* last update of seq was before checkpoint */
			fetch = log = fetch + SEQ_LOG_VALS;
			logit = true;
		}
	}
V
Vadim B. Mikheev 已提交
476

B
Bruce Momjian 已提交
477
	while (fetch)				/* try to fetch cache [+ log ] numbers */
478
	{
479 480 481 482
		/*
		 * Check MAXVALUE for ascending sequences and MINVALUE for
		 * descending sequences
		 */
483
		if (incby > 0)
484
		{
485
			/* ascending sequence */
486 487 488 489
			if ((maxv >= 0 && next > maxv - incby) ||
				(maxv < 0 && next + incby > maxv))
			{
				if (rescnt > 0)
V
Vadim B. Mikheev 已提交
490
					break;		/* stop fetching */
491
				if (!seq->is_cycled)
492
				{
B
Bruce Momjian 已提交
493 494
					char		buf[100];

495
					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
496
					ereport(ERROR,
B
Bruce Momjian 已提交
497
					  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
498
					   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
B
Bruce Momjian 已提交
499
							  sequence->relname, buf)));
500
				}
501 502 503 504 505 506 507
				next = minv;
			}
			else
				next += incby;
		}
		else
		{
508
			/* descending sequence */
509 510 511 512
			if ((minv < 0 && next < minv - incby) ||
				(minv >= 0 && next + incby < minv))
			{
				if (rescnt > 0)
V
Vadim B. Mikheev 已提交
513
					break;		/* stop fetching */
514
				if (!seq->is_cycled)
515
				{
B
Bruce Momjian 已提交
516 517
					char		buf[100];

518
					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
519
					ereport(ERROR,
B
Bruce Momjian 已提交
520
					  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
521
					   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
B
Bruce Momjian 已提交
522
							  sequence->relname, buf)));
523
				}
524 525 526 527 528
				next = maxv;
			}
			else
				next += incby;
		}
V
Vadim B. Mikheev 已提交
529 530 531 532 533 534
		fetch--;
		if (rescnt < cache)
		{
			log--;
			rescnt++;
			last = next;
B
Bruce Momjian 已提交
535 536
			if (rescnt == 1)	/* if it's first result - */
				result = next;	/* it's what to return */
V
Vadim B. Mikheev 已提交
537
		}
538 539
	}

540 541 542
	log -= fetch;				/* adjust for any unfetched numbers */
	Assert(log >= 0);

543 544
	/* save info in local cache */
	elm->last = result;			/* last returned number */
V
Vadim B. Mikheev 已提交
545 546
	elm->cached = last;			/* last fetched number */

547
	START_CRIT_SECTION();
548 549 550

	/* XLOG stuff */
	if (logit && !seqrel->rd_istemp)
V
Vadim B. Mikheev 已提交
551 552 553
	{
		xl_seq_rec	xlrec;
		XLogRecPtr	recptr;
B
Bruce Momjian 已提交
554
		XLogRecData rdata[2];
V
Vadim B. Mikheev 已提交
555

556
		xlrec.node = seqrel->rd_node;
557
		rdata[0].buffer = InvalidBuffer;
B
Bruce Momjian 已提交
558
		rdata[0].data = (char *) &xlrec;
559 560 561
		rdata[0].len = sizeof(xl_seq_rec);
		rdata[0].next = &(rdata[1]);

562
		/* set values that will be saved in xlog */
563
		seq->last_value = next;
564
		seq->is_called = true;
565
		seq->log_cnt = 0;
566

567
		rdata[1].buffer = InvalidBuffer;
B
Bruce Momjian 已提交
568 569 570
		rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper;
		rdata[1].len = ((PageHeader) page)->pd_special -
			((PageHeader) page)->pd_upper;
571 572
		rdata[1].next = NULL;

B
Bruce Momjian 已提交
573
		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
V
Vadim B. Mikheev 已提交
574

575 576
		PageSetLSN(page, recptr);
		PageSetSUI(page, ThisStartUpID);
V
Vadim B. Mikheev 已提交
577
	}
578

579
	/* update on-disk data */
V
Vadim B. Mikheev 已提交
580
	seq->last_value = last;		/* last fetched number */
581
	seq->is_called = true;
V
Vadim B. Mikheev 已提交
582
	seq->log_cnt = log;			/* how much is logged */
583

584
	END_CRIT_SECTION();
585

V
Vadim B. Mikheev 已提交
586 587
	LockBuffer(buf, BUFFER_LOCK_UNLOCK);

B
Bruce Momjian 已提交
588
	WriteBuffer(buf);
589

590 591
	relation_close(seqrel, NoLock);

592
	PG_RETURN_INT64(result);
593 594
}

595 596
Datum
currval(PG_FUNCTION_ARGS)
597
{
598
	text	   *seqin = PG_GETARG_TEXT_P(0);
599
	RangeVar   *sequence;
600
	SeqTable	elm;
601
	Relation	seqrel;
602
	int64		result;
603

604
	sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
B
Bruce Momjian 已提交
605
															 "currval"));
606

V
Vadim B. Mikheev 已提交
607
	/* open and AccessShareLock sequence */
608
	init_sequence(sequence, &elm, &seqrel);
609

610
	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK)
611 612
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
613
				 errmsg("permission denied for sequence %s",
614
						sequence->relname)));
615

616
	if (elm->increment == 0)	/* nextval/read_info were not called */
617 618
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
619
				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
620
						sequence->relname)));
621 622 623

	result = elm->last;

624 625
	relation_close(seqrel, NoLock);

626
	PG_RETURN_INT64(result);
627 628
}

B
Bruce Momjian 已提交
629
/*
630 631 632 633
 * 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
B
Bruce Momjian 已提交
634
 * work if multiple users are attached to the database and referencing
635 636
 * the sequence (unlikely if pg_dump is restoring it).
 *
B
Bruce Momjian 已提交
637
 * It is necessary to have the 3 arg version so that pg_dump can
638 639 640 641
 * 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.
 */
B
Bruce Momjian 已提交
642
static void
643
do_setval(RangeVar *sequence, int64 next, bool iscalled)
M
 
Marc G. Fournier 已提交
644 645
{
	SeqTable	elm;
646
	Relation	seqrel;
647
	Buffer		buf;
648
	Form_pg_sequence seq;
M
 
Marc G. Fournier 已提交
649

650
	/* open and AccessShareLock sequence */
651
	init_sequence(sequence, &elm, &seqrel);
652 653

	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
654 655
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
656
				 errmsg("permission denied for sequence %s",
657
						sequence->relname)));
M
 
Marc G. Fournier 已提交
658

659
	/* lock page' buffer and read tuple */
660
	seq = read_info(elm, seqrel, &buf);
M
 
Marc G. Fournier 已提交
661

662
	if ((next < seq->min_value) || (next > seq->max_value))
663
	{
B
Bruce Momjian 已提交
664 665 666 667
		char		bufv[100],
					bufm[100],
					bufx[100];

668 669 670
		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);
671 672
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
673 674
				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
						bufv, sequence->relname, bufm, bufx)));
675
	}
M
 
Marc G. Fournier 已提交
676 677 678

	/* save info in local cache */
	elm->last = next;			/* last returned number */
B
Bruce Momjian 已提交
679 680
	elm->cached = next;			/* last cached number (forget cached
								 * values) */
M
 
Marc G. Fournier 已提交
681

682
	START_CRIT_SECTION();
683 684 685

	/* XLOG stuff */
	if (!seqrel->rd_istemp)
V
Vadim B. Mikheev 已提交
686 687 688
	{
		xl_seq_rec	xlrec;
		XLogRecPtr	recptr;
B
Bruce Momjian 已提交
689
		XLogRecData rdata[2];
690
		Page		page = BufferGetPage(buf);
V
Vadim B. Mikheev 已提交
691

692
		xlrec.node = seqrel->rd_node;
693
		rdata[0].buffer = InvalidBuffer;
B
Bruce Momjian 已提交
694
		rdata[0].data = (char *) &xlrec;
695 696 697
		rdata[0].len = sizeof(xl_seq_rec);
		rdata[0].next = &(rdata[1]);

698
		/* set values that will be saved in xlog */
699
		seq->last_value = next;
700
		seq->is_called = true;
701
		seq->log_cnt = 0;
702

703
		rdata[1].buffer = InvalidBuffer;
B
Bruce Momjian 已提交
704 705 706
		rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper;
		rdata[1].len = ((PageHeader) page)->pd_special -
			((PageHeader) page)->pd_upper;
707 708
		rdata[1].next = NULL;

B
Bruce Momjian 已提交
709
		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
710 711 712

		PageSetLSN(page, recptr);
		PageSetSUI(page, ThisStartUpID);
V
Vadim B. Mikheev 已提交
713
	}
714

715 716
	/* save info in sequence relation */
	seq->last_value = next;		/* last fetched number */
717
	seq->is_called = iscalled;
718
	seq->log_cnt = (iscalled) ? 0 : 1;
719

720
	END_CRIT_SECTION();
M
 
Marc G. Fournier 已提交
721

V
Vadim B. Mikheev 已提交
722 723
	LockBuffer(buf, BUFFER_LOCK_UNLOCK);

B
Bruce Momjian 已提交
724
	WriteBuffer(buf);
725 726

	relation_close(seqrel, NoLock);
727 728
}

729 730 731 732
/*
 * Implement the 2 arg setval procedure.
 * See do_setval for discussion.
 */
733 734 735 736
Datum
setval(PG_FUNCTION_ARGS)
{
	text	   *seqin = PG_GETARG_TEXT_P(0);
737
	int64		next = PG_GETARG_INT64(1);
738 739 740
	RangeVar   *sequence;

	sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
B
Bruce Momjian 已提交
741
															  "setval"));
742

743
	do_setval(sequence, next, true);
744

745
	PG_RETURN_INT64(next);
746 747
}

748 749 750 751
/*
 * Implement the 3 arg setval procedure.
 * See do_setval for discussion.
 */
752 753 754 755
Datum
setval_and_iscalled(PG_FUNCTION_ARGS)
{
	text	   *seqin = PG_GETARG_TEXT_P(0);
756
	int64		next = PG_GETARG_INT64(1);
757
	bool		iscalled = PG_GETARG_BOOL(2);
758
	RangeVar   *sequence;
759

760
	sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
B
Bruce Momjian 已提交
761
															  "setval"));
762

763
	do_setval(sequence, next, iscalled);
764

765
	PG_RETURN_INT64(next);
M
 
Marc G. Fournier 已提交
766 767
}

768

769 770 771 772 773
/*
 * Given a relation name, open and lock the sequence.  p_elm and p_rel are
 * output parameters.
 */
static void
774
init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel)
775
{
776
	Oid			relid = RangeVarGetRelid(relation, false);
777 778
	TransactionId thisxid = GetCurrentTransactionId();
	SeqTable	elm;
779
	Relation	seqrel;
B
Bruce Momjian 已提交
780

781
	/* Look to see if we already have a seqtable entry for relation */
782
	for (elm = seqtab; elm != NULL; elm = elm->next)
783
	{
784
		if (elm->relid == relid)
785 786 787
			break;
	}

788 789 790 791 792 793 794 795
	/*
	 * 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);
796

797
	if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
798 799 800 801
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("\"%s\" is not a sequence",
						relation->relname)));
802

803
	/*
804
	 * Allocate new seqtable entry if we didn't find one.
805 806
	 *
	 * NOTE: seqtable entries remain in the list for the life of a backend.
B
Bruce Momjian 已提交
807 808 809
	 * If the sequence itself is deleted then the entry becomes wasted
	 * memory, but it's small enough that this should not matter.
	 */
810
	if (elm == NULL)
811
	{
812 813
		/*
		 * Time to make a new seqtable entry.  These entries live as long
814 815 816
		 * as the backend does, so we use plain malloc for them.
		 */
		elm = (SeqTable) malloc(sizeof(SeqTableData));
T
Tom Lane 已提交
817
		if (elm == NULL)
818 819 820
			ereport(ERROR,
					(errcode(ERRCODE_OUT_OF_MEMORY),
					 errmsg("out of memory")));
821
		elm->relid = relid;
822 823 824 825
		/* increment is set to 0 until we do read_info (see currval) */
		elm->last = elm->cached = elm->increment = 0;
		elm->next = seqtab;
		seqtab = elm;
826 827
	}

828 829 830 831 832
	/* Flag that we have a lock in the current xact. */
	elm->xid = thisxid;

	*p_elm = elm;
	*p_rel = seqrel;
833 834 835
}


836 837
/* Given an opened relation, lock the page buffer and find the tuple */
static Form_pg_sequence
838
read_info(SeqTable elm, Relation rel, Buffer *buf)
839
{
840 841 842 843 844
	PageHeader	page;
	ItemId		lp;
	HeapTupleData tuple;
	sequence_magic *sm;
	Form_pg_sequence seq;
845

846
	if (rel->rd_nblocks > 1)
847 848
		elog(ERROR, "invalid number of blocks in sequence \"%s\"",
			 RelationGetRelationName(rel));
849 850 851

	*buf = ReadBuffer(rel, 0);
	if (!BufferIsValid(*buf))
852
		elog(ERROR, "ReadBuffer failed");
853 854 855 856 857 858 859

	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);

	page = (PageHeader) BufferGetPage(*buf);
	sm = (sequence_magic *) PageGetSpecialPointer(page);

	if (sm->magic != SEQ_MAGIC)
860 861
		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
			 RelationGetRelationName(rel), sm->magic);
862 863 864 865 866 867 868 869 870 871

	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;
872 873 874
}


875
static void
876
init_params(List *options, Form_pg_sequence new)
877
{
878 879 880 881 882
	DefElem    *last_value = NULL;
	DefElem    *increment_by = NULL;
	DefElem    *max_value = NULL;
	DefElem    *min_value = NULL;
	DefElem    *cache_value = NULL;
883
	bool		is_cycled_set = false;
884
	List	   *option;
885

B
Bruce Momjian 已提交
886
	foreach(option, options)
887
	{
888
		DefElem    *defel = (DefElem *) lfirst(option);
889

890
		if (strcmp(defel->defname, "increment") == 0)
891 892
		{
			if (increment_by)
893 894 895
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
896
			increment_by = defel;
897
		}
B
Bruce Momjian 已提交
898

B
Bruce Momjian 已提交
899
		/*
B
Bruce Momjian 已提交
900
		 * start is for a new sequence restart is for alter
B
Bruce Momjian 已提交
901
		 */
902 903
		else if (strcmp(defel->defname, "start") == 0 ||
				 strcmp(defel->defname, "restart") == 0)
904 905
		{
			if (last_value)
906 907 908
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
909
			last_value = defel;
910
		}
911
		else if (strcmp(defel->defname, "maxvalue") == 0)
912 913
		{
			if (max_value)
914 915 916
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
917
			max_value = defel;
918
		}
919
		else if (strcmp(defel->defname, "minvalue") == 0)
920 921
		{
			if (min_value)
922 923 924
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
925
			min_value = defel;
926
		}
927
		else if (strcmp(defel->defname, "cache") == 0)
928 929
		{
			if (cache_value)
930 931 932
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
933
			cache_value = defel;
934
		}
935
		else if (strcmp(defel->defname, "cycle") == 0)
936 937
		{
			if (is_cycled_set)
938 939 940
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
941
			is_cycled_set = true;
942
			new->is_cycled = (defel->arg != NULL);
943
		}
944
		else
945
			elog(ERROR, "option \"%s\" not recognized",
946 947 948
				 defel->defname);
	}

B
Bruce Momjian 已提交
949
	/* INCREMENT BY */
B
Bruce Momjian 已提交
950
	if (new->increment_by == 0 && increment_by == (DefElem *) NULL)
951
		new->increment_by = 1;
B
Bruce Momjian 已提交
952 953 954
	else if (increment_by != (DefElem *) NULL)
	{
		new->increment_by = defGetInt64(increment_by);
955 956 957
		if (new->increment_by == 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
958
					 errmsg("INCREMENT must not be zero")));
B
Bruce Momjian 已提交
959
	}
960

B
Bruce Momjian 已提交
961
	/* MAXVALUE */
B
Bruce Momjian 已提交
962
	if ((new->max_value == 0 && max_value == (DefElem *) NULL)
B
Bruce Momjian 已提交
963
		|| (max_value != (DefElem *) NULL && !max_value->arg))
964
	{
965
		if (new->increment_by > 0)
B
Bruce Momjian 已提交
966
			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
967
		else
B
Bruce Momjian 已提交
968
			new->max_value = -1;	/* descending seq */
969
	}
B
Bruce Momjian 已提交
970
	else if (max_value != (DefElem *) NULL)
971
		new->max_value = defGetInt64(max_value);
972

B
Bruce Momjian 已提交
973
	/* MINVALUE */
B
Bruce Momjian 已提交
974
	if ((new->min_value == 0 && min_value == (DefElem *) NULL)
B
Bruce Momjian 已提交
975
		|| (min_value != (DefElem *) NULL && !min_value->arg))
976
	{
977
		if (new->increment_by > 0)
B
Bruce Momjian 已提交
978
			new->min_value = 1; /* ascending seq */
979
		else
B
Bruce Momjian 已提交
980
			new->min_value = SEQ_MINVALUE;		/* descending seq */
981
	}
B
Bruce Momjian 已提交
982
	else if (min_value != (DefElem *) NULL)
983
		new->min_value = defGetInt64(min_value);
984 985

	if (new->min_value >= new->max_value)
986
	{
B
Bruce Momjian 已提交
987 988 989
		char		bufm[100],
					bufx[100];

990 991
		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
		snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value);
992 993 994 995
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("MINVALUE (%s) must be less than MAXVALUE (%s)",
						bufm, bufx)));
996
	}
997

B
Bruce Momjian 已提交
998
	/* START WITH */
B
Bruce Momjian 已提交
999
	if (new->last_value == 0 && last_value == (DefElem *) NULL)
1000
	{
1001 1002 1003 1004
		if (new->increment_by > 0)
			new->last_value = new->min_value;	/* ascending seq */
		else
			new->last_value = new->max_value;	/* descending seq */
1005
	}
B
Bruce Momjian 已提交
1006
	else if (last_value != (DefElem *) NULL)
1007
		new->last_value = defGetInt64(last_value);
1008 1009

	if (new->last_value < new->min_value)
1010
	{
B
Bruce Momjian 已提交
1011 1012 1013
		char		bufs[100],
					bufm[100];

1014 1015
		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
1016 1017
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
1018 1019
			  errmsg("START value (%s) can't be less than MINVALUE (%s)",
					 bufs, bufm)));
1020
	}
1021
	if (new->last_value > new->max_value)
1022
	{
B
Bruce Momjian 已提交
1023 1024 1025
		char		bufs[100],
					bufm[100];

1026 1027
		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
1028 1029
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
1030 1031
		   errmsg("START value (%s) can't be greater than MAXVALUE (%s)",
				  bufs, bufm)));
1032
	}
1033

B
Bruce Momjian 已提交
1034 1035
	/* CACHE */
	if (cache_value == (DefElem *) NULL)
1036
		new->cache_value = 1;
1037
	else if ((new->cache_value = defGetInt64(cache_value)) <= 0)
1038
	{
B
Bruce Momjian 已提交
1039 1040
		char		buf[100];

1041
		snprintf(buf, sizeof(buf), INT64_FORMAT, new->cache_value);
1042 1043 1044
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("CACHE (%s) must be greater than zero", buf)));
1045
	}
1046 1047
}

V
Vadim B. Mikheev 已提交
1048

B
Bruce Momjian 已提交
1049 1050
void
seq_redo(XLogRecPtr lsn, XLogRecord *record)
V
Vadim B. Mikheev 已提交
1051
{
B
Bruce Momjian 已提交
1052 1053 1054 1055 1056 1057 1058
	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);
1059
	sequence_magic *sm;
V
Vadim B. Mikheev 已提交
1060

1061
	if (info != XLOG_SEQ_LOG)
1062
		elog(PANIC, "seq_redo: unknown op code %u", info);
V
Vadim B. Mikheev 已提交
1063 1064 1065 1066 1067

	reln = XLogOpenRelation(true, RM_SEQ_ID, xlrec->node);
	if (!RelationIsValid(reln))
		return;

1068
	buffer = XLogReadBuffer(true, reln, 0);
V
Vadim B. Mikheev 已提交
1069
	if (!BufferIsValid(buffer))
1070
		elog(PANIC, "seq_redo: can't read block of %u/%u",
B
Bruce Momjian 已提交
1071
			 xlrec->node.tblNode, xlrec->node.relNode);
V
Vadim B. Mikheev 已提交
1072 1073 1074

	page = (Page) BufferGetPage(buffer);

1075 1076
	/* Always reinit the page and reinstall the magic number */
	/* See comments in DefineSequence */
1077 1078 1079
	PageInit((Page) page, BufferGetPageSize(buffer), sizeof(sequence_magic));
	sm = (sequence_magic *) PageGetSpecialPointer(page);
	sm->magic = SEQ_MAGIC;
V
Vadim B. Mikheev 已提交
1080

B
Bruce Momjian 已提交
1081
	item = (char *) xlrec + sizeof(xl_seq_rec);
1082 1083
	itemsz = record->xl_len - sizeof(xl_seq_rec);
	itemsz = MAXALIGN(itemsz);
B
Bruce Momjian 已提交
1084
	if (PageAddItem(page, (Item) item, itemsz,
1085
					FirstOffsetNumber, LP_USED) == InvalidOffsetNumber)
1086
		elog(PANIC, "seq_redo: failed to add item to page");
V
Vadim B. Mikheev 已提交
1087 1088 1089 1090 1091 1092

	PageSetLSN(page, lsn);
	PageSetSUI(page, ThisStartUpID);
	UnlockAndWriteBuffer(buffer);
}

B
Bruce Momjian 已提交
1093 1094
void
seq_undo(XLogRecPtr lsn, XLogRecord *record)
V
Vadim B. Mikheev 已提交
1095 1096 1097
{
}

B
Bruce Momjian 已提交
1098 1099
void
seq_desc(char *buf, uint8 xl_info, char *rec)
V
Vadim B. Mikheev 已提交
1100
{
B
Bruce Momjian 已提交
1101 1102
	uint8		info = xl_info & ~XLR_INFO_MASK;
	xl_seq_rec *xlrec = (xl_seq_rec *) rec;
V
Vadim B. Mikheev 已提交
1103 1104 1105 1106 1107 1108 1109 1110 1111

	if (info == XLOG_SEQ_LOG)
		strcat(buf, "log: ");
	else
	{
		strcat(buf, "UNKNOWN");
		return;
	}

1112
	sprintf(buf + strlen(buf), "node %u/%u",
B
Bruce Momjian 已提交
1113
			xlrec->node.tblNode, xlrec->node.relNode);
V
Vadim B. Mikheev 已提交
1114
}