1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2004,2008 Oracle.  All rights reserved.
5 *
6 * $Id: sequence.c,v 12.54 2008/05/05 20:25:09 mbrey Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12#include "dbinc/db_page.h"
13#include "dbinc/db_am.h"
14#include "dbinc/lock.h"
15#include "dbinc/txn.h"
16#include "dbinc_auto/sequence_ext.h"
17
18#ifdef HAVE_RPC
19#ifdef HAVE_SYSTEM_INCLUDE_FILES
20#include <rpc/rpc.h>
21#endif
22#include "db_server.h"
23#include "dbinc_auto/rpc_client_ext.h"
24#endif
25
26#ifdef HAVE_64BIT_TYPES
27/*
28 * Sequences must be architecture independent but they are stored as user
29 * data in databases so the code here must handle the byte ordering.  We
30 * store them in little-endian byte ordering.  If we are on a big-endian
31 * machine we swap in and out when we read from the database. seq->seq_rp
32 * always points to the record in native ordering.
33 *
34 * Version 1 always stored things in native format so if we detect this we
35 * upgrade on the fly and write the record back at open time.
36 */
37#define	SEQ_SWAP(rp)							\
38	do {								\
39		M_32_SWAP((rp)->seq_version);				\
40		M_32_SWAP((rp)->flags);					\
41		M_64_SWAP((rp)->seq_value);				\
42		M_64_SWAP((rp)->seq_max);				\
43		M_64_SWAP((rp)->seq_min);				\
44	} while (0)
45
46#define	SEQ_SWAP_IN(env, seq) \
47	do {								\
48		if (!F_ISSET((env), ENV_LITTLEENDIAN)) {		\
49			memcpy(&seq->seq_record, seq->seq_data.data,	\
50			     sizeof(seq->seq_record));			\
51			SEQ_SWAP(&seq->seq_record);			\
52		}							\
53	} while (0)
54
55#define	SEQ_SWAP_OUT(env, seq) \
56	do {								\
57		if (!F_ISSET((env), ENV_LITTLEENDIAN)) {		\
58			memcpy(seq->seq_data.data,			\
59			     &seq->seq_record, sizeof(seq->seq_record));\
60			SEQ_SWAP((DB_SEQ_RECORD*)seq->seq_data.data);	\
61		}							\
62	} while (0)
63
64static int __seq_chk_cachesize __P((ENV *, int32_t, db_seq_t, db_seq_t));
65static int __seq_close __P((DB_SEQUENCE *, u_int32_t));
66static int __seq_get
67	       __P((DB_SEQUENCE *, DB_TXN *, int32_t,  db_seq_t *, u_int32_t));
68static int __seq_get_cachesize __P((DB_SEQUENCE *, int32_t *));
69static int __seq_get_db __P((DB_SEQUENCE *, DB **));
70static int __seq_get_flags __P((DB_SEQUENCE *, u_int32_t *));
71static int __seq_get_key __P((DB_SEQUENCE *, DBT *));
72static int __seq_get_range __P((DB_SEQUENCE *, db_seq_t *, db_seq_t *));
73static int __seq_initial_value __P((DB_SEQUENCE *, db_seq_t));
74static int __seq_open_pp __P((DB_SEQUENCE *, DB_TXN *, DBT *, u_int32_t));
75static int __seq_remove __P((DB_SEQUENCE *, DB_TXN *, u_int32_t));
76static int __seq_set_cachesize __P((DB_SEQUENCE *, int32_t));
77static int __seq_set_flags __P((DB_SEQUENCE *, u_int32_t));
78static int __seq_set_range __P((DB_SEQUENCE *, db_seq_t, db_seq_t));
79static int __seq_update
80	__P((DB_SEQUENCE *, DB_THREAD_INFO *, DB_TXN *, int32_t, u_int32_t));
81
82/*
83 * db_sequence_create --
84 *	DB_SEQUENCE constructor.
85 *
86 * EXTERN: int db_sequence_create __P((DB_SEQUENCE **, DB *, u_int32_t));
87 */
88int
89db_sequence_create(seqp, dbp, flags)
90	DB_SEQUENCE **seqp;
91	DB *dbp;
92	u_int32_t flags;
93{
94	DB_SEQUENCE *seq;
95	ENV *env;
96	int ret;
97
98	env = dbp->env;
99
100	DB_ILLEGAL_BEFORE_OPEN(dbp, "db_sequence_create");
101#ifdef HAVE_RPC
102	if (RPC_ON(dbp->dbenv))
103		return (__dbcl_dbenv_illegal(dbp->dbenv));
104#endif
105
106	/* Check for invalid function flags. */
107	switch (flags) {
108	case 0:
109		break;
110	default:
111		return (__db_ferr(env, "db_sequence_create", 0));
112	}
113
114	/* Allocate the sequence. */
115	if ((ret = __os_calloc(env, 1, sizeof(*seq), &seq)) != 0)
116		return (ret);
117
118	seq->seq_dbp = dbp;
119	seq->close = __seq_close;
120	seq->get = __seq_get;
121	seq->get_cachesize = __seq_get_cachesize;
122	seq->set_cachesize = __seq_set_cachesize;
123	seq->get_db = __seq_get_db;
124	seq->get_flags = __seq_get_flags;
125	seq->get_key = __seq_get_key;
126	seq->get_range = __seq_get_range;
127	seq->initial_value = __seq_initial_value;
128	seq->open = __seq_open_pp;
129	seq->remove = __seq_remove;
130	seq->set_flags = __seq_set_flags;
131	seq->set_range = __seq_set_range;
132	seq->stat = __seq_stat;
133	seq->stat_print = __seq_stat_print;
134	seq->seq_rp = &seq->seq_record;
135	*seqp = seq;
136
137	return (0);
138}
139
140/*
141 * __seq_open --
142 *	DB_SEQUENCE->open method.
143 *
144 */
145static int
146__seq_open_pp(seq, txn, keyp, flags)
147	DB_SEQUENCE *seq;
148	DB_TXN *txn;
149	DBT *keyp;
150	u_int32_t flags;
151{
152	DB *dbp;
153	DB_SEQ_RECORD *rp;
154	DB_THREAD_INFO *ip;
155	ENV *env;
156	u_int32_t tflags;
157	int handle_check, txn_local, ret, t_ret;
158#define	SEQ_OPEN_FLAGS	(DB_CREATE | DB_EXCL | DB_THREAD)
159
160	dbp = seq->seq_dbp;
161	env = dbp->env;
162	txn_local = 0;
163
164	STRIP_AUTO_COMMIT(flags);
165	SEQ_ILLEGAL_AFTER_OPEN(seq, "DB_SEQUENCE->open");
166
167	ENV_ENTER(env, ip);
168
169	/* Check for replication block. */
170	handle_check = IS_ENV_REPLICATED(env);
171	if (handle_check &&
172	    (ret = __db_rep_enter(dbp, 1, 0, txn != NULL)) != 0) {
173		handle_check = 0;
174		goto err;
175	}
176
177	if ((ret = __db_fchk(env,
178	    "DB_SEQUENCE->open", flags, SEQ_OPEN_FLAGS)) != 0)
179		goto err;
180
181	if (keyp->size == 0) {
182		__db_errx(env, "Zero length sequence key specified");
183		ret = EINVAL;
184		goto err;
185	}
186
187	if ((ret = __db_get_flags(dbp, &tflags)) != 0)
188		goto err;
189
190	/*
191	 * We can let replication clients open sequences, but must
192	 * check later that they do not update them.
193	 */
194	if (F_ISSET(dbp, DB_AM_RDONLY)) {
195		ret = __db_rdonly(dbp->env, "DB_SEQUENCE->open");
196		goto err;
197	}
198	if (FLD_ISSET(tflags, DB_DUP)) {
199		__db_errx(env,
200	"Sequences not supported in databases configured for duplicate data");
201		ret = EINVAL;
202		goto err;
203	}
204
205	if (LF_ISSET(DB_THREAD)) {
206		if (RPC_ON(dbp->dbenv)) {
207			__db_errx(env,
208		    "DB_SEQUENCE->open: DB_THREAD not supported with RPC");
209			goto err;
210		}
211		if ((ret = __mutex_alloc(env,
212		    MTX_SEQUENCE, DB_MUTEX_PROCESS_ONLY, &seq->mtx_seq)) != 0)
213			goto err;
214	}
215
216	memset(&seq->seq_data, 0, sizeof(DBT));
217	if (F_ISSET(env, ENV_LITTLEENDIAN)) {
218		seq->seq_data.data = &seq->seq_record;
219		seq->seq_data.flags = DB_DBT_USERMEM;
220	} else {
221		if ((ret = __os_umalloc(env,
222		     sizeof(seq->seq_record), &seq->seq_data.data)) != 0)
223			goto err;
224		seq->seq_data.flags = DB_DBT_REALLOC;
225	}
226
227	seq->seq_data.ulen = seq->seq_data.size = sizeof(seq->seq_record);
228	seq->seq_rp = &seq->seq_record;
229
230	if ((ret = __dbt_usercopy(env, keyp)) != 0)
231		goto err;
232
233	memset(&seq->seq_key, 0, sizeof(DBT));
234	if ((ret = __os_malloc(env, keyp->size, &seq->seq_key.data)) != 0)
235		goto err;
236	memcpy(seq->seq_key.data, keyp->data, keyp->size);
237	seq->seq_key.size = seq->seq_key.ulen = keyp->size;
238	seq->seq_key.flags = DB_DBT_USERMEM;
239
240retry:	if ((ret = __db_get(dbp, ip,
241	    txn, &seq->seq_key, &seq->seq_data, 0)) != 0) {
242		if (ret == DB_BUFFER_SMALL &&
243		    seq->seq_data.size > sizeof(seq->seq_record)) {
244			seq->seq_data.flags = DB_DBT_REALLOC;
245			seq->seq_data.data = NULL;
246			goto retry;
247		}
248		if ((ret != DB_NOTFOUND && ret != DB_KEYEMPTY) ||
249		    !LF_ISSET(DB_CREATE))
250			goto err;
251		if (IS_REP_CLIENT(env) &&
252		    !F_ISSET(dbp, DB_AM_NOT_DURABLE)) {
253			ret = __db_rdonly(env, "DB_SEQUENCE->open");
254			goto err;
255		}
256		ret = 0;
257
258		rp = &seq->seq_record;
259		if (!F_ISSET(rp, DB_SEQ_RANGE_SET)) {
260			rp->seq_max = INT64_MAX;
261			rp->seq_min = INT64_MIN;
262		}
263		/* INC is the default. */
264		if (!F_ISSET(rp, DB_SEQ_DEC))
265			F_SET(rp, DB_SEQ_INC);
266
267		rp->seq_version = DB_SEQUENCE_VERSION;
268
269		if (rp->seq_value > rp->seq_max ||
270		    rp->seq_value < rp->seq_min) {
271			__db_errx(env, "Sequence value out of range");
272			ret = EINVAL;
273			goto err;
274		} else {
275			SEQ_SWAP_OUT(env, seq);
276			/* Create local transaction as necessary. */
277			if (IS_DB_AUTO_COMMIT(dbp, txn)) {
278				if ((ret =
279				    __txn_begin(env, ip, NULL, &txn, 0)) != 0)
280					goto err;
281				txn_local = 1;
282			}
283
284			if ((ret = __db_put(dbp, ip, txn, &seq->seq_key,
285			     &seq->seq_data, DB_NOOVERWRITE)) != 0) {
286				__db_errx(env, "Sequence create failed");
287				goto err;
288			}
289		}
290	} else if (LF_ISSET(DB_CREATE) && LF_ISSET(DB_EXCL)) {
291		ret = EEXIST;
292		goto err;
293	} else if (seq->seq_data.size < sizeof(seq->seq_record)) {
294		__db_errx(env, "Bad sequence record format");
295		ret = EINVAL;
296		goto err;
297	}
298
299	if (F_ISSET(env, ENV_LITTLEENDIAN))
300		seq->seq_rp = seq->seq_data.data;
301
302	/*
303	 * The first release was stored in native mode.
304	 * Check the version number before swapping.
305	 */
306	rp = seq->seq_data.data;
307	if (rp->seq_version == DB_SEQUENCE_OLDVER) {
308oldver:		if (IS_REP_CLIENT(env) &&
309		    !F_ISSET(dbp, DB_AM_NOT_DURABLE)) {
310			ret = __db_rdonly(env, "DB_SEQUENCE->open");
311			goto err;
312		}
313		rp->seq_version = DB_SEQUENCE_VERSION;
314		if (!F_ISSET(env, ENV_LITTLEENDIAN)) {
315			if (IS_DB_AUTO_COMMIT(dbp, txn)) {
316				if ((ret =
317				    __txn_begin(env, ip, NULL, &txn, 0)) != 0)
318					goto err;
319				txn_local = 1;
320				goto retry;
321			}
322			memcpy(&seq->seq_record, rp, sizeof(seq->seq_record));
323			SEQ_SWAP_OUT(env, seq);
324		}
325		if ((ret = __db_put(dbp,
326		     ip, txn, &seq->seq_key, &seq->seq_data, 0)) != 0)
327			goto err;
328	}
329	rp = seq->seq_rp;
330
331	SEQ_SWAP_IN(env, seq);
332
333	if (rp->seq_version != DB_SEQUENCE_VERSION) {
334		/*
335		 * The database may have moved from one type
336		 * of machine to another, check here.
337		 * If we moved from little-end to big-end then
338		 * the swap above will make the version correct.
339		 * If the move was from big to little
340		 * then we need to swap to see if this
341		 * is an old version.
342		 */
343		if (rp->seq_version == DB_SEQUENCE_OLDVER)
344			goto oldver;
345		M_32_SWAP(rp->seq_version);
346		if (rp->seq_version == DB_SEQUENCE_OLDVER) {
347			SEQ_SWAP(rp);
348			goto oldver;
349		}
350		M_32_SWAP(rp->seq_version);
351		__db_errx(env,
352		     "Unsupported sequence version: %d", rp->seq_version);
353		goto err;
354	}
355
356	seq->seq_last_value = rp->seq_value;
357	if (F_ISSET(rp, DB_SEQ_INC))
358		seq->seq_last_value--;
359	else
360		seq->seq_last_value++;
361
362	/*
363	 * It's an error to specify a cache larger than the range of sequences.
364	 */
365	if (seq->seq_cache_size != 0 && (ret = __seq_chk_cachesize(
366	    env, seq->seq_cache_size, rp->seq_max, rp->seq_min)) != 0)
367		goto err;
368
369err:	if (txn_local &&
370	    (t_ret = __db_txn_auto_resolve(env, txn, 0, ret)) && ret == 0)
371		ret = t_ret;
372	if (ret != 0) {
373		__os_free(env, seq->seq_key.data);
374		seq->seq_key.data = NULL;
375	}
376	/* Release replication block. */
377	if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0)
378		ret = t_ret;
379
380	ENV_LEAVE(env, ip);
381	__dbt_userfree(env, keyp, NULL, NULL);
382	return (ret);
383}
384
385/*
386 * __seq_get_cachesize --
387 *	Accessor for value passed into DB_SEQUENCE->set_cachesize call.
388 *
389 */
390static int
391__seq_get_cachesize(seq, cachesize)
392	DB_SEQUENCE *seq;
393	int32_t *cachesize;
394{
395	SEQ_ILLEGAL_BEFORE_OPEN(seq, "DB_SEQUENCE->get_cachesize");
396
397	*cachesize = seq->seq_cache_size;
398	return (0);
399}
400
401/*
402 * __seq_set_cachesize --
403 *	DB_SEQUENCE->set_cachesize.
404 *
405 */
406static int
407__seq_set_cachesize(seq, cachesize)
408	DB_SEQUENCE *seq;
409	int32_t cachesize;
410{
411	ENV *env;
412	int ret;
413
414	env = seq->seq_dbp->env;
415
416	if (cachesize < 0) {
417		__db_errx(env, "Cache size must be >= 0");
418		return (EINVAL);
419	}
420
421	/*
422	 * It's an error to specify a cache larger than the range of sequences.
423	 */
424	if (SEQ_IS_OPEN(seq) && (ret = __seq_chk_cachesize(env,
425	    cachesize, seq->seq_rp->seq_max, seq->seq_rp->seq_min)) != 0)
426		return (ret);
427
428	seq->seq_cache_size = cachesize;
429	return (0);
430}
431
432#define	SEQ_SET_FLAGS	(DB_SEQ_WRAP | DB_SEQ_INC | DB_SEQ_DEC)
433/*
434 * __seq_get_flags --
435 *	Accessor for flags passed into DB_SEQUENCE->open call
436 *
437 */
438static int
439__seq_get_flags(seq, flagsp)
440	DB_SEQUENCE *seq;
441	u_int32_t *flagsp;
442{
443	SEQ_ILLEGAL_BEFORE_OPEN(seq, "DB_SEQUENCE->get_flags");
444
445	*flagsp = F_ISSET(seq->seq_rp, SEQ_SET_FLAGS);
446	return (0);
447}
448
449/*
450 * __seq_set_flags --
451 *	DB_SEQUENCE->set_flags.
452 *
453 */
454static int
455__seq_set_flags(seq, flags)
456	DB_SEQUENCE *seq;
457	u_int32_t flags;
458{
459	DB_SEQ_RECORD *rp;
460	ENV *env;
461	int ret;
462
463	env = seq->seq_dbp->env;
464	rp = seq->seq_rp;
465
466	SEQ_ILLEGAL_AFTER_OPEN(seq, "DB_SEQUENCE->set_flags");
467
468	if ((ret = __db_fchk(
469	    env, "DB_SEQUENCE->set_flags", flags, SEQ_SET_FLAGS)) != 0)
470		return (ret);
471	if ((ret = __db_fcchk(env,
472	     "DB_SEQUENCE->set_flags", flags, DB_SEQ_DEC, DB_SEQ_INC)) != 0)
473		return (ret);
474
475	if (LF_ISSET(DB_SEQ_DEC | DB_SEQ_INC))
476		F_CLR(rp, DB_SEQ_DEC | DB_SEQ_INC);
477	F_SET(rp, flags);
478
479	return (0);
480}
481
482/*
483 * __seq_initial_value --
484 *	DB_SEQUENCE->initial_value.
485 *
486 */
487static int
488__seq_initial_value(seq, value)
489	DB_SEQUENCE *seq;
490	db_seq_t value;
491{
492	DB_SEQ_RECORD *rp;
493	ENV *env;
494
495	env = seq->seq_dbp->env;
496	SEQ_ILLEGAL_AFTER_OPEN(seq, "DB_SEQUENCE->initial_value");
497
498	rp = seq->seq_rp;
499	if (F_ISSET(rp, DB_SEQ_RANGE_SET) &&
500	     (value > rp->seq_max || value < rp->seq_min)) {
501		__db_errx(env, "Sequence value out of range");
502		return (EINVAL);
503	}
504
505	rp->seq_value = value;
506
507	return (0);
508}
509
510/*
511 * __seq_get_range --
512 *	Accessor for range passed into DB_SEQUENCE->set_range call
513 *
514 */
515static int
516__seq_get_range(seq, minp, maxp)
517	DB_SEQUENCE *seq;
518	db_seq_t *minp, *maxp;
519{
520	SEQ_ILLEGAL_BEFORE_OPEN(seq, "DB_SEQUENCE->get_range");
521
522	*minp = seq->seq_rp->seq_min;
523	*maxp = seq->seq_rp->seq_max;
524	return (0);
525}
526
527/*
528 * __seq_set_range --
529 *	SEQUENCE->set_range.
530 *
531 */
532static int
533__seq_set_range(seq, min, max)
534	DB_SEQUENCE *seq;
535	db_seq_t min, max;
536{
537	ENV *env;
538
539	env = seq->seq_dbp->env;
540	SEQ_ILLEGAL_AFTER_OPEN(seq, "DB_SEQUENCE->set_range");
541
542	if (min >= max) {
543		__db_errx(env,
544	    "Minimum sequence value must be less than maximum sequence value");
545		return (EINVAL);
546	}
547
548	seq->seq_rp->seq_min = min;
549	seq->seq_rp->seq_max = max;
550	F_SET(seq->seq_rp, DB_SEQ_RANGE_SET);
551
552	return (0);
553}
554
555static int
556__seq_update(seq, ip, txn, delta, flags)
557	DB_SEQUENCE *seq;
558	DB_THREAD_INFO *ip;
559	DB_TXN *txn;
560	int32_t delta;
561	u_int32_t flags;
562{
563	DB *dbp;
564	DB_SEQ_RECORD *rp;
565	ENV *env;
566	int32_t adjust;
567	int ret, txn_local;
568
569	dbp = seq->seq_dbp;
570	env = dbp->env;
571
572	/*
573	 * Create a local transaction as necessary, check for consistent
574	 * transaction usage, and, if we have no transaction but do have
575	 * locking on, acquire a locker id for the handle lock acquisition.
576	 */
577	if (IS_DB_AUTO_COMMIT(dbp, txn)) {
578		if ((ret = __txn_begin(env, ip, NULL, &txn, 0)) != 0)
579			return (ret);
580		txn_local = 1;
581	} else
582		txn_local = 0;
583
584	/* Check for consistent transaction usage. */
585	if ((ret = __db_check_txn(dbp, txn, DB_LOCK_INVALIDID, 0)) != 0)
586		goto err;
587
588retry:	if ((ret = __db_get(dbp, ip,
589	    txn, &seq->seq_key, &seq->seq_data, 0)) != 0) {
590		if (ret == DB_BUFFER_SMALL &&
591		    seq->seq_data.size > sizeof(seq->seq_record)) {
592			seq->seq_data.flags = DB_DBT_REALLOC;
593			seq->seq_data.data = NULL;
594			goto retry;
595		}
596		goto err;
597	}
598
599	if (F_ISSET(env, ENV_LITTLEENDIAN))
600		seq->seq_rp = seq->seq_data.data;
601	SEQ_SWAP_IN(env, seq);
602	rp = seq->seq_rp;
603
604	if (F_ISSET(rp, DB_SEQ_WRAPPED))
605		goto overflow;
606
607	if (seq->seq_data.size < sizeof(seq->seq_record)) {
608		__db_errx(env, "Bad sequence record format");
609		ret = EINVAL;
610		goto err;
611	}
612
613	adjust = delta > seq->seq_cache_size ? delta : seq->seq_cache_size;
614
615	/*
616	 * Check whether this operation will cause the sequence to wrap.
617	 *
618	 * The sequence minimum and maximum values can be INT64_MIN and
619	 * INT64_MAX, so we need to do the test carefully to cope with
620	 * arithmetic overflow.  The first part of the test below checks
621	 * whether we will hit the end of the 64-bit range.  The second part
622	 * checks whether we hit the end of the sequence.
623	 */
624again:	if (F_ISSET(rp, DB_SEQ_INC)) {
625		if (rp->seq_value + adjust - 1 < rp->seq_value ||
626		     rp->seq_value + adjust - 1 > rp->seq_max) {
627			/* Don't wrap just to fill the cache. */
628			if (adjust > delta) {
629				adjust = delta;
630				goto again;
631			}
632			if (F_ISSET(rp, DB_SEQ_WRAP))
633				rp->seq_value = rp->seq_min;
634			else {
635overflow:			__db_errx(env, "Sequence overflow");
636				ret = EINVAL;
637				goto err;
638			}
639		}
640		/* See if we are at the end of the 64 bit range. */
641		if (!F_ISSET(rp, DB_SEQ_WRAP) &&
642		    rp->seq_value + adjust < rp->seq_value)
643			F_SET(rp, DB_SEQ_WRAPPED);
644	} else {
645		if ((rp->seq_value - adjust) + 1 > rp->seq_value ||
646		   (rp->seq_value - adjust) + 1 < rp->seq_min) {
647			/* Don't wrap just to fill the cache. */
648			if (adjust > delta) {
649				adjust = delta;
650				goto again;
651			}
652			if (F_ISSET(rp, DB_SEQ_WRAP))
653				rp->seq_value = rp->seq_max;
654			else
655				goto overflow;
656		}
657		/* See if we are at the end of the 64 bit range. */
658		if (!F_ISSET(rp, DB_SEQ_WRAP) &&
659		    rp->seq_value - adjust > rp->seq_value)
660			F_SET(rp, DB_SEQ_WRAPPED);
661		adjust = -adjust;
662	}
663
664	rp->seq_value += adjust;
665	SEQ_SWAP_OUT(env, seq);
666	ret = __db_put(dbp, ip, txn, &seq->seq_key, &seq->seq_data, 0);
667	rp->seq_value -= adjust;
668	if (ret != 0) {
669		__db_errx(env, "Sequence update failed");
670		goto err;
671	}
672	seq->seq_last_value = rp->seq_value + adjust;
673	if (F_ISSET(rp, DB_SEQ_INC))
674		seq->seq_last_value--;
675	else
676		seq->seq_last_value++;
677
678err:	return (txn_local ? __db_txn_auto_resolve(
679	    env, txn, LF_ISSET(DB_TXN_NOSYNC), ret) : ret);
680}
681
682static int
683__seq_get(seq, txn, delta, retp, flags)
684	DB_SEQUENCE *seq;
685	DB_TXN *txn;
686	int32_t delta;
687	db_seq_t *retp;
688	u_int32_t flags;
689{
690	DB *dbp;
691	DB_SEQ_RECORD *rp;
692	DB_THREAD_INFO *ip;
693	ENV *env;
694	int handle_check, ret, t_ret;
695
696	dbp = seq->seq_dbp;
697	env = dbp->env;
698	rp = seq->seq_rp;
699	ret = 0;
700
701	STRIP_AUTO_COMMIT(flags);
702	SEQ_ILLEGAL_BEFORE_OPEN(seq, "DB_SEQUENCE->get");
703
704	if (delta <= 0) {
705		__db_errx(env, "Sequence delta must be greater than 0");
706		return (EINVAL);
707	}
708
709	if (seq->seq_cache_size != 0 && txn != NULL) {
710		__db_errx(env,
711	    "Sequence with non-zero cache may not specify transaction handle");
712		return (EINVAL);
713	}
714
715	ENV_ENTER(env, ip);
716
717	/* Check for replication block. */
718	handle_check = IS_ENV_REPLICATED(env);
719	if (handle_check && (ret = __db_rep_enter(dbp, 1, 0, txn != NULL)) != 0)
720		return (ret);
721
722	MUTEX_LOCK(env, seq->mtx_seq);
723
724	if (handle_check && IS_REP_CLIENT(env) &&
725	    !F_ISSET(dbp, DB_AM_NOT_DURABLE)) {
726		ret = __db_rdonly(env, "DB_SEQUENCE->get");
727		goto err;
728	}
729
730
731	if (rp->seq_min + delta > rp->seq_max) {
732		__db_errx(env, "Sequence overflow");
733		ret = EINVAL;
734		goto err;
735	}
736
737	if (F_ISSET(rp, DB_SEQ_INC)) {
738		if (seq->seq_last_value + 1 - rp->seq_value < delta &&
739		   (ret = __seq_update(seq, ip, txn, delta, flags)) != 0)
740			goto err;
741
742		rp = seq->seq_rp;
743		*retp = rp->seq_value;
744		rp->seq_value += delta;
745	} else {
746		if ((rp->seq_value - seq->seq_last_value) + 1 < delta &&
747		    (ret = __seq_update(seq, ip, txn, delta, flags)) != 0)
748			goto err;
749
750		rp = seq->seq_rp;
751		*retp = rp->seq_value;
752		rp->seq_value -= delta;
753	}
754
755err:	MUTEX_UNLOCK(env, seq->mtx_seq);
756
757	/* Release replication block. */
758	if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0)
759		ret = t_ret;
760
761	ENV_LEAVE(env, ip);
762	return (ret);
763}
764
765/*
766 * __seq_get_db --
767 *	Accessor for dbp passed into db_sequence_create call
768 *
769 */
770static int
771__seq_get_db(seq, dbpp)
772	DB_SEQUENCE *seq;
773	DB **dbpp;
774{
775	*dbpp = seq->seq_dbp;
776	return (0);
777}
778
779/*
780 * __seq_get_key --
781 *	Accessor for key passed into DB_SEQUENCE->open call
782 *
783 */
784static int
785__seq_get_key(seq, key)
786	DB_SEQUENCE *seq;
787	DBT *key;
788{
789	SEQ_ILLEGAL_BEFORE_OPEN(seq, "DB_SEQUENCE->get_key");
790
791	if (F_ISSET(key, DB_DBT_USERCOPY))
792		return (__db_retcopy(seq->seq_dbp->env, key,
793		    seq->seq_key.data, seq->seq_key.size, NULL, 0));
794
795	key->data = seq->seq_key.data;
796	key->size = key->ulen = seq->seq_key.size;
797	key->flags = seq->seq_key.flags;
798	return (0);
799}
800
801/*
802 * __seq_close --
803 *	Close a sequence
804 *
805 */
806static int
807__seq_close(seq, flags)
808	DB_SEQUENCE *seq;
809	u_int32_t flags;
810{
811	ENV *env;
812	int ret, t_ret;
813
814	ret = 0;
815	env = seq->seq_dbp->env;
816
817	if (flags != 0)
818		ret = __db_ferr(env, "DB_SEQUENCE->close", 0);
819
820	if ((t_ret = __mutex_free(env, &seq->mtx_seq)) != 0 && ret == 0)
821		ret = t_ret;
822
823	if (seq->seq_key.data != NULL)
824		__os_free(env, seq->seq_key.data);
825	if (seq->seq_data.data != NULL &&
826	    seq->seq_data.data != &seq->seq_record)
827		__os_ufree(env, seq->seq_data.data);
828	seq->seq_key.data = NULL;
829
830	memset(seq, CLEAR_BYTE, sizeof(*seq));
831	__os_free(env, seq);
832
833	return (ret);
834}
835
836/*
837 * __seq_remove --
838 *	Remove a sequence from the database.
839 */
840static int
841__seq_remove(seq, txn, flags)
842	DB_SEQUENCE *seq;
843	DB_TXN *txn;
844	u_int32_t flags;
845{
846	DB *dbp;
847	DB_THREAD_INFO *ip;
848	ENV *env;
849	int handle_check, ret, t_ret, txn_local;
850
851	dbp = seq->seq_dbp;
852	env = dbp->env;
853	txn_local = 0;
854
855	SEQ_ILLEGAL_BEFORE_OPEN(seq, "DB_SEQUENCE->remove");
856	ENV_ENTER(env, ip);
857
858	/* Check for replication block. */
859	handle_check = IS_ENV_REPLICATED(env);
860	if (handle_check &&
861	    (ret = __db_rep_enter(dbp, 1, 0, txn != NULL)) != 0) {
862		handle_check = 0;
863		goto err;
864	}
865	if (flags != 0) {
866		ret = __db_ferr(env, "DB_SEQUENCE->remove", 0);
867		goto err;
868	}
869
870	/*
871	 * Create a local transaction as necessary, check for consistent
872	 * transaction usage, and, if we have no transaction but do have
873	 * locking on, acquire a locker id for the handle lock acquisition.
874	 */
875	if (IS_DB_AUTO_COMMIT(dbp, txn)) {
876		if ((ret = __txn_begin(env, ip, NULL, &txn, 0)) != 0)
877			return (ret);
878		txn_local = 1;
879	}
880
881	/* Check for consistent transaction usage. */
882	if ((ret = __db_check_txn(dbp, txn, DB_LOCK_INVALIDID, 0)) != 0)
883		goto err;
884
885	ret = __db_del(dbp, ip, txn, &seq->seq_key, 0);
886
887	if ((t_ret = __seq_close(seq, 0)) != 0 && ret == 0)
888		ret = t_ret;
889
890	/* Release replication block. */
891	if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0)
892		ret = t_ret;
893err:	if (txn_local && (t_ret =
894	    __db_txn_auto_resolve(env, txn, 0, ret)) != 0 && ret == 0)
895		ret = t_ret;
896
897	ENV_LEAVE(env, ip);
898	return (ret);
899}
900
901/*
902 * __seq_chk_cachesize --
903 *	Validate the cache size vs. the range.
904 */
905static int
906__seq_chk_cachesize(env, cachesize, max, min)
907	ENV *env;
908	int32_t cachesize;
909	db_seq_t max, min;
910{
911	/*
912	 * It's an error to specify caches larger than the sequence range.
913	 *
914	 * The min and max of the range can be either positive or negative,
915	 * the difference will fit in an unsigned variable of the same type.
916	 * Assume a 2's complement machine, and simply subtract.
917	 */
918	if ((u_int32_t)cachesize > (u_int64_t)max - (u_int64_t)min) {
919		__db_errx(env,
920    "Number of items to be cached is larger than the sequence range");
921		return (EINVAL);
922	}
923	return (0);
924}
925
926#else /* !HAVE_64BIT_TYPES */
927
928int
929db_sequence_create(seqp, dbp, flags)
930	DB_SEQUENCE **seqp;
931	DB *dbp;
932	u_int32_t flags;
933{
934	COMPQUIET(seqp, NULL);
935	COMPQUIET(flags, 0);
936	__db_errx(dbp->env,
937	    "library build did not include support for sequences");
938	return (DB_OPNOTSUP);
939}
940#endif /* HAVE_64BIT_TYPES */
941