sequence.c 28.8 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
 *	  $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.106 2003/12/14 00:34:47 neilc 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
static Form_pg_sequence read_info(SeqTable elm, Relation rel, Buffer *buf);
73
static void init_params(List *options, Form_pg_sequence new, bool isInit);
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

97 98
	/* Check and set all option values */
	init_params(seq->options, &new, true);
99 100

	/*
101
	 * Create relation (and fill *null & *value)
102 103 104
	 */
	stmt->tableElts = NIL;
	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
105
	{
106 107 108
		ColumnDef  *coldef;
		TypeName   *typnam;

109 110
		typnam = makeNode(TypeName);
		typnam->setof = FALSE;
111
		typnam->arrayBounds = NIL;
B
Bruce Momjian 已提交
112
		typnam->typmod = -1;
113

114 115
		coldef = makeNode(ColumnDef);
		coldef->typename = typnam;
116 117
		coldef->inhcount = 0;
		coldef->is_local = true;
118
		coldef->is_not_null = true;
119 120
		coldef->raw_default = NULL;
		coldef->cooked_default = NULL;
121 122 123
		coldef->constraints = NIL;
		coldef->support = NULL;

124 125 126 127
		null[i - 1] = ' ';

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

178 179
	stmt->relation = seq->sequence;
	stmt->inhRelations = NIL;
180
	stmt->constraints = NIL;
181
	stmt->hasoids = false;
182
	stmt->oncommit = ONCOMMIT_NOOP;
183

184
	seqoid = DefineRelation(stmt, RELKIND_SEQUENCE);
185

186
	rel = heap_open(seqoid, AccessExclusiveLock);
187
	tupDesc = RelationGetDescr(rel);
188

189 190
	/* Initialize first page of relation with special magic number */

191 192 193
	buf = ReadBuffer(rel, P_NEW);

	if (!BufferIsValid(buf))
194
		elog(ERROR, "ReadBuffer failed");
195

196 197
	Assert(BufferGetBlockNumber(buf) == 0);

198 199 200 201 202 203
	page = (PageHeader) BufferGetPage(buf);

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

204 205 206
	/* hack: ensure heap_insert will insert on the just-created page */
	rel->rd_targblock = 0;

207
	/* Now form & insert sequence tuple */
208
	tuple = heap_formtuple(tupDesc, value, null);
209
	simple_heap_insert(rel, tuple);
210

211 212
	Assert(ItemPointerGetOffsetNumber(&(tuple->t_self)) == FirstOffsetNumber);

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

230
	START_CRIT_SECTION();
231 232 233

	{
		/*
B
Bruce Momjian 已提交
234 235 236 237 238 239
		 * 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.
240 241 242 243 244 245 246
		 */
		ItemId		itemId;
		Item		item;

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

247
		HeapTupleHeaderSetXmin((HeapTupleHeader) item, FrozenTransactionId);
248 249
		((HeapTupleHeader) item)->t_infomask |= HEAP_XMIN_COMMITTED;

250
		HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId);
251 252 253
		tuple->t_data->t_infomask |= HEAP_XMIN_COMMITTED;
	}

254 255
	/* XLOG stuff */
	if (!rel->rd_istemp)
256
	{
257 258 259 260
		xl_seq_rec	xlrec;
		XLogRecPtr	recptr;
		XLogRecData rdata[2];
		Form_pg_sequence newseq = (Form_pg_sequence) GETSTRUCT(tuple);
261 262

		/* We do not log first nextval call, so "advance" sequence here */
263
		/* Note we are scribbling on local tuple, not the disk buffer */
264
		newseq->is_called = true;
265 266 267 268 269 270 271 272 273
		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;
274
		rdata[1].data = (char *) tuple->t_data;
275 276 277 278 279 280 281 282
		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);
	}
283

284
	END_CRIT_SECTION();
285

286
	LockBuffer(buf, BUFFER_LOCK_UNLOCK);
287 288
	WriteBuffer(buf);
	heap_close(rel, NoLock);
289 290
}

B
Bruce Momjian 已提交
291 292 293
/*
 * AlterSequence
 *
294
 * Modify the definition of a sequence relation
B
Bruce Momjian 已提交
295 296
 */
void
297
AlterSequence(AlterSeqStmt *stmt)
B
Bruce Momjian 已提交
298 299 300 301 302 303 304 305 306
{
	SeqTable	elm;
	Relation	seqrel;
	Buffer		buf;
	Page		page;
	Form_pg_sequence seq;
	FormData_pg_sequence new;

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

309
	/* allow ALTER to sequence owner only */
B
Bruce Momjian 已提交
310
	if (!pg_class_ownercheck(elm->relid, GetUserId()))
311 312
		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
					   stmt->sequence->relname);
