sequence.c 13.4 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * sequence.c
4
 *	  PostgreSQL sequences support code.
5 6 7 8
 *
 *-------------------------------------------------------------------------
 */

9
#include "postgres.h"
10

11 12 13
#include "access/heapam.h"
#include "commands/creatinh.h"
#include "commands/sequence.h"
B
Bruce Momjian 已提交
14
#include "miscadmin.h"
15
#include "utils/acl.h"
B
Bruce Momjian 已提交
16
#include "utils/builtins.h"
17

18
#define SEQ_MAGIC	  0x1717
19 20 21 22

#define SEQ_MAXVALUE	((int4)0x7FFFFFFF)
#define SEQ_MINVALUE	-(SEQ_MAXVALUE)

23 24
typedef struct FormData_pg_sequence
{
25 26 27 28 29 30 31 32
	NameData	sequence_name;
	int4		last_value;
	int4		increment_by;
	int4		max_value;
	int4		min_value;
	int4		cache_value;
	char		is_cycled;
	char		is_called;
33
} FormData_pg_sequence;
34

35
typedef FormData_pg_sequence *Form_pg_sequence;
36 37 38

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

42 43
typedef struct SeqTableData
{
44 45 46 47 48 49
	char	   *name;
	Oid			relid;
	Relation	rel;
	int4		cached;
	int4		last;
	int4		increment;
50
	struct SeqTableData *next;
51
} SeqTableData;
52 53 54 55 56

typedef SeqTableData *SeqTable;

static SeqTable seqtab = NULL;

57
static SeqTable init_sequence(char *caller, char *name);
58 59
static Form_pg_sequence read_info(char *caller, SeqTable elm, Buffer *buf);
static void init_params(CreateSeqStmt *seq, Form_pg_sequence new);
60
static int	get_param(DefElem *def);
61 62

/*
B
Bruce Momjian 已提交
63
 * DefineSequence
64
 *				Creates a new sequence relation
65 66
 */
void
67
DefineSequence(CreateSeqStmt *seq)
68
{
69
	FormData_pg_sequence new;
70 71 72 73 74 75
	CreateStmt *stmt = makeNode(CreateStmt);
	ColumnDef  *coldef;
	TypeName   *typnam;
	Relation	rel;
	Buffer		buf;
	PageHeader	page;
76
	sequence_magic *sm;
77 78 79 80 81
	HeapTuple	tuple;
	TupleDesc	tupDesc;
	Datum		value[SEQ_COL_LASTCOL];
	char		null[SEQ_COL_LASTCOL];
	int			i;
82
	NameData	name;
83 84 85 86 87

	/* Check and set values */
	init_params(seq, &new);

	/*
88
	 * Create relation (and fill *null & *value)
89 90 91
	 */
	stmt->tableElts = NIL;
	for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
92
	{
93 94 95
		typnam = makeNode(TypeName);
		typnam->setof = FALSE;
		typnam->arrayBounds = NULL;
B
Bruce Momjian 已提交
96
		typnam->typmod = -1;
97 98
		coldef = makeNode(ColumnDef);
		coldef->typename = typnam;
99 100
		coldef->raw_default = NULL;
		coldef->cooked_default = NULL;
101 102 103 104 105
		coldef->is_not_null = false;
		null[i - 1] = ' ';

		switch (i)
		{
106 107 108
			case SEQ_COL_NAME:
				typnam->name = "name";
				coldef->colname = "sequence_name";
109 110
				namestrcpy(&name, seq->seqname);
				value[i - 1] = NameGetDatum(&name);
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
				break;
			case SEQ_COL_LASTVAL:
				typnam->name = "int4";
				coldef->colname = "last_value";
				value[i - 1] = Int32GetDatum(new.last_value);
				break;
			case SEQ_COL_INCBY:
				typnam->name = "int4";
				coldef->colname = "increment_by";
				value[i - 1] = Int32GetDatum(new.increment_by);
				break;
			case SEQ_COL_MAXVALUE:
				typnam->name = "int4";
				coldef->colname = "max_value";
				value[i - 1] = Int32GetDatum(new.max_value);
				break;
			case SEQ_COL_MINVALUE:
				typnam->name = "int4";
				coldef->colname = "min_value";
				value[i - 1] = Int32GetDatum(new.min_value);
				break;
			case SEQ_COL_CACHE:
				typnam->name = "int4";
				coldef->colname = "cache_value";
				value[i - 1] = Int32GetDatum(new.cache_value);
				break;
			case SEQ_COL_CYCLE:
				typnam->name = "char";
				coldef->colname = "is_cycled";
				value[i - 1] = CharGetDatum(new.is_cycled);
				break;
			case SEQ_COL_CALLED:
				typnam->name = "char";
				coldef->colname = "is_called";
				value[i - 1] = CharGetDatum('f');
				break;
147 148 149 150 151 152 153 154
		}
		stmt->tableElts = lappend(stmt->tableElts, coldef);
	}

	stmt->relname = seq->seqname;
	stmt->inhRelnames = NIL;
	stmt->constraints = NIL;

155
	DefineRelation(stmt, RELKIND_SEQUENCE);
156

157
	rel = heap_openr(seq->seqname, AccessExclusiveLock);
158

159
	tupDesc = RelationGetDescr(rel);
160 161 162 163 164

	Assert(RelationGetNumberOfBlocks(rel) == 0);
	buf = ReadBuffer(rel, P_NEW);

	if (!BufferIsValid(buf))
165
		elog(ERROR, "DefineSequence: ReadBuffer failed");
166 167 168 169 170 171 172 173 174 175 176 177

	page = (PageHeader) BufferGetPage(buf);

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

	/* Now - form & insert sequence tuple */
	tuple = heap_formtuple(tupDesc, value, null);
	heap_insert(rel, tuple);

	if (WriteBuffer(buf) == STATUS_ERROR)
178
		elog(ERROR, "DefineSequence: WriteBuffer failed");
179

180
	heap_close(rel, AccessExclusiveLock);
181 182 183 184
}


