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
 *	  $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.112 2004/06/18 06:13:23 tgl 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 = MUST_NOT_HAVE_OIDS;
182
	stmt->oncommit = ONCOMMIT_NOOP;
183
	stmt->tablespacename = seq->tablespacename;
184

185
	seqoid = DefineRelation(stmt, RELKIND_SEQUENCE);
186

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

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

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

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

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

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

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

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

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

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

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

231
	START_CRIT_SECTION();
232 233 234

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

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

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

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

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

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

285
	END_CRIT_SECTION();
286

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

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

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

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

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

319 320
	/* Copy old values of options into workspace */
	memcpy(&new, seq, sizeof(FormData_pg_sequence));
B
Bruce Momjian 已提交
321

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

325
	/* Now okay to update the on-disk tuple */
326
	memcpy(seq, &new, sizeof(FormData_pg_sequence));
B
Bruce Momjian 已提交
327

328 329
	/* Clear local cache so that we don't think we have cached numbers */
	elm->last = new.last_value;			/* last returned number */
B
Bruce Momjian 已提交
330 331
	elm->cached = new.last_value;		/* last cached number (forget
										 * cached values) */
332

B
Bruce Momjian 已提交
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
	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);
}

369

370 371
Datum
nextval(PG_FUNCTION_ARGS)
372
{
373
	text	   *seqin = PG_GETARG_TEXT_P(0);
374
	RangeVar   *sequence;
375
	SeqTable	elm;
376
	Relation	seqrel;
377
	Buffer		buf;
378
	Page		page;
379
	Form_pg_sequence seq;
380
	int64		incby,
381 382
				maxv,
				minv,
V
Vadim B. Mikheev 已提交
383 384 385 386
				cache,
				log,
				fetch,
				last;
387
	int64		result,
388 389
				next,
				rescnt = 0;
V
Vadim B. Mikheev 已提交
390
	bool		logit = false;
391

392
	sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
B
Bruce Momjian 已提交
393
															 "nextval"));
394

V
Vadim B. Mikheev 已提交
395
	/* open and AccessShareLock sequence */
396
	init_sequence(sequence, &elm, &seqrel);
397

398
	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
399 400
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
401
				 errmsg("permission denied for sequence %s",
402
						sequence->relname)));
403 404 405 406

	if (elm->last != elm->cached)		/* some numbers were cached */
	{
		elm->last += elm->increment;
407
		relation_close(seqrel, NoLock);
408
		PG_RETURN_INT64(elm->last);
409
	}
410

411
	/* lock page' buffer and read tuple */
412
	seq = read_info(elm, seqrel, &buf);
413
	page = BufferGetPage(buf);
414

V
Vadim B. Mikheev 已提交
415
	last = next = result = seq->last_value;
416 417 418
	incby = seq->increment_by;
	maxv = seq->max_value;
	minv = seq->min_value;
V
Vadim B. Mikheev 已提交
419 420
	fetch = cache = seq->cache_value;
	log = seq->log_cnt;
421

422
	if (!seq->is_called)
V
Vadim B. Mikheev 已提交
423
	{
424
		rescnt++;				/* last_value if not called */
V
Vadim B. Mikheev 已提交
425 426 427
		fetch--;
		log--;
	}
428

429
	/*
B
Bruce Momjian 已提交
430
	 * Decide whether we should emit a WAL log record.	If so, force up
431 432 433
	 * 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 已提交
434 435
	 * 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
436
	 * checkpoint would fail to advance the sequence past the logged
B
Bruce Momjian 已提交
437
	 * values.	In this case we may as well fetch extra values.
438
	 */
V
Vadim B. Mikheev 已提交
439 440
	if (log < fetch)
	{
441 442
		/* forced log to satisfy local demand for values */
		fetch = log = fetch + SEQ_LOG_VALS;
V
Vadim B. Mikheev 已提交
443 444
		logit = true;
	}