B
Bruce Momjian 已提交
313 314

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

318
	/* copy old values of options */
B
Bruce Momjian 已提交
319
	new.increment_by = seq->increment_by;
B
Bruce Momjian 已提交
320
	new.max_value = seq->max_value;
B
Bruce Momjian 已提交
321 322 323 324 325
	new.min_value = seq->min_value;
	new.cache_value = seq->cache_value;
	new.is_cycled = seq->is_cycled;
	new.last_value = seq->last_value;

326 327
	/* Check and set new values */
	init_params(stmt->options, &new, false);
B
Bruce Momjian 已提交
328

329
	/* Now okay to update the on-disk tuple */
B
Bruce Momjian 已提交
330 331 332 333 334 335 336 337 338 339 340 341
	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;
	}

342
	/* save info in local cache */
B
Bruce Momjian 已提交
343 344 345
	elm->last = new.last_value; /* last returned number */
	elm->cached = new.last_value;		/* last cached number (forget
										 * cached values) */
346

B
Bruce Momjian 已提交
347 348 349 350 351 352 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
	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);
}

383

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

406
	sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
B
Bruce Momjian 已提交
407
															 "nextval"));
408

V
Vadim B. Mikheev 已提交
409
	/* open and AccessShareLock sequence */
410
	init_sequence(sequence, &elm, &seqrel);
411

412
	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
413 414
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
415
				 errmsg("permission denied for sequence %s",
416
						sequence->relname)));
417 418 419 420

	if (elm->last != elm->cached)		/* some numbers were cached */
	{
		elm->last += elm->increment;
421
		relation_close(seqrel, NoLock);
422
		PG_RETURN_INT64(elm->last);
423
	}
424

425
	/* lock page' buffer and read tuple */
426
	seq = read_info(elm, seqrel, &buf);
427
	page = BufferGetPage(buf);
428

V
Vadim B. Mikheev 已提交
429
	last = next = result = seq->last_value;
430 431 432
	incby = seq->increment_by;
	maxv = seq->max_value;
	minv = seq->min_value;
V
Vadim B. Mikheev 已提交
433 434
	fetch = cache = seq->cache_value;
	log = seq->log_cnt;
435

436
	if (!seq->is_called)
V
Vadim B. Mikheev 已提交
437
	{
438
		rescnt++;				/* last_value if not called */
V
Vadim B. Mikheev 已提交
439 440 441
		fetch--;
		log--;
	}
442

443
	/*
B
Bruce Momjian 已提交
444
	 * Decide whether we should emit a WAL log record.	If so, force up
445 446 447
	 * 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 已提交
448 449
	 * 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
450
	 * checkpoint would fail to advance the sequence past the logged
B
Bruce Momjian 已提交
451
	 * values.	In this case we may as well fetch extra values.
452
	 */
V
Vadim B. Mikheev 已提交
453 454
	if (log < fetch)
	{
455 456
		/* forced log to satisfy local demand for values */
		fetch = log = fetch + SEQ_LOG_VALS;
V
Vadim B. Mikheev 已提交
457 458
		logit = true;
	}
459 460 461 462 463 464 465 466 467 468 469
	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 已提交
470

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

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

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

534 535 536
	log -= fetch;				/* adjust for any unfetched numbers */
	Assert(log >= 0);

537 538
	/* save info in local cache */
	elm->last = result;			/* last returned number */
V
Vadim B. Mikheev 已提交
539 540
	elm->cached = last;			/* last fetched number */

541
	START_CRIT_SECTION();
542 543 544

	/* XLOG stuff */
	if (logit && !seqrel->rd_istemp)
V
Vadim B. Mikheev 已提交
545 546 547
	{
		xl_seq_rec	xlrec;
		XLogRecPtr	recptr;
B
Bruce Momjian 已提交
548
		XLogRecData rdata[2];
V
Vadim B. Mikheev 已提交
549

550
		xlrec.node = seqrel->rd_node;
551
		rdata[0].buffer = InvalidBuffer;
B
Bruce Momjian 已提交
552
		rdata[0].data = (char *) &xlrec;
553 554 555
		rdata[0].len = sizeof(xl_seq_rec);
		rdata[0].next = &(rdata[1]);

556
		/* set values that will be saved in xlog */
557
		seq->last_value = next;
558
		seq->is_called = true;
559
		seq->log_cnt = 0;
560

561
		rdata[1].buffer = InvalidBuffer;
B
Bruce Momjian 已提交
562 563 564
		rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper;
		rdata[1].len = ((PageHeader) page)->pd_special -
			((PageHeader) page)->pd_upper;
565 566
		rdata[1].next = NULL;

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

569 570
		PageSetLSN(page, recptr);
		PageSetSUI(page, ThisStartUpID);
V
Vadim B. Mikheev 已提交
571
	}
