sequence.c 13.1 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 99 100 101 102 103 104
		coldef = makeNode(ColumnDef);
		coldef->typename = typnam;
		coldef->defval = NULL;
		coldef->is_not_null = false;
		null[i - 1] = ' ';

		switch (i)
		{
105 106 107
			case SEQ_COL_NAME:
				typnam->name = "name";
				coldef->colname = "sequence_name";
108 109
				namestrcpy(&name, seq->seqname);
				value[i - 1] = NameGetDatum(&name);
110 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
				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;
146 147 148 149 150 151 152 153
		}
		stmt->tableElts = lappend(stmt->tableElts, coldef);
	}

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

154
	DefineRelation(stmt, RELKIND_SEQUENCE);
155

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

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

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

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

	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)
177
		elog(ERROR, "DefineSequence: WriteBuffer failed");
178

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


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

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

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

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

	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 */
221
	{
222 223 224 225 226 227 228 229 230 231 232 233 234

		/*
		 * 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')
235
					elog(ERROR, "%s.nextval: got MAXVALUE (%d)",
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
						 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')
251
					elog(ERROR, "%s.nextval: got MINVALUE (%d)",
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
						 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 已提交
271 272
	LockBuffer(buf, BUFFER_LOCK_UNLOCK);

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

276
	return result;
277

278 279 280 281
}


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

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

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

	result = elm->last;

297
	return result;
298

299 300
}

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

#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 已提交
315
	/* open and AccessShareLock sequence */
316
	elm = init_sequence("setval", seqname);
B
Bruce Momjian 已提交
317 318
	seq = read_info("setval", elm, &buf);		/* lock page' buffer and
												 * read tuple */
M
 
Marc G. Fournier 已提交
319

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

326 327 328 329
	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 已提交
330 331 332 333 334 335 336 337 338 339
	}

	/* 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 已提交
340 341
	LockBuffer(buf, BUFFER_LOCK_UNLOCK);

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

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

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

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

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

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

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

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

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

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

	elm->increment = seq->increment_by;

381
	return seq;
382 383 384 385

}


386
static SeqTable
387
init_sequence(char *caller, char *name)
388
{
389 390 391
	SeqTable	elm,
				priv = (SeqTable) NULL;
	SeqTable	temp;
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413

	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 */
414
			return elm;
415 416 417
		temp = elm;
	}

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

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

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

443
	return elm;
444 445 446 447
}


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

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


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

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

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

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

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

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

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

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

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

}


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

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

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