sequence.c 13.2 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 391 392
	SeqTable	elm,
				priv = (SeqTable) NULL;
	SeqTable	temp;
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414

	for (elm = seqtab; elm != (SeqTable) NULL;)
	{
		if (strcmp(elm->name, name) == 0)
			break;
		priv = elm;
		elm = elm->next;
	}

	if (elm == (SeqTable) NULL) /* not found */
	{
		temp = (SeqTable) malloc(sizeof(SeqTableData));
		temp->name = malloc(strlen(name) + 1);
		strcpy(temp->name, name);
		temp->rel = (Relation) NULL;
		temp->cached = temp->last = temp->increment = 0;
		temp->next = (SeqTable) NULL;
	}
	else
/* found */
	{
		if (elm->rel != (Relation) NULL)		/* already opened */
415
			return elm;
416 417 418
		temp = elm;
	}

419
	temp->rel = heap_openr(name, AccessShareLock);
420 421

	if (temp->rel->rd_rel->relkind != RELKIND_SEQUENCE)
422
		elog(ERROR, "%s.%s: %s is not sequence !", name, caller, name);
423 424 425

	if (elm != (SeqTable) NULL) /* we opened sequence from our */
	{							/* SeqTable - check relid ! */
426
		if (RelationGetRelid(elm->rel) != elm->relid)
427 428 429 430
		{
			elog(NOTICE, "%s.%s: sequence was re-created",
				 name, caller, name);
			elm->cached = elm->last = elm->increment = 0;
431
			elm->relid = RelationGetRelid(elm->rel);
432 433 434 435 436
		}
	}
	else
	{
		elm = temp;
437
		elm->relid = RelationGetRelid(elm->rel);
438 439 440 441 442 443
		if (seqtab == (SeqTable) NULL)
			seqtab = elm;
		else
			priv->next = elm;
	}

444
	return elm;
445 446 447 448
}


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

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


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

	new->is_cycled = 'f';
	foreach(option, seq->options)
	{
483
		DefElem    *defel = (DefElem *) lfirst(option);
484 485 486 487 488 489 490 491 492 493 494 495 496 497

		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)
498
				elog(ERROR, "DefineSequence: CYCLE ??");
499 500 501
			new->is_cycled = 't';
		}
		else
502
			elog(ERROR, "DefineSequence: option \"%s\" not recognized",
503 504 505 506 507 508
				 defel->defname);
	}

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

	if (max_value == (DefElem *) NULL)	/* MAXVALUE */
512
	{
513 514 515 516
		if (new->increment_by > 0)
			new->max_value = SEQ_MAXVALUE;		/* ascending seq */
		else
			new->max_value = -1;/* descending seq */
517
	}
518
	else
519
		new->max_value = get_param(max_value);
520

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

	if (new->min_value >= new->max_value)
532
		elog(ERROR, "DefineSequence: MINVALUE (%d) can't be >= MAXVALUE (%d)",
533 534 535
			 new->min_value, new->max_value);

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

	if (new->last_value < new->min_value)
546
		elog(ERROR, "DefineSequence: START value (%d) can't be < MINVALUE (%d)",
547 548
			 new->last_value, new->min_value);
	if (new->last_value > new->max_value)
549
		elog(ERROR, "DefineSequence: START value (%d) can't be > MAXVALUE (%d)",
550 551 552 553 554
			 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)
555
		elog(ERROR, "DefineSequence: CACHE (%d) can't be <= 0",
556
			 new->cache_value);
557 558 559 560 561

}


static int
562
get_param(DefElem *def)
563
{
564
	if (def->arg == (Node *) NULL)
565
		elog(ERROR, "DefineSequence: \"%s\" value unspecified", def->defname);
566 567

	if (nodeTag(def->arg) == T_Integer)
568
		return intVal(def->arg);
569

570
	elog(ERROR, "DefineSequence: \"%s\" is to be integer", def->defname);
571
	return -1;
572
}