572

573
	/* update on-disk data */
V
Vadim B. Mikheev 已提交
574
	seq->last_value = last;		/* last fetched number */
575
	seq->is_called = true;
V
Vadim B. Mikheev 已提交
576
	seq->log_cnt = log;			/* how much is logged */
577

578
	END_CRIT_SECTION();
579

V
Vadim B. Mikheev 已提交
580 581
	LockBuffer(buf, BUFFER_LOCK_UNLOCK);

B
Bruce Momjian 已提交
582
	WriteBuffer(buf);
583

584 585
	relation_close(seqrel, NoLock);

586
	PG_RETURN_INT64(result);
587 588
}

589 590
Datum
currval(PG_FUNCTION_ARGS)
591
{
592
	text	   *seqin = PG_GETARG_TEXT_P(0);
593
	RangeVar   *sequence;
594
	SeqTable	elm;
595
	Relation	seqrel;
596
	int64		result;
597

598
	sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
B
Bruce Momjian 已提交
599
															 "currval"));
600

V
Vadim B. Mikheev 已提交
601
	/* open and AccessShareLock sequence */
602
	init_sequence(sequence, &elm, &seqrel);
603

604
	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK)
605 606
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
607
				 errmsg("permission denied for sequence %s",
608
						sequence->relname)));
609

610
	if (elm->increment == 0)	/* nextval/read_info were not called */
611 612
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
613
				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
614
						sequence->relname)));
615 616 617

	result = elm->last;

618 619
	relation_close(seqrel, NoLock);

620
	PG_RETURN_INT64(result);
621 622
}

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

644
	/* open and AccessShareLock sequence */
645
	init_sequence(sequence, &elm, &seqrel);
646 647

	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
648 649
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
650
				 errmsg("permission denied for sequence %s",
651
						sequence->relname)));
M
 
Marc G. Fournier 已提交
652

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

656
	if ((next < seq->min_value) || (next > seq->max_value))
657
	{
B
Bruce Momjian 已提交
658 659 660 661
		char		bufv[100],
					bufm[100],
					bufx[100];

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

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

676
	START_CRIT_SECTION();
677 678 679

	/* XLOG stuff */
	if (!seqrel->rd_istemp)
V
Vadim B. Mikheev 已提交
680 681 682
	{
		xl_seq_rec	xlrec;
		XLogRecPtr	recptr;
B
Bruce Momjian 已提交
683
		XLogRecData rdata[2];
684
		Page		page = BufferGetPage(buf);
V
Vadim B. Mikheev 已提交
685

686
		xlrec.node = seqrel->rd_node;
687
		rdata[0].buffer = InvalidBuffer;
B
Bruce Momjian 已提交
688
		rdata[0].data = (char *) &xlrec;
689 690 691
		rdata[0].len = sizeof(xl_seq_rec);
		rdata[0].next = &(rdata[1]);

692
		/* set values that will be saved in xlog */
693
		seq->last_value = next;
694
		seq->is_called = true;
695
		seq->log_cnt = 0;
696

697
		rdata[1].buffer = InvalidBuffer;
B
Bruce Momjian 已提交
698 699 700
		rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper;
		rdata[1].len = ((PageHeader) page)->pd_special -
			((PageHeader) page)->pd_upper;
701 702
		rdata[1].next = NULL;

B
Bruce Momjian 已提交
703
		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
704 705 706

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

709 710
	/* save info in sequence relation */
	seq->last_value = next;		/* last fetched number */
711
	seq->is_called = iscalled;
712
	seq->log_cnt = (iscalled) ? 0 : 1;
713

714
	END_CRIT_SECTION();
M
 
Marc G. Fournier 已提交
715

V
Vadim B. Mikheev 已提交
716 717
	LockBuffer(buf, BUFFER_LOCK_UNLOCK);

B
Bruce Momjian 已提交
718
	WriteBuffer(buf);
719 720

	relation_close(seqrel, NoLock);
721 722
}

723 724 725 726
/*
 * Implement the 2 arg setval procedure.
 * See do_setval for discussion.
 */
727 728 729 730
Datum
setval(PG_FUNCTION_ARGS)
{
	text	   *seqin = PG_GETARG_TEXT_P(0);
731
	int64		next = PG_GETARG_INT64(1);
732 733 734
	RangeVar   *sequence;

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

737
	do_setval(sequence, next, true);
738

739
	PG_RETURN_INT64(next);
740 741
}

742 743 744 745
/*
 * Implement the 3 arg setval procedure.
 * See do_setval for discussion.
 */
746 747 748 749
Datum
setval_and_iscalled(PG_FUNCTION_ARGS)
{
	text	   *seqin = PG_GETARG_TEXT_P(0);
750
	int64		next = PG_GETARG_INT64(1);
751
	bool		iscalled = PG_GETARG_BOOL(2);
752
	RangeVar   *sequence;
753

754
	sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
B
Bruce Momjian 已提交
755
															  "setval"));