int4
185
nextval(struct varlena * seqin)
186
{
187 188 189
	char	   *seqname = textout(seqin);
	SeqTable	elm;
	Buffer		buf;
190
	Form_pg_sequence seq;
191 192 193 194 195 196 197
	int4		incby,
				maxv,
				minv,
				cache;
	int4		result,
				next,
				rescnt = 0;
198

V
Vadim B. Mikheev 已提交
199
	/* open and AccessShareLock sequence */
200 201 202 203 204 205
	elm = init_sequence("nextval", seqname);
	pfree(seqname);

	if (elm->last != elm->cached)		/* some numbers were cached */
	{
		elm->last += elm->increment;
206
		return elm->last;
207
	}
208

B
Bruce Momjian 已提交
209 210
	seq = read_info("nextval", elm, &buf);		/* lock page' buffer and
												 * read tuple */
211 212 213 214 215 216 217 218 219 220 221

	next = result = seq->last_value;
	incby = seq->increment_by;
	maxv = seq->max_value;
	minv = seq->min_value;
	cache = seq->cache_value;

	if (seq->is_called != 't')
		rescnt++;				/* last_value if not called */

	while (rescnt < cache)		/* try to fetch cache numbers */
222
	{
223 224 225 226 227 228 229 230 231 232 233 234 235

		/*
		 * Check MAXVALUE for ascending sequences and MINVALUE for
		 * descending sequences
		 */
		if (incby > 0)			/* ascending sequence */
		{
			if ((maxv >= 0 && next > maxv - incby) ||
				(maxv < 0 && next + incby > maxv))
			{
				if (rescnt > 0)
					break;		/* stop caching */
				if (seq->is_cycled != 't')
236
					elog(ERROR, "%s.nextval: got MAXVALUE (%d)",
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
						 elm->name, maxv);
				next = minv;
			}
			else
				next += incby;
		}
		else
/* descending sequence */
		{
			if ((minv < 0 && next < minv - incby) ||
				(minv >= 0 && next + incby < minv))
			{
				if (rescnt > 0)
					break;		/* stop caching */
				if (seq->is_cycled != 't')
252
					elog(ERROR, "%s.nextval: got MINVALUE (%d)",
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
						 elm->name, minv);
				next = maxv;
			}
			else
				next += incby;
		}
		rescnt++;				/* got result */
		if (rescnt == 1)		/* if it's first one - */
			result = next;		/* it's what to return */
	}

	/* save info in local cache */
	elm->last = result;			/* last returned number */
	elm->cached = next;			/* last cached number */

	/* save info in sequence relation */
	seq->last_value = next;		/* last fetched number */
	seq->is_called = 't';

V
Vadim B. Mikheev 已提交
272 273
	LockBuffer(buf, BUFFER_LOCK_UNLOCK);

274
	if (WriteBuffer(buf) == STATUS_ERROR)
275
		elog(ERROR, "%s.nextval: WriteBuffer failed", elm->name);
276

277
	return result;
278

279 280 281 282
}


