sequence.c 28.3 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.110 2004/05/08 19:09:24 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

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 319
	/* Copy old values of options into workspace */
	memcpy(&new, seq, sizeof(FormData_pg_sequence));
B
Bruce Momjian 已提交
320

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

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

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

B
Bruce Momjian 已提交
332 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
	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);
}

368

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

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

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

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

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

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

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

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

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

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

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

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

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

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

526
	START_CRIT_SECTION();
527 528 529

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

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

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

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

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

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

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

563
	END_CRIT_SECTION();
564

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

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

569 570
	relation_close(seqrel, NoLock);

571
	PG_RETURN_INT64(result);
572 573
}

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

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

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

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

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

	result = elm->last;

603 604
	relation_close(seqrel, NoLock);

605
	PG_RETURN_INT64(result);
606 607
}

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

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

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

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

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

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

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

661
	START_CRIT_SECTION();
662 663 664

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

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

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

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

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

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

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

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

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

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

	relation_close(seqrel, NoLock);
706 707
}

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

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

722
	do_setval(sequence, next, true);
723

724
	PG_RETURN_INT64(next);
725 726
}

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

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

742
	do_setval(sequence, next, iscalled);
743

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

747

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

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

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

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

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

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

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


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

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

	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1007
	/* crosscheck */
1008
	if (new->last_value < new->min_value)
1009
	{
B
Bruce Momjian 已提交
1010 1011 1012
		char		bufs[100],
					bufm[100];

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

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

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

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

V
Vadim B. Mikheev 已提交
1052

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

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

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

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

	page = (Page) BufferGetPage(buffer);

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

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

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

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

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

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

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