445 446 447 448 449 450 451 452 453 454 455
	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 已提交
456

B
Bruce Momjian 已提交
457
	while (fetch)				/* try to fetch cache [+ log ] numbers */
458
	{
459 460 461 462
		/*
		 * Check MAXVALUE for ascending sequences and MINVALUE for
		 * descending sequences
		 */
463
		if (incby > 0)
464
		{
465
			/* ascending sequence */
466 467 468 469
			if ((maxv >= 0 && next > maxv - incby) ||
				(maxv < 0 && next + incby > maxv))
			{
				if (rescnt > 0)
V
Vadim B. Mikheev 已提交
470
					break;		/* stop fetching */
471
				if (!seq->is_cycled)
472
				{
B
Bruce Momjian 已提交
473 474
					char		buf[100];

475
					snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
476
					ereport(ERROR,
B
Bruce Momjian 已提交
477
					  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
478
					   errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
B
Bruce Momjian 已提交
479
							  sequence->relname, buf)));
480
				}
481 482 483 484 485 486 487
				next = minv;
			}
			else
				next += incby;
		}
		else
		{
488
			/* descending sequence */
489 490 491 492
			if ((minv < 0 && next < minv - incby) ||
				(minv >= 0 && next + incby < minv))
			{
				if (rescnt > 0)
V
Vadim B. Mikheev 已提交
493
					break;		/* stop fetching */
494
				if (!seq->is_cycled)
495
				{
B
Bruce Momjian 已提交
496 497
					char		buf[100];

498
					snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
499
					ereport(ERROR,
B
Bruce Momjian 已提交
500
					  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
501
					   errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
B
Bruce Momjian 已提交
502
							  sequence->relname, buf)));
503
				}
504 505 506 507 508
				next = maxv;
			}
			else
				next += incby;
		}
V
Vadim B. Mikheev 已提交
509 510 511 512 513 514
		fetch--;
		if (rescnt < cache)
		{
			log--;
			rescnt++;
			last = next;
B
Bruce Momjian 已提交
515 516
			if (rescnt == 1)	/* if it's first result - */
				result = next;	/* it's what to return */
V
Vadim B. Mikheev 已提交
517
		}
518 519
	}

520 521 522
	log -= fetch;				/* adjust for any unfetched numbers */
	Assert(log >= 0);

523 524
	/* save info in local cache */
	elm->last = result;			/* last returned number */
V
Vadim B. Mikheev 已提交
525 526
	elm->cached = last;			/* last fetched number */

527
	START_CRIT_SECTION();
528 529 530

	/* XLOG stuff */
	if (logit && !seqrel->rd_istemp)
V
Vadim B. Mikheev 已提交
531 532 533
	{
		xl_seq_rec	xlrec;
		XLogRecPtr	recptr;
B
Bruce Momjian 已提交
534
		XLogRecData rdata[2];
V
Vadim B. Mikheev 已提交
535

536
		xlrec.node = seqrel->rd_node;
537
		rdata[0].buffer = InvalidBuffer;
B
Bruce Momjian 已提交
538
		rdata[0].data = (char *) &xlrec;
539 540 541
		rdata[0].len = sizeof(xl_seq_rec);
		rdata[0].next = &(rdata[1]);

542
		/* set values that will be saved in xlog */
543
		seq->last_value = next;
544
		seq->is_called = true;
545
		seq->log_cnt = 0;
546

547
		rdata[1].buffer = InvalidBuffer;
B
Bruce Momjian 已提交
548 549 550
		rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper;
		rdata[1].len = ((PageHeader) page)->pd_special -
			((PageHeader) page)->pd_upper;
551 552
		rdata[1].next = NULL;

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

555 556
		PageSetLSN(page, recptr);
		PageSetSUI(page, ThisStartUpID);
V
Vadim B. Mikheev 已提交
557
	}
558

559
	/* update on-disk data */