int4
283
currval(struct varlena * seqin)
284
{
285 286 287
	char	   *seqname = textout(seqin);
	SeqTable	elm;
	int4		result;
288

V
Vadim B. Mikheev 已提交
289
	/* open and AccessShareLock sequence */
290 291 292 293
	elm = init_sequence("currval", seqname);
	pfree(seqname);

	if (elm->increment == 0)	/* nextval/read_info were not called */
294
		elog(ERROR, "%s.currval is not yet defined in this session", elm->name);
295 296 297

	result = elm->last;

298
	return result;
299

300 301
}

M
 
Marc G. Fournier 已提交
302 303 304
int4
setval(struct varlena * seqin, int4 next)
{
305
	char	   *seqname = textout(seqin);
M
 
Marc G. Fournier 已提交
306
	SeqTable	elm;
307
	Buffer		buf;
308
	Form_pg_sequence seq;
M
 
Marc G. Fournier 已提交
309 310 311 312 313 314 315

#ifndef NO_SECURITY
	if (pg_aclcheck(seqname, getpgusername(), ACL_WR) != ACLCHECK_OK)
		elog(ERROR, "%s.setval: you don't have permissions to set sequence %s",
			 seqname, seqname);
#endif

V
Vadim B. Mikheev 已提交
316
	/* open and AccessShareLock sequence */
317
	elm = init_sequence("setval", seqname);
B
Bruce Momjian 已提交
318 319
	seq = read_info("setval", elm, &buf);		/* lock page' buffer and
												 * read tuple */
M
 
Marc G. Fournier 已提交
320

321 322 323 324
	if (seq->cache_value != 1)
	{
		elog(ERROR, "%s.setval: can't set value of sequence %s, cache != 1",
			 seqname, seqname);
M
 
Marc G. Fournier 已提交
325 326
	}

327 328 329 330
	if ((next < seq->min_value) || (next > seq->max_value))
	{
		elog(ERROR, "%s.setval: value %d is of of bounds (%d,%d)",
			 seqname, next, seq->min_value, seq->max_value);
M
 
Marc G. Fournier 已提交
331 332 333 334 335 336 337 338 339 340
	}

	/* save info in local cache */
	elm->last = next;			/* last returned number */
	elm->cached = next;			/* last cached number */

	/* save info in sequence relation */
	seq->last_value = next;		/* last fetched number */
	seq->is_called = 't';

V
Vadim B. Mikheev 已提交
341 342
	LockBuffer(buf, BUFFER_LOCK_UNLOCK);

343 344
	if (WriteBuffer(buf) == STATUS_ERROR)
		elog(ERROR, "%s.settval: WriteBuffer failed", seqname);
M
 
Marc G. Fournier 已提交
345

346
	return next;
M
 
Marc G. Fournier 已提交
347 348
}