756

757
	do_setval(sequence, next, iscalled);
758

759
	PG_RETURN_INT64(next);
M
 
Marc G. Fournier 已提交
760 761
}

762

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

775
	/* Look to see if we already have a seqtable entry for relation */
776
	for (elm = seqtab; elm != NULL; elm = elm->next)
777
	{
778
		if (elm->relid == relid)
779 780 781
			break;
	}

782 783 784 785 786 787 788 789
	/*
	 * 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);
790

791
	if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
792 793 794 795
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("\"%s\" is not a sequence",
						relation->relname)));
796

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

822 823 824 825 826
	/* Flag that we have a lock in the current xact. */
	elm->xid = thisxid;

	*p_elm = elm;
	*p_rel = seqrel;
827 828 829
}


830 831
/* Given an opened relation, lock the page buffer and find the tuple */
static Form_pg_sequence
832
read_info(SeqTable elm, Relation rel, Buffer *buf)
833
{
834 835 836 837 838
	PageHeader	page;
	ItemId		lp;
	HeapTupleData tuple;
	sequence_magic *sm;
	Form_pg_sequence seq;
839

840
	if (rel->rd_nblocks > 1)
841 842
		elog(ERROR, "invalid number of blocks in sequence \"%s\"",
			 RelationGetRelationName(rel));
843 844 845

	*buf = ReadBuffer(rel, 0);
	if (!BufferIsValid(*buf))
846
		elog(ERROR, "ReadBuffer failed");
847 848 849 850 851 852 853

	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);

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

	if (sm->magic != SEQ_MAGIC)
854 855
		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
			 RelationGetRelationName(rel), sm->magic);
856 857 858 859 860 861 862 863 864 865

	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;
866 867
}

868 869 870 871 872 873 874
/*
 * 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.
 */
875
static void
876
init_params(List *options, Form_pg_sequence new, bool isInit)
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
	DefElem    *is_cycled = NULL;
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)
938 939 940
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
941
			is_cycled = defel;
942
		}
943
		else
944
			elog(ERROR, "option \"%s\" not recognized",
945 946 947
				 defel->defname);
	}

B
Bruce Momjian 已提交
948
	/* INCREMENT BY */
949
	if (increment_by != (DefElem *) NULL)
B
Bruce Momjian 已提交
950 951
	{
		new->increment_by = defGetInt64(increment_by);
952 953 954
		if (new->increment_by == 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
955
					 errmsg("INCREMENT must not be zero")));
B
Bruce Momjian 已提交
956
	}
957 958 959 960 961 962 963 964 965 966 967
	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;
968

969 970 971 972 973 974
	/* 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)
975
	{
976
		if (new->increment_by > 0)
B
Bruce Momjian 已提交
977
			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
978
		else
979
			new->max_value = -1;				/* descending seq */
980
	}
981

982 983 984 985 986 987
	/* 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)
988
	{
989
		if (new->increment_by > 0)
990
			new->min_value = 1;					/* ascending seq */
991
		else
B
Bruce Momjian 已提交
992
			new->min_value = SEQ_MINVALUE;		/* descending seq */
993
	}
994

995
	/* crosscheck min/max */
996
	if (new->min_value >= new->max_value)
997
	{
B
Bruce Momjian 已提交
998 999 1000
		char		bufm[100],
					bufx[100];

1001 1002
		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
		snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value);
1003 1004 1005 1006
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("MINVALUE (%s) must be less than MAXVALUE (%s)",
						bufm, bufx)));
1007
	}
1008

B
Bruce Momjian 已提交
1009
	/* START WITH */