V
Vadim B. Mikheev 已提交
560
	seq->last_value = last;		/* last fetched number */
561
	seq->is_called = true;
V
Vadim B. Mikheev 已提交
562
	seq->log_cnt = log;			/* how much is logged */
563

564
	END_CRIT_SECTION();
565

V
Vadim B. Mikheev 已提交
566 567
	LockBuffer(buf, BUFFER_LOCK_UNLOCK);

B
Bruce Momjian 已提交
568
	WriteBuffer(buf);
569

570 571
	relation_close(seqrel, NoLock);

572
	PG_RETURN_INT64(result);
573 574
}

575 576
Datum
currval(PG_FUNCTION_ARGS)
577
{
578
	text	   *seqin = PG_GETARG_TEXT_P(0);
579
	RangeVar   *sequence;
580
	SeqTable	elm;
581
	Relation	seqrel;
582
	int64		result;
583

584
	sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin,
B
Bruce Momjian 已提交
585
															 "currval"));
586

V
Vadim B. Mikheev 已提交
587
	/* open and AccessShareLock sequence */
588
	init_sequence(sequence, &elm, &seqrel);
589

590
	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK)
591 592
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
593
				 errmsg("permission denied for sequence %s",
594
						sequence->relname)));
595

596
	if (elm->increment == 0)	/* nextval/read_info were not called */
597 598
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
599
				 errmsg("currval of sequence \"%s\" is not yet defined in this session",
600
						sequence->relname)));
601 602 603

	result = elm->last;

604 605
	relation_close(seqrel, NoLock);

606
	PG_RETURN_INT64(result);
607 608
}

B
Bruce Momjian 已提交
609
/*
610 611 612 613
 * 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 已提交
614
 * work if multiple users are attached to the database and referencing
615 616
 * the sequence (unlikely if pg_dump is restoring it).
 *
B
Bruce Momjian 已提交
617
 * It is necessary to have the 3 arg version so that pg_dump can
618 619 620 621
 * 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 已提交
622
static void
623
do_setval(RangeVar *sequence, int64 next, bool iscalled)
M
 
Marc G. Fournier 已提交
624 625
{
	SeqTable	elm;
626
	Relation	seqrel;
627
	Buffer		buf;
628
	Form_pg_sequence seq;
M
 
Marc G. Fournier 已提交
629

630
	/* open and AccessShareLock sequence */
631
	init_sequence(sequence, &elm, &seqrel);
632 633

	if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK)
634 635
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
636
				 errmsg("permission denied for sequence %s",
637
						sequence->relname)));
M
 
Marc G. Fournier 已提交
638

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

642
	if ((next < seq->min_value) || (next > seq->max_value))
643
	{
B
Bruce Momjian 已提交
644 645 646 647
		char		bufv[100],
					bufm[100],
					bufx[100];

648 649 650
		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);
651 652
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
653 654
				 errmsg("setval: value %s is out of bounds for sequence \"%s\" (%s..%s)",
						bufv, sequence->relname, bufm, bufx)));
655
	}
M
 
Marc G. Fournier 已提交
656 657 658

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

662
	START_CRIT_SECTION();
663 664 665

	/* XLOG stuff */
	if (!seqrel->rd_istemp)