349
static Form_pg_sequence
B
Bruce Momjian 已提交
350
read_info(char *caller, SeqTable elm, Buffer *buf)
351
{
B
Bruce Momjian 已提交
352 353 354
	PageHeader	page;
	ItemId		lp;
	HeapTupleData tuple;
355
	sequence_magic *sm;
B
Bruce Momjian 已提交
356
	Form_pg_sequence seq;
357 358

	if (RelationGetNumberOfBlocks(elm->rel) != 1)
359
		elog(ERROR, "%s.%s: invalid number of blocks in sequence",
360 361 362 363
			 elm->name, caller);

	*buf = ReadBuffer(elm->rel, 0);
	if (!BufferIsValid(*buf))
364
		elog(ERROR, "%s.%s: ReadBuffer failed", elm->name, caller);
365

V
Vadim B. Mikheev 已提交
366 367
	LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);

368 369 370 371
	page = (PageHeader) BufferGetPage(*buf);
	sm = (sequence_magic *) PageGetSpecialPointer(page);

	if (sm->magic != SEQ_MAGIC)
372
		elog(ERROR, "%s.%s: bad magic (%08X)", elm->name, caller, sm->magic);
373 374 375

	lp = PageGetItemId(page, FirstOffsetNumber);
	Assert(ItemIdIsUsed(lp));
376
	tuple.t_data = (HeapTupleHeader) PageGetItem((Page) page, lp);
377

378
	seq = (Form_pg_sequence) GETSTRUCT(&tuple);
379 380 381

	elm->increment = seq->increment_by;

382
	return seq;
383 384 385 386

}


387
static SeqTable
388
init_sequence(char *caller, char *name)
389
{
390
	SeqTable	elm,
391 392
				prev = (SeqTable) NULL;
	Relation	seqrel;
393

394 395
	/* Look to see if we already have a seqtable entry for name */
	for (elm = seqtab; elm != (SeqTable) NULL; elm = elm->next)
396 397 398
	{
		if (strcmp(elm->name, name) == 0)
			break;
399
		prev = elm;
400 401
	}

402 403 404
	/* If so, and if it's already been opened in this xact, just return it */
	if (elm != (SeqTable) NULL && elm->rel != (Relation) NULL)
		return elm;
405

406 407 408
	/* Else open and check it */
	seqrel = heap_openr(name, AccessShareLock);
	if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE)
409
		elog(ERROR, "%s.%s: %s is not sequence !", name, caller, name);
410

411 412
	if (elm != (SeqTable) NULL)
	{
413 414 415

		/*
		 * We are using a seqtable entry left over from a previous xact;
416 417 418 419
		 * must check for relid change.
		 */
		elm->rel = seqrel;
		if (RelationGetRelid(seqrel) != elm->relid)
420 421
		{
			elog(NOTICE, "%s.%s: sequence was re-created",
422
				 name, caller);
423
			elm->relid = RelationGetRelid(seqrel);
424 425 426 427 428
			elm->cached = elm->last = elm->increment = 0;
		}
	}
	else
	{
429 430 431

		/*
		 * Time to make a new seqtable entry.  These entries live as long
432 433 434 435 436 437 438 439 440 441
		 * as the backend does, so we use plain malloc for them.
		 */
		elm = (SeqTable) malloc(sizeof(SeqTableData));
		elm->name = malloc(strlen(name) + 1);
		strcpy(elm->name, name);
		elm->rel = seqrel;
		elm->relid = RelationGetRelid(seqrel);
		elm->cached = elm->last = elm->increment = 0;
		elm->next = (SeqTable) NULL;

442 443 444
		if (seqtab == (SeqTable) NULL)
			seqtab = elm;
		else
445
			prev->next = elm;
446 447
	}

448
	return elm;
449 450 451 452
}


/*
B
Bruce Momjian 已提交
453
 * CloseSequences
454
 *				is calling by xact mgr at commit/abort.
455 456
 */
void
457
CloseSequences(void)
458
{
459 460
	SeqTable	elm;
	Relation	rel;
461

462
	for (elm = seqtab; elm != (SeqTable) NULL; elm = elm->next)
463
	{
464 465 466 467
		if (elm->rel != (Relation) NULL)		/* opened in current xact */
		{
			rel = elm->rel;
			elm->rel = (Relation) NULL;
468
			heap_close(rel, AccessShareLock);
469 470
		}
	}
471 472 473
}


