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-2004, PostgreSQL Global Development Group
7 8 9 10
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
B
Bruce Momjian 已提交
11
 *	  $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.115 2004/08/29 04:12:30 momjian 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 = NULL;
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
		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);
282
		PageSetTLI(page, ThisTimeLineID);
283
	}
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
	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);
357
		PageSetTLI(page, ThisTimeLineID);
B
Bruce Momjian 已提交
358 359 360 361 362 363 364 365 366 367 368
	}

	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
		PageSetLSN(page, recptr);
556
		PageSetTLI(page, ThisTimeLineID);
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

		PageSetLSN(page, recptr);
692
		PageSetTLI(page, ThisTimeLineID);
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

	PageSetLSN(page, lsn);
1094
	PageSetTLI(page, ThisTimeLineID);
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
}