1010 1011 1012
	if (last_value != (DefElem *) NULL)
		new->last_value = defGetInt64(last_value);
	else if (isInit)
1013
	{
1014 1015 1016 1017
		if (new->increment_by > 0)
			new->last_value = new->min_value;	/* ascending seq */
		else
			new->last_value = new->max_value;	/* descending seq */
1018
	}
1019

1020
	/* crosscheck */
1021
	if (new->last_value < new->min_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->min_value);
1028 1029
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
1030 1031
			  errmsg("START value (%s) can't be less than MINVALUE (%s)",
					 bufs, bufm)));
1032
	}
1033
	if (new->last_value > new->max_value)
1034
	{
B
Bruce Momjian 已提交
1035 1036 1037
		char		bufs[100],
					bufm[100];

1038 1039
		snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->last_value);
		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
1040 1041
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
B
Bruce Momjian 已提交
1042 1043
		   errmsg("START value (%s) can't be greater than MAXVALUE (%s)",
				  bufs, bufm)));
1044
	}
1045

B
Bruce Momjian 已提交
1046
	/* CACHE */
1047
	if (cache_value != (DefElem *) NULL)
1048
	{
1049 1050 1051 1052
		new->cache_value = defGetInt64(cache_value);
		if (new->cache_value <= 0)
		{
			char		buf[100];
B
Bruce Momjian 已提交
1053

1054 1055 1056 1057 1058 1059
			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)));
		}
1060
	}
1061 1062
	else if (isInit)
		new->cache_value = 1;
1063 1064
}

V
Vadim B. Mikheev 已提交
1065

B
Bruce Momjian 已提交
1066 1067
void
seq_redo(XLogRecPtr lsn, XLogRecord *record)
V
Vadim B. Mikheev 已提交
1068
{
B
Bruce Momjian 已提交
1069 1070 1071 1072 1073 1074 1075
	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);
1076
	sequence_magic *sm;
V
Vadim B. Mikheev 已提交
1077

1078
	if (info != XLOG_SEQ_LOG)
1079
		elog(PANIC, "seq_redo: unknown op code %u", info);
V
Vadim B. Mikheev 已提交
1080 1081 1082 1083 1084

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

1085
	buffer = XLogReadBuffer(true, reln, 0);
V
Vadim B. Mikheev 已提交
1086
	if (!BufferIsValid(buffer))
1087
		elog(PANIC, "seq_redo: can't read block of %u/%u",
B
Bruce Momjian 已提交
1088
			 xlrec->node.tblNode, xlrec->node.relNode);
V
Vadim B. Mikheev 已提交
1089 1090 1091

	page = (Page) BufferGetPage(buffer);

1092 1093
	/* Always reinit the page and reinstall the magic number */
	/* See comments in DefineSequence */
1094 1095 1096
	PageInit((Page) page, BufferGetPageSize(buffer), sizeof(sequence_magic));
	sm = (sequence_magic *) PageGetSpecialPointer(page);
	sm->magic = SEQ_MAGIC;
V
Vadim B. Mikheev 已提交
1097

B
Bruce Momjian 已提交
1098
	item = (char *) xlrec + sizeof(xl_seq_rec);
1099 1100
	itemsz = record->xl_len - sizeof(xl_seq_rec);
	itemsz = MAXALIGN(itemsz);
B
Bruce Momjian 已提交
1101
	if (PageAddItem(page, (Item) item, itemsz,
1102
					FirstOffsetNumber, LP_USED) == InvalidOffsetNumber)
1103
		elog(PANIC, "seq_redo: failed to add item to page");
V
Vadim B. Mikheev 已提交
1104 1105 1106

	PageSetLSN(page, lsn);
	PageSetSUI(page, ThisStartUpID);
1107 1108
	LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
	WriteBuffer(buffer);
V
Vadim B. Mikheev 已提交
1109 1110
}

B
Bruce Momjian 已提交
1111 1112
void
seq_undo(XLogRecPtr lsn, XLogRecord *record)
V
Vadim B. Mikheev 已提交
1113 1114 1115
{
}

B
Bruce Momjian 已提交
1116 1117
void
seq_desc(char *buf, uint8 xl_info, char *rec)
V
Vadim B. Mikheev 已提交
1118
{
B
Bruce Momjian 已提交
1119 1120
	uint8		info = xl_info & ~XLR_INFO_MASK;
	xl_seq_rec *xlrec = (xl_seq_rec *) rec;
V
Vadim B. Mikheev 已提交
1121 1122 1123 1124 1125 1126 1127 1128 1129

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

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