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	if (DB_IS_READONLY(dbp)) {
191		ret = __db_rdonly(dbp->env, "DB_SEQUENCE->open");
192		goto err;
193	}
194	if (FLD_ISSET(tflags, DB_DUP)) {
195		__db_errx(env,
196	"Sequences not supported in databases configured for duplicate data");
197		ret = EINVAL;
198		goto err;
199	}
200
201	if (LF_ISSET(DB_THREAD)) {
202		if (RPC_ON(dbp->dbenv)) {
203			__db_errx(env,
204		    "DB_SEQUENCE->open: DB_THREAD not supported with RPC");
205			goto err;
206		}
207		if ((ret = __mutex_alloc(env,
208		    MTX_SEQUENCE, DB_MUTEX_PROCESS_ONLY, &seq->mtx_seq)) != 0)
209			goto err;
210	}
211
212	memset(&seq->seq_data, 0, sizeof(DBT));
213	if (F_ISSET(env, ENV_LITTLEENDIAN)) {
214		seq->seq_data.data = &seq->seq_record;
215		seq->seq_data.flags = DB_DBT_USERMEM;
216	} else {
217		if ((ret = __os_umalloc(env,
218		     sizeof(seq->seq_record), &seq->seq_data.data)) != 0)
219			goto err;
220		seq->seq_data.flags = DB_DBT_REALLOC;
221	}
222
223	seq->seq_data.ulen = seq->seq_data.size = sizeof(seq->seq_record);
224	seq->seq_rp = &seq->seq_record;
225
226	if ((ret = __dbt_usercopy(env, keyp)) != 0)
227		goto err;
228
229	memset(&seq->seq_key, 0, sizeof(DBT));
230	if ((ret = __os_malloc(env, keyp->size, &seq->seq_key.data)) != 0)
231		goto err;
232	memcpy(seq->seq_key.data, keyp->data, keyp->size);
233	seq->seq_key.size = seq->seq_key.ulen = keyp->size;
234	seq->seq_key.flags = DB_DBT_USERMEM;
235
236retry:	if ((ret = __db_get(dbp, ip,
237	    txn, &seq->seq_key, &seq->seq_data, 0)) != 0) {
238		if (ret == DB_BUFFER_SMALL &&
239		    seq->seq_data.size > sizeof(seq->seq_record)) {
240			seq->seq_data.flags = DB_DBT_REALLOC;
241			seq->seq_data.data = NULL;
242			goto retry;
243		}
244		if ((ret != DB_NOTFOUND && ret != DB_KEYEMPTY) ||
245		    !LF_ISSET(DB_CREATE))
246			goto err;
247		ret = 0;
248
249		rp = &seq->seq_record;
250		if (!F_ISSET(rp, DB_SEQ_RANGE_SET)) {
251			rp->seq_max = INT64_MAX;
252			rp->seq_min = INT64_MIN;
253		}
254		/* INC is the default. */
255		if (!F_ISSET(rp, DB_SEQ_DEC))
256			F_SET(rp, DB_SEQ_INC);
257
258		rp->seq_version = DB_SEQUENCE_VERSION;
259
260		if (rp->seq_value > rp->seq_max ||
261		    rp->seq_value < rp->seq_min) {
262			__db_errx(env, "Sequence value out of range");
263			ret = EINVAL;
264			goto err;
265		} else {
266			SEQ_SWAP_OUT(env, seq);
267			/* Create local transaction as necessary. */
268			if (IS_DB_AUTO_COMMIT(dbp, txn)) {
269				if ((ret =
270				    __txn_begin(env, ip, NULL, &txn, 0)) != 0)
271					goto err;
272				txn_local = 1;
273			}
274
275			if ((ret = __db_put(dbp, ip, txn, &seq->seq_key,
276			     &seq->seq_data, DB_NOOVERWRITE)) != 0) {
277				__db_errx(env, "Sequence create failed");
278				goto err;
279			}
280		}
281	} else if (LF_ISSET(DB_CREATE) && LF_ISSET(DB_EXCL)) {
282		ret = EEXIST;
283		goto err;
284	} else if (seq->seq_data.size < sizeof(seq->seq_record)) {
285		__db_errx(env, "Bad sequence record format");
286		ret = EINVAL;
287		goto err;
288	}
289
290	if (F_ISSET(env, ENV_LITTLEENDIAN))
291		seq->seq_rp = seq->seq_data.data;
292
293	/*
294	 * The first release was stored in native mode.
295	 * Check the version number before swapping.
296	 */
297	rp = seq->seq_data.data;
298	if (rp->seq_version == DB_SEQUENCE_OLDVER) {
299oldver:		rp->seq_version = DB_SEQUENCE_VERSION;
300		if (!F_ISSET(env, ENV_LITTLEENDIAN)) {
301			if (IS_DB_AUTO_COMMIT(dbp, txn)) {
302				if ((ret =
303				    __txn_begin(env, ip, NULL, &txn, 0)) != 0)
304					goto err;
305				txn_local = 1;
306				goto retry;
307			}
308			memcpy(&seq->seq_record, rp, sizeof(seq->seq_record));
309			SEQ_SWAP_OUT(env, seq);
310		}
311		if ((ret = __db_put(dbp,
312		     ip, txn, &seq->seq_key, &seq->seq_data, 0)) != 0)
313			goto err;
314	}
315	rp = seq->seq_rp;
316
317	SEQ_SWAP_IN(env, seq);
318
319	if (rp->seq_version != DB_SEQUENCE_VERSION) {
320		/*
321		 * The database may have moved from one type
322		 * of machine to another, check here.
323		 * If we moved from little-end to big-end then
324		 * the swap above will make the version correct.
325		 * If the move was from big to little
326		 * then we need to swap to see if this
327		 * is an old version.
328		 */
329		if (rp->seq_version == DB_SEQUENCE_OLDVER)
330			goto oldver;
331		M_32_SWAP(rp->seq_version);
332		if (rp->seq_version == DB_SEQUENCE_OLDVER) {
333			SEQ_SWAP(rp);
334			goto oldver;
335		}
336		M_32_SWAP(rp->seq_version);
337		__db_errx(env,
338		     "Unsupported sequence version: %d", rp->seq_version);
339		goto err;
340	}
341
342	seq->seq_last_value = rp->seq_value;
343	if (F_ISSET(rp, DB_SEQ_INC))
344		seq->seq_last_value--;
345	else
346		seq->seq_last_value++;
347
348	/*
349	 * It's an error to specify a cache larger than the range of sequences.
350	 */
351	if (seq->seq_cache_size != 0 && (ret = __seq_chk_cachesize(
352	    env, seq->seq_cache_size, rp->seq_max, rp->seq_min)) != 0)
353		goto err;
354
355err:	if (txn_local &&
356	    (t_ret = __db_txn_auto_resolve(env, txn, 0, ret)) && ret == 0)
357		ret = t_ret;
358	if (ret != 0) {
359		__os_free(env, seq->seq_key.data);
360		seq->seq_key.data = NULL;
361	}
362	/* Release replication block. */
363	if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0)
364		ret = t_ret;
365
366	ENV_LEAVE(env, ip);
367	__dbt_userfree(env, keyp, NULL, NULL);
368	return (ret);
369}
370
371/*
372 * __seq_get_cachesize --
373 *	Accessor for value passed into DB_SEQUENCE->set_cachesize call.
374 *
375 */
376static int
377__seq_get_cachesize(seq, cachesize)
378	DB_SEQUENCE *seq;
379	int32_t *cachesize;
380{
381	SEQ_ILLEGAL_BEFORE_OPEN(seq, "DB_SEQUENCE->get_cachesize");
382
383	*cachesize = seq->seq_cache_size;
384	return (0);
385}
386
387/*
388 * __seq_set_cachesize --
389 *	DB_SEQUENCE->set_cachesize.
390 *
391 */
392static int
393__seq_set_cachesize(seq, cachesize)
394	DB_SEQUENCE *seq;
395	int32_t cachesize;
396{
397	ENV *env;
398	int ret;
399
400	env = seq->seq_dbp->env;
401
402	if (cachesize < 0) {
403		__db_errx(env, "Cache size must be >= 0");
404		return (EINVAL);
405	}
406
407	/*
408	 * It's an error to specify a cache larger than the range of sequences.
409	 */
410	if (SEQ_IS_OPEN(seq) && (ret = __seq_chk_cachesize(env,
411	    cachesize, seq->seq_rp->seq_max, seq->seq_rp->seq_min)) != 0)
412		return (ret);
413
414	seq->seq_cache_size = cachesize;
415	return (0);
416}
417
418#define	SEQ_SET_FLAGS	(DB_SEQ_WRAP | DB_SEQ_INC | DB_SEQ_DEC)
419/*
420 * __seq_get_flags --
421 *	Accessor for flags passed into DB_SEQUENCE->open call
422 *
423 */
424static int
425__seq_get_flags(seq, flagsp)
426	DB_SEQUENCE *seq;
427	u_int32_t *flagsp;
428{
429	SEQ_ILLEGAL_BEFORE_OPEN(seq, "DB_SEQUENCE->get_flags");
430
431	*flagsp = F_ISSET(seq->seq_rp, SEQ_SET_FLAGS);
432	return (0);
433}
434
435/*
436 * __seq_set_flags --
437 *	DB_SEQUENCE->set_flags.
438 *
439 */
440static int
441__seq_set_flags(seq, flags)
442	DB_SEQUENCE *seq;
443	u_int32_t flags;
444{
445	DB_SEQ_RECORD *rp;
446	ENV *env;
447	int ret;
448
449	env = seq->seq_dbp->env;
450	rp = seq->seq_rp;
451
452	SEQ_ILLEGAL_AFTER_OPEN(seq, "DB_SEQUENCE->set_flags");
453
454	if ((ret = __db_fchk(
455	    env, "DB_SEQUENCE->set_flags", flags, SEQ_SET_FLAGS)) != 0)
456		return (ret);
457	if ((ret = __db_fcchk(env,
458	     "DB_SEQUENCE->set_flags", flags, DB_SEQ_DEC, DB_SEQ_INC)) != 0)
459		return (ret);
460
461	if (LF_ISSET(DB_SEQ_DEC | DB_SEQ_INC))
462		F_CLR(rp, DB_SEQ_DEC | DB_SEQ_INC);
463	F_SET(rp, flags);
464
465	return (0);
466}
467
468/*
469 * __seq_initial_value --
470 *	DB_SEQUENCE->initial_value.
471 *
472 */
473static int
474__seq_initial_value(seq, value)
475	DB_SEQUENCE *seq;
476	db_seq_t value;
477{
478	DB_SEQ_RECORD *rp;
479	ENV *env;
480
481	env = seq->seq_dbp->env;
482	SEQ_ILLEGAL_AFTER_OPEN(seq, "DB_SEQUENCE->initial_value");
483
484	rp = seq->seq_rp;
485	if (F_ISSET(rp, DB_SEQ_RANGE_SET) &&
486	     (value > rp->seq_max || value < rp->seq_min)) {
487		__db_errx(env, "Sequence value out of range");
488		return (EINVAL);
489	}
490
491	rp->seq_value = value;
492
493	return (0);
494}
495
496/*
497 * __seq_get_range --
498 *	Accessor for range passed into DB_SEQUENCE->set_range call
499 *
500 */
501static int
502__seq_get_range(seq, minp, maxp)
503	DB_SEQUENCE *seq;
504	db_seq_t *minp, *maxp;
505{
506	SEQ_ILLEGAL_BEFORE_OPEN(seq, "DB_SEQUENCE->get_range");
507
508	*minp = seq->seq_rp->seq_min;
509	*maxp = seq->seq_rp->seq_max;
510	return (0);
511}
512
513/*
514 * __seq_set_range --
515 *	SEQUENCE->set_range.
516 *
517 */
518static int
519__seq_set_range(seq, min, max)
520	DB_SEQUENCE *seq;
521	db_seq_t min, max;
522{
523	ENV *env;
524
525	env = seq->seq_dbp->env;
526	SEQ_ILLEGAL_AFTER_OPEN(seq, "DB_SEQUENCE->set_range");
527
528	if (min >= max) {
529		__db_errx(env,
530	    "Minimum sequence value must be less than maximum sequence value");
531		return (EINVAL);
532	}
533
534	seq->seq_rp->seq_min = min;
535	seq->seq_rp->seq_max = max;
536	F_SET(seq->seq_rp, DB_SEQ_RANGE_SET);
537
538	return (0);
539}
540
541static int
542__seq_update(seq, ip, txn, delta, flags)
543	DB_SEQUENCE *seq;
544	DB_THREAD_INFO *ip;
545	DB_TXN *txn;
546	int32_t delta;
547	u_int32_t flags;
548{
549	DB *dbp;
550	DB_SEQ_RECORD *rp;
551	ENV *env;
552	int32_t adjust;
553	int ret, txn_local;
554
555	dbp = seq->seq_dbp;
556	env = dbp->env;
557
558	/*
559	 * Create a local transaction as necessary, check for consistent
560	 * transaction usage, and, if we have no transaction but do have
561	 * locking on, acquire a locker id for the handle lock acquisition.
562	 */
563	if (IS_DB_AUTO_COMMIT(dbp, txn)) {
564		if ((ret = __txn_begin(env, ip, NULL, &txn, 0)) != 0)
565			return (ret);
566		txn_local = 1;
567	} else
568		txn_local = 0;
569
570	/* Check for consistent transaction usage. */
571	if ((ret = __db_check_txn(dbp, txn, DB_LOCK_INVALIDID, 0)) != 0)
572		goto err;
573
574retry:	if ((ret = __db_get(dbp, ip,
575	    txn, &seq->seq_key, &seq->seq_data, 0)) != 0) {
576		if (ret == DB_BUFFER_SMALL &&
577		    seq->seq_data.size > sizeof(seq->seq_record)) {
578			seq->seq_data.flags = DB_DBT_REALLOC;
579			seq->seq_data.data = NULL;
580			goto retry;
581		}
582		goto err;
583	}
584
585	if (F_ISSET(env, ENV_LITTLEENDIAN))
586		seq->seq_rp = seq->seq_data.data;
587	SEQ_SWAP_IN(env, seq);
588	rp = seq->seq_rp;
589
590	if (F_ISSET(rp, DB_SEQ_WRAPPED))
591		goto overflow;
592
593	if (seq->seq_data.size < sizeof(seq->seq_record)) {
594		__db_errx(env, "Bad sequence record format");
595		ret = EINVAL;
596		goto err;
597	}
598
599	adjust = delta > seq->seq_cache_size ? delta : seq->seq_cache_size;
600
601	/*
602	 * Check whether this operation will cause the sequence to wrap.
603	 *
604	 * The sequence minimum and maximum values can be INT64_MIN and
605	 * INT64_MAX, so we need to do the test carefully to cope with
606	 * arithmetic overflow.  The first part of the test below checks
607	 * whether we will hit the end of the 64-bit range.  The second part
608	 * checks whether we hit the end of the sequence.
609	 */
610again:	if (F_ISSET(rp, DB_SEQ_INC)) {
611		if (rp->seq_value + adjust - 1 < rp->seq_value ||
612		     rp->seq_value + adjust - 1 > rp->seq_max) {
613			/* Don't wrap just to fill the cache. */
614			if (adjust > delta) {
615				adjust = delta;
616				goto again;
617			}
618			if (F_ISSET(rp, DB_SEQ_WRAP))
619				rp->seq_value = rp->seq_min;
620			else {
621overflow:			__db_errx(env, "Sequence overflow");
622				ret = EINVAL;
623				goto err;
624			}
625		}
626		/* See if we are at the end of the 64 bit range. */
627		if (!F_ISSET(rp, DB_SEQ_WRAP) &&
628		    rp->seq_value + adjust < rp->seq_value)
629			F_SET(rp, DB_SEQ_WRAPPED);
630	} else {
631		if ((rp->seq_value - adjust) + 1 > rp->seq_value ||
632		   (rp->seq_value - adjust) + 1 < rp->seq_min) {
633			/* Don't wrap just to fill the cache. */
634			if (adjust > delta) {
635				adjust = delta;
636				goto again;
637			}
638			if (F_ISSET(rp, DB_SEQ_WRAP))
639				rp->seq_value = rp->seq_max;
640			else
641				goto overflow;
642		}
643		/* See if we are at the end of the 64 bit range. */
644		if (!F_ISSET(rp, DB_SEQ_WRAP) &&
645		    rp->seq_value - adjust > rp->seq_value)
646			F_SET(rp, DB_SEQ_WRAPPED);
647		adjust = -adjust;
648	}
649
650	rp->seq_value += adjust;
651	SEQ_SWAP_OUT(env, seq);
652	ret = __db_put(dbp, ip, txn, &seq->seq_key, &seq->seq_data, 0);
653	rp->seq_value -= adjust;
654	if (ret != 0) {
655		__db_errx(env, "Sequence update failed");
656		goto err;
657	}
658	seq->seq_last_value = rp->seq_value + adjust;
659	if (F_ISSET(rp, DB_SEQ_INC))
660		seq->seq_last_value--;
661	else
662		seq->seq_last_value++;
663
664err:	return (txn_local ? __db_txn_auto_resolve(
665	    env, txn, LF_ISSET(DB_TXN_NOSYNC), ret) : ret);
666}
667
668static int
669__seq_get(seq, txn, delta, retp, flags)
670	DB_SEQUENCE *seq;
671	DB_TXN *txn;
672	int32_t delta;
673	db_seq_t *retp;
674	u_int32_t flags;
675{
676	DB *dbp;
677	DB_SEQ_RECORD *rp;
678	DB_THREAD_INFO *ip;
679	ENV *env;
680	int handle_check, ret, t_ret;
681
682	dbp = seq->seq_dbp;
683	env = dbp->env;
684	rp = seq->seq_rp;
685	ret = 0;
686
687	STRIP_AUTO_COMMIT(flags);
688	SEQ_ILLEGAL_BEFORE_OPEN(seq, "DB_SEQUENCE->get");
689
690	if (delta <= 0) {
691		__db_errx(env, "Sequence delta must be greater than 0");
692		return (EINVAL);
693	}
694
695	if (seq->seq_cache_size != 0 && txn != NULL) {
696		__db_errx(env,
697	    "Sequence with non-zero cache may not specify transaction handle");
698		return (EINVAL);
699	}
700
701	ENV_ENTER(env, ip);
702
703	/* Check for replication block. */
704	handle_check = IS_ENV_REPLICATED(env);
705	if (handle_check && (ret = __db_rep_enter(dbp, 1, 0, txn != NULL)) != 0)
706		return (ret);
707
708	MUTEX_LOCK(env, seq->mtx_seq);
709
710	if (rp->seq_min + delta > rp->seq_max) {
711		__db_errx(env, "Sequence overflow");
712		ret = EINVAL;
713		goto err;
714	}
715
716	if (F_ISSET(rp, DB_SEQ_INC)) {
717		if (seq->seq_last_value + 1 - rp->seq_value < delta &&
718		   (ret = __seq_update(seq, ip, txn, delta, flags)) != 0)
719			goto err;
720
721		rp = seq->seq_rp;
722		*retp = rp->seq_value;
723		rp->seq_value += delta;
724	} else {
725		if ((rp->seq_value - seq->seq_last_value) + 1 < delta &&
726		    (ret = __seq_update(seq, ip, txn, delta, flags)) != 0)
727			goto err;
728
729		rp = seq->seq_rp;
730		*retp = rp->seq_value;
731		rp->seq_value -= delta;
732	}
733
734err:	MUTEX_UNLOCK(env, seq->mtx_seq);
735
736	/* Release replication block. */
737	if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0)
738		ret = t_ret;
739
740	ENV_LEAVE(env, ip);
741	return (ret);
742}
743
744/*
745 * __seq_get_db --
746 *	Accessor for dbp passed into db_sequence_create call
747 *
748 */
749static int
750__seq_get_db(seq, dbpp)
751	DB_SEQUENCE *seq;
752	DB **dbpp;
753{
754	*dbpp = seq->seq_dbp;
755	return (0);
756}
757
758/*
759 * __seq_get_key --
760 *	Accessor for key passed into DB_SEQUENCE->open call
761 *
762 */
763static int
764__seq_get_key(seq, key)
765	DB_SEQUENCE *seq;
766	DBT *key;
767{
768	SEQ_ILLEGAL_BEFORE_OPEN(seq, "DB_SEQUENCE->get_key");
769
770	if (F_ISSET(key, DB_DBT_USERCOPY))
771		return (__db_retcopy(seq->seq_dbp->env, key,
772		    seq->seq_key.data, seq->seq_key.size, NULL, 0));
773
774	key->data = seq->seq_key.data;
775	key->size = key->ulen = seq->seq_key.size;
776	key->flags = seq->seq_key.flags;
777	return (0);
778}
779
780/*
781 * __seq_close --
782 *	Close a sequence
783 *
784 */
785static int
786__seq_close(seq, flags)
787	DB_SEQUENCE *seq;
788	u_int32_t flags;
789{
790	ENV *env;
791	int ret, t_ret;
792
793	ret = 0;
794	env = seq->seq_dbp->env;
795
796	if (flags != 0)
797		ret = __db_ferr(env, "DB_SEQUENCE->close", 0);
798
799	if ((t_ret = __mutex_free(env, &seq->mtx_seq)) != 0 && ret == 0)
800		ret = t_ret;
801
802	if (seq->seq_key.data != NULL)
803		__os_free(env, seq->seq_key.data);
804	if (seq->seq_data.data != NULL &&
805	    seq->seq_data.data != &seq->seq_record)
806		__os_ufree(env, seq->seq_data.data);
807	seq->seq_key.data = NULL;
808
809	memset(seq, CLEAR_BYTE, sizeof(*seq));
810	__os_free(env, seq);
811
812	return (ret);
813}
814
815/*
816 * __seq_remove --
817 *	Remove a sequence from the database.
818 */
819static int
820__seq_remove(seq, txn, flags)
821	DB_SEQUENCE *seq;
822	DB_TXN *txn;
823	u_int32_t flags;
824{
825	DB *dbp;
826	DB_THREAD_INFO *ip;
827	ENV *env;
828	int handle_check, ret, t_ret, txn_local;
829
830	dbp = seq->seq_dbp;
831	env = dbp->env;
832	txn_local = 0;
833
834	SEQ_ILLEGAL_BEFORE_OPEN(seq, "DB_SEQUENCE->remove");
835	ENV_ENTER(env, ip);
836
837	/* Check for replication block. */
838	handle_check = IS_ENV_REPLICATED(env);
839	if (handle_check &&
840	    (ret = __db_rep_enter(dbp, 1, 0, txn != NULL)) != 0) {
841		handle_check = 0;
842		goto err;
843	}
844	if (flags != 0) {
845		ret = __db_ferr(env, "DB_SEQUENCE->remove", 0);
846		goto err;
847	}
848
849	/*
850	 * Create a local transaction as necessary, check for consistent
851	 * transaction usage, and, if we have no transaction but do have
852	 * locking on, acquire a locker id for the handle lock acquisition.
853	 */
854	if (IS_DB_AUTO_COMMIT(dbp, txn)) {
855		if ((ret = __txn_begin(env, ip, NULL, &txn, 0)) != 0)
856			return (ret);
857		txn_local = 1;
858	}
859
860	/* Check for consistent transaction usage. */
861	if ((ret = __db_check_txn(dbp, txn, DB_LOCK_INVALIDID, 0)) != 0)
862		goto err;
863
864	ret = __db_del(dbp, ip, txn, &seq->seq_key, 0);
865
866	if ((t_ret = __seq_close(seq, 0)) != 0 && ret == 0)
867		ret = t_ret;
868
869	/* Release replication block. */
870	if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0)
871		ret = t_ret;
872err:	if (txn_local && (t_ret =
873	    __db_txn_auto_resolve(env, txn, 0, ret)) != 0 && ret == 0)
874		ret = t_ret;
875
876	ENV_LEAVE(env, ip);
877	return (ret);
878}
879
880/*
881 * __seq_chk_cachesize --
882 *	Validate the cache size vs. the range.
883 */
884static int
885__seq_chk_cachesize(env, cachesize, max, min)
886	ENV *env;
887	int32_t cachesize;
888	db_seq_t max, min;
889{
890	/*
891	 * It's an error to specify caches larger than the sequence range.
892	 *
893	 * The min and max of the range can be either positive or negative,
894	 * the difference will fit in an unsigned variable of the same type.
895	 * Assume a 2's complement machine, and simply subtract.
896	 */
897	if ((u_int32_t)cachesize > (u_int64_t)max - (u_int64_t)min) {
898		__db_errx(env,
899    "Number of items to be cached is larger than the sequence range");
900		return (EINVAL);
901	}
902	return (0);
903}
904
905#else /* !HAVE_64BIT_TYPES */
906
907int
908db_sequence_create(seqp, dbp, flags)
909	DB_SEQUENCE **seqp;
910	DB *dbp;
911	u_int32_t flags;
912{
913	COMPQUIET(seqp, NULL);
914	COMPQUIET(flags, 0);
915	__db_errx(dbp->env,
916	    "library build did not include support for sequences");
917	return (DB_OPNOTSUP);
918}
919#endif /* HAVE_64BIT_TYPES */
920