V
Vadim B. Mikheev 已提交
666 667 668
	{
		xl_seq_rec	xlrec;
		XLogRecPtr	recptr;
B
Bruce Momjian 已提交
669
		XLogRecData rdata[2];
670
		Page		page = BufferGetPage(buf);
V
Vadim B. Mikheev 已提交
671

672
		xlrec.node = seqrel->rd_node;
673
		rdata[0].buffer = InvalidBuffer;
B
Bruce Momjian 已提交
674
		rdata[0].data = (char *) &xlrec;
675 676 677
		rdata[0].len = sizeof(xl_seq_rec);
		rdata[0].next = &(rdata[1]);

678
		/* set values that will be saved in xlog */
679
		seq->last_value = next;
680
		seq->is_called = true;
681
		seq->log_cnt = 0;
682

683
		rdata[1].buffer = InvalidBuffer;
B
Bruce Momjian 已提交
684 685 686
		rdata[1].data = (char *) page + ((PageHeader) page)->pd_upper;
		rdata[1].len = ((PageHeader) page)->pd_special -
			((PageHeader) page)->pd_upper;
687 688
		rdata[1].next = NULL;

B
Bruce Momjian 已提交
689
		recptr = XLogInsert(RM_SEQ_ID, XLOG_SEQ_LOG | XLOG_NO_TRAN, rdata);
690 691 692

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

695 696
	/* save info in sequence relation */
	seq->last_value = next;		/* last fetched number */
697
	seq->is_called = iscalled;
698
	seq->log_cnt = (iscalled) ? 0 : 1;
699

700
	END_CRIT_SECTION();
M
 
Marc G. Fournier 已提交
701

V
Vadim B. Mikheev 已提交
702 703
	LockBuffer(buf, BUFFER_LOCK_UNLOCK);

B
Bruce Momjian 已提交
704
	WriteBuffer(buf);
705 706

	relation_close(seqrel, NoLock);
707 708
}

709 710 711 712
/*
 * Implement the 2 arg setval procedure.
 * See do_setval for discussion.
 */
713 714 715 716
Datum
setval(PG_FUNCTION_ARGS)
{
	text	   *seqin = PG_GETARG_TEXT_P(0);
717
	int64		next = PG_GETARG_INT64(1);
718 719 720
	RangeVar   *sequence;

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

723
	do_setval(sequence, next, true);
724

725
	PG_RETURN_INT64(next);
726 727
}

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

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

743
	do_setval(sequence, next, iscalled);
744

745
	PG_RETURN_INT64(next);
M
 
Marc G. Fournier 已提交
746 747
}

748

749 750 751 752 753
/*
 * Given a relation name, open and lock the sequence.  p_elm and p_rel are
 * output parameters.
 */
static void
754
init_sequence(RangeVar *relation, SeqTable *p_elm, Relation *p_rel)
755
{
756
	Oid			relid = RangeVarGetRelid(relation, false);
757 758
	TransactionId thisxid = GetCurrentTransactionId();
	SeqTable	elm;
759
	Relation	seqrel;
B
Bruce Momjian 已提交
760

761
	/* Look to see if we already have a seqtable entry for relation */
762
	for (elm = seqtab; elm != NULL; elm = elm->next)
763
	{
764
		if (elm->relid == relid)
765 766 767
			break;
	}

768 769 770 771 772 773 774 775
	/*
	 * 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);
776

777
	if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
778 779 780 781
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("\"%s\" is not a sequence",
						relation->relname)));
782

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

808 809 810 811 812
	/* Flag that we have a lock in the current xact. */
	elm->xid = thisxid;

	*p_elm = elm;
	*p_rel = seqrel;
813 814 815
}


816 817
/* Given an opened relation, lock the page buffer and find the tuple */
static Form_pg_sequence
818
read_info(SeqTable elm, Relation rel, Buffer *buf)
819
{
820 821 822 823 824
	PageHeader	page;
	ItemId		lp;
	HeapTupleData tuple;
	sequence_magic *sm;
	Form_pg_sequence seq;
825

826 827
	*buf = ReadBuffer(rel, 0);
	if (!BufferIsValid(*buf))
828
		elog(ERROR, "ReadBuffer failed");
829 830 831 832 833 834 835

	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);

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

	if (sm->magic != SEQ_MAGIC)
836 837
		elog(ERROR, "bad magic number in sequence \"%s\": %08X",
			 RelationGetRelationName(rel), sm->magic);
838 839 840 841 842 843 844 845 846 847

	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;
848 849
}

850 851 852 853 854 855 856
/*
 * 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.
 */
