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