474
static void
475
init_params(CreateSeqStmt *seq, Form_pg_sequence new)
476
{
477 478 479 480 481 482
	DefElem    *last_value = NULL;
	DefElem    *increment_by = NULL;
	DefElem    *max_value = NULL;
	DefElem    *min_value = NULL;
	DefElem    *cache_value = NULL;
	List	   *option;
483 484 485 486

	new->is_cycled = 'f';
	foreach(option, seq->options)
	{
487
		DefElem    *defel = (DefElem *) lfirst(option);
488 489 490 491 492 493 494 495 496 497 498 499 500 501

		if (!strcasecmp(defel->defname, "increment"))
			increment_by = defel;
		else if (!strcasecmp(defel->defname, "start"))
			last_value = defel;
		else if (!strcasecmp(defel->defname, "maxvalue"))
			max_value = defel;
		else if (!strcasecmp(defel->defname, "minvalue"))
			min_value = defel;
		else if (!strcasecmp(defel->defname, "cache"))
			cache_value = defel;
		else if (!strcasecmp(defel->defname, "cycle"))
		{
			if (defel->arg != (Node *) NULL)
502
				elog(ERROR, "DefineSequence: CYCLE ??");
503 504 505
			new->is_cycled = 't';
		}
		else
506
			elog(ERROR, "DefineSequence: option \"%s\" not recognized",
507 508 509 510 511 512
				 defel->defname);
	}

	if (increment_by == (DefElem *) NULL)		/* INCREMENT BY */
		new->increment_by = 1;
	else if ((new->increment_by = get_param(increment_by)) == 0)
513
		elog(ERROR, "DefineSequence: can't INCREMENT by 0");
514 515

	if (max_value == (DefElem *) NULL)	/* MAXVALUE */
516
	{
517 518 519 520
		if (new->increment_by > 0)
			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
		else
			new->max_value = -1;/* descending seq */
521
	}
522
	else
523
		new->max_value = get_param(max_value);
524

525
	if (min_value == (DefElem *) NULL)	/* MINVALUE */
526
	{
527 528 529 530
		if (new->increment_by > 0)
			new->min_value = 1; /* ascending seq */
		else
			new->min_value = SEQ_MINVALUE;		/* descending seq */
531
	}
532
	else
533 534 535
		new->min_value = get_param(min_value);

	if (new->min_value >= new->max_value)
536
		elog(ERROR, "DefineSequence: MINVALUE (%d) can't be >= MAXVALUE (%d)",
537 538 539
			 new->min_value, new->max_value);

	if (last_value == (DefElem *) NULL) /* START WITH */
540
	{
541 542 543 544
		if (new->increment_by > 0)
			new->last_value = new->min_value;	/* ascending seq */
		else
			new->last_value = new->max_value;	/* descending seq */
545
	}
546
	else
547 548 549
		new->last_value = get_param(last_value);

	if (new->last_value < new->min_value)
550
		elog(ERROR, "DefineSequence: START value (%d) can't be < MINVALUE (%d)",
551 552
			 new->last_value, new->min_value);
	if (new->last_value > new->max_value)
553
		elog(ERROR, "DefineSequence: START value (%d) can't be > MAXVALUE (%d)",
554 555 556 557 558
			 new->last_value, new->max_value);

	if (cache_value == (DefElem *) NULL)		/* CACHE */
		new->cache_value = 1;
	else if ((new->cache_value = get_param(cache_value)) <= 0)
559
		elog(ERROR, "DefineSequence: CACHE (%d) can't be <= 0",
560
			 new->cache_value);
561 562 563 564 565

}


static int
566
get_param(DefElem *def)
567
{
568
	if (def->arg == (Node *) NULL)
569
		elog(ERROR, "DefineSequence: \"%s\" value unspecified", def->defname);
570 571

	if (nodeTag(def->arg) == T_Integer)
572
		return intVal(def->arg);
573

574
	elog(ERROR, "DefineSequence: \"%s\" is to be integer", def->defname);
575
	return -1;
576
}