857
static void
858
init_params(List *options, Form_pg_sequence new, bool isInit)
859
{
860 861 862 863 864
	DefElem    *last_value = NULL;
	DefElem    *increment_by = NULL;
	DefElem    *max_value = NULL;
	DefElem    *min_value = NULL;
	DefElem    *cache_value = NULL;
865
	DefElem    *is_cycled = NULL;
866
	ListCell   *option;
867

B
Bruce Momjian 已提交
868
	foreach(option, options)
869
	{
870
		DefElem    *defel = (DefElem *) lfirst(option);
871

872
		if (strcmp(defel->defname, "increment") == 0)
873 874
		{
			if (increment_by)
875 876 877
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
878
			increment_by = defel;
879
		}
B
Bruce Momjian 已提交
880

B
Bruce Momjian 已提交
881
		/*
B
Bruce Momjian 已提交
882
		 * start is for a new sequence restart is for alter
B
Bruce Momjian 已提交
883
		 */
884 885
		else if (strcmp(defel->defname, "start") == 0 ||
				 strcmp(defel->defname, "restart") == 0)
886 887
		{
			if (last_value)
888 889 890
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
891
			last_value = defel;
892
		}
893
		else if (strcmp(defel->defname, "maxvalue") == 0)
894 895
		{
			if (max_value)
896 897 898
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
899
			max_value = defel;
900
		}
901
		else if (strcmp(defel->defname, "minvalue") == 0)
902 903
		{
			if (min_value)
904 905 906
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
907
			min_value = defel;
908
		}
909
		else if (strcmp(defel->defname, "cache") == 0)
910 911
		{
			if (cache_value)
912 913 914
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
915
			cache_value = defel;
916
		}
917
		else if (strcmp(defel->defname, "cycle") == 0)
918
		{
919
			if (is_cycled)
920 921 922
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("conflicting or redundant options")));
923
			is_cycled = defel;
924
		}
925
		else
926
			elog(ERROR, "option \"%s\" not recognized",
927 928 929
				 defel->defname);
	}

B
Bruce Momjian 已提交
930
	/* INCREMENT BY */
931
	if (increment_by != NULL)
B
Bruce Momjian 已提交
932 933
	{
		new->increment_by = defGetInt64(increment_by);
934 935 936
		if (new->increment_by == 0)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
937
					 errmsg("INCREMENT must not be zero")));
B
Bruce Momjian 已提交
938
	}
939 940 941 942
	else if (isInit)
		new->increment_by = 1;

	/* CYCLE */
943
	if (is_cycled != NULL)
944 945 946 947 948 949
	{
		new->is_cycled = intVal(is_cycled->arg);
		Assert(new->is_cycled == false || new->is_cycled == true);
	}
	else if (isInit)
		new->is_cycled = false;
950

951
	/* MAXVALUE (null arg means NO MAXVALUE) */
952
	if (max_value != NULL && max_value->arg)
953 954 955
	{
		new->max_value = defGetInt64(max_value);
	}
956
	else if (isInit || max_value != NULL)
957
	{
958
		if (new->increment_by > 0)
B
Bruce Momjian 已提交
959
			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
960
		else
961
			new->max_value = -1;				/* descending seq */
962
	}
963

964
	/* MINVALUE (null arg means NO MINVALUE) */
965
	if (min_value != NULL && min_value->arg)
966 967 968
	{
		new->min_value = defGetInt64(min_value);
	}
969
	else if (isInit || min_value != NULL)
970
	{
971
		if (new->increment_by > 0)
972
			new->min_value = 1;					/* ascending seq */
973
		else
B
Bruce Momjian 已提交
974
			new->min_value = SEQ_MINVALUE;		/* descending seq */
975
	}
976

977
	/* crosscheck min/max */
978
	if (new->min_value >= new->max_value)
979
	{
B
Bruce Momjian 已提交
980 981 982
		char		bufm[100],
					bufx[100];

983 984
		snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
		snprintf(bufx, sizeof(bufx), INT64_FORMAT, new->max_value);
985 986 987 988
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("MINVALUE (%s) must be less than MAXVALUE (%s)",
						bufm, bufx)));
989
	}
990

B
Bruce Momjian 已提交
991
	/* START WITH */
992
	if (last_value != NULL)
993
	{
994
		new->last_value = defGetInt64(last_value);
995 996 997
		new->is_called = false;
		new->log_cnt = 1;
	}
998
	else if (isInit)
999
	{
1000 1001 1002 1003
		if (new->increment_by > 0)
			new->last_value = new->min_value;	/* ascending seq */
		else
			new->last_value = new->max_value;	/* descending seq */
1004 1005
		new->is_called = false;
		new->log_cnt = 1;
1006
	}
1007

1008
	/* crosscheck */
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
	/* CACHE */
1035
	if (cache_value != NULL)
1036
	{
1037 1038 1039 1040
		new->cache_value = defGetInt64(cache_value);
		if (new->cache_value <= 0)
		{
			char		buf[100];
B
Bruce Momjian 已提交
1041

1042 1043 1044 1045 1046 1047
			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)));
		}
1048
	}
1049 1050
	else if (isInit)
		new->cache_value = 1;
1051 1052
}

V
Vadim B. Mikheev 已提交
1053

B
Bruce Momjian 已提交
1054 1055
void
seq_redo(XLogRecPtr lsn, XLogRecord *record)
V
Vadim B. Mikheev 已提交
1056
{
B
Bruce Momjian 已提交
1057 1058 1059 1060 1061 1062 1063
	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);
1064
	sequence_magic *sm;
V
Vadim B. Mikheev 已提交
1065

1066
	if (info != XLOG_SEQ_LOG)
1067
		elog(PANIC, "seq_redo: unknown op code %u", info);
V
Vadim B. Mikheev 已提交
1068 1069 1070 1071 1072

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

1073
	buffer = XLogReadBuffer(true, reln, 0);
V
Vadim B. Mikheev 已提交
1074
	if (!BufferIsValid(buffer))
1075 1076
		elog(PANIC, "seq_redo: can't read block 0 of rel %u/%u/%u",
			 xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);
V
Vadim B. Mikheev 已提交
1077 1078 1079

	page = (Page) BufferGetPage(buffer);

1080 1081
	/* Always reinit the page and reinstall the magic number */
	/* See comments in DefineSequence */
1082 1083 1084
	PageInit((Page) page, BufferGetPageSize(buffer), sizeof(sequence_magic));
	sm = (sequence_magic *) PageGetSpecialPointer(page);
	sm->magic = SEQ_MAGIC;
V
Vadim B. Mikheev 已提交
1085

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

	PageSetLSN(page, lsn);
	PageSetSUI(page, ThisStartUpID);
1095 1096
	LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
	WriteBuffer(buffer);
V
Vadim B. Mikheev 已提交
1097 1098
}

B
Bruce Momjian 已提交
1099 1100
void
seq_undo(XLogRecPtr lsn, XLogRecord *record)
V
Vadim B. Mikheev 已提交
1101 1102 1103
{
}

B
Bruce Momjian 已提交
1104 1105
void
seq_desc(char *buf, uint8 xl_info, char *rec)
V
Vadim B. Mikheev 已提交
1106
{
B
Bruce Momjian 已提交
1107 1108
	uint8		info = xl_info & ~XLR_INFO_MASK;
	xl_seq_rec *xlrec = (xl_seq_rec *) rec;
V
Vadim B. Mikheev 已提交
1109 1110 1111 1112 1113 1114 1115 1116 1117

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

1118 1119
	sprintf(buf + strlen(buf), "rel %u/%u/%u",
			xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);
V
Vadim B. Mikheev 已提交
1120
}