1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2004-2009 Oracle. All rights reserved. 5 * 6 * $Id$ 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, flags)) != 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 if (rp->seq_min + delta > rp->seq_max) { 731 __db_errx(env, "Sequence overflow"); 732 ret = EINVAL; 733 goto err; 734 } 735 736 if (F_ISSET(rp, DB_SEQ_INC)) { 737 if (seq->seq_last_value + 1 - rp->seq_value < delta && 738 (ret = __seq_update(seq, ip, txn, delta, flags)) != 0) 739 goto err; 740 741 rp = seq->seq_rp; 742 *retp = rp->seq_value; 743 rp->seq_value += delta; 744 } else { 745 if ((rp->seq_value - seq->seq_last_value) + 1 < delta && 746 (ret = __seq_update(seq, ip, txn, delta, flags)) != 0) 747 goto err; 748 749 rp = seq->seq_rp; 750 *retp = rp->seq_value; 751 rp->seq_value -= delta; 752 } 753 754err: MUTEX_UNLOCK(env, seq->mtx_seq); 755 756 /* Release replication block. */ 757 if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0) 758 ret = t_ret; 759 760 ENV_LEAVE(env, ip); 761 return (ret); 762} 763 764/* 765 * __seq_get_db -- 766 * Accessor for dbp passed into db_sequence_create call 767 * 768 */ 769static int 770__seq_get_db(seq, dbpp) 771 DB_SEQUENCE *seq; 772 DB **dbpp; 773{ 774 *dbpp = seq->seq_dbp; 775 return (0); 776} 777 778/* 779 * __seq_get_key -- 780 * Accessor for key passed into DB_SEQUENCE->open call 781 * 782 */ 783static int 784__seq_get_key(seq, key) 785 DB_SEQUENCE *seq; 786 DBT *key; 787{ 788 SEQ_ILLEGAL_BEFORE_OPEN(seq, "DB_SEQUENCE->get_key"); 789 790 if (F_ISSET(key, DB_DBT_USERCOPY)) 791 return (__db_retcopy(seq->seq_dbp->env, key, 792 seq->seq_key.data, seq->seq_key.size, NULL, 0)); 793 794 key->data = seq->seq_key.data; 795 key->size = key->ulen = seq->seq_key.size; 796 key->flags = seq->seq_key.flags; 797 return (0); 798} 799 800/* 801 * __seq_close -- 802 * Close a sequence 803 * 804 */ 805static int 806__seq_close(seq, flags) 807 DB_SEQUENCE *seq; 808 u_int32_t flags; 809{ 810 ENV *env; 811 int ret, t_ret; 812 813 ret = 0; 814 env = seq->seq_dbp->env; 815 816 if (flags != 0) 817 ret = __db_ferr(env, "DB_SEQUENCE->close", 0); 818 819 if ((t_ret = __mutex_free(env, &seq->mtx_seq)) != 0 && ret == 0) 820 ret = t_ret; 821 822 if (seq->seq_key.data != NULL) 823 __os_free(env, seq->seq_key.data); 824 if (seq->seq_data.data != NULL && 825 seq->seq_data.data != &seq->seq_record) 826 __os_ufree(env, seq->seq_data.data); 827 seq->seq_key.data = NULL; 828 829 memset(seq, CLEAR_BYTE, sizeof(*seq)); 830 __os_free(env, seq); 831 832 return (ret); 833} 834 835/* 836 * __seq_remove -- 837 * Remove a sequence from the database. 838 */ 839static int 840__seq_remove(seq, txn, flags) 841 DB_SEQUENCE *seq; 842 DB_TXN *txn; 843 u_int32_t flags; 844{ 845 DB *dbp; 846 DB_THREAD_INFO *ip; 847 ENV *env; 848 int handle_check, ret, t_ret, txn_local; 849 850 dbp = seq->seq_dbp; 851 env = dbp->env; 852 txn_local = 0; 853 854 SEQ_ILLEGAL_BEFORE_OPEN(seq, "DB_SEQUENCE->remove"); 855 856 /* 857 * Flags can only be 0, unless the database has DB_AUTO_COMMIT enabled. 858 * Then DB_TXN_NOSYNC is allowed. 859 */ 860 if (flags != 0 && 861 (flags != DB_TXN_NOSYNC || !IS_DB_AUTO_COMMIT(dbp, txn))) 862 return (__db_ferr(env, "DB_SEQUENCE->remove illegal flag", 0)); 863 864 ENV_ENTER(env, ip); 865 866 /* Check for replication block. */ 867 handle_check = IS_ENV_REPLICATED(env); 868 if (handle_check && 869 (ret = __db_rep_enter(dbp, 1, 0, txn != NULL)) != 0) { 870 handle_check = 0; 871 goto err; 872 } 873 874 /* 875 * Create a local transaction as necessary, check for consistent 876 * transaction usage, and, if we have no transaction but do have 877 * locking on, acquire a locker id for the handle lock acquisition. 878 */ 879 if (IS_DB_AUTO_COMMIT(dbp, txn)) { 880 if ((ret = __txn_begin(env, ip, NULL, &txn, flags)) != 0) 881 return (ret); 882 txn_local = 1; 883 } 884 885 /* Check for consistent transaction usage. */ 886 if ((ret = __db_check_txn(dbp, txn, DB_LOCK_INVALIDID, 0)) != 0) 887 goto err; 888 889 ret = __db_del(dbp, ip, txn, &seq->seq_key, 0); 890 891 if ((t_ret = __seq_close(seq, 0)) != 0 && ret == 0) 892 ret = t_ret; 893 894 /* Release replication block. */ 895 if (handle_check && (t_ret = __env_db_rep_exit(env)) != 0 && ret == 0) 896 ret = t_ret; 897err: if (txn_local && (t_ret = 898 __db_txn_auto_resolve(env, txn, 0, ret)) != 0 && ret == 0) 899 ret = t_ret; 900 901 ENV_LEAVE(env, ip); 902 return (ret); 903} 904 905/* 906 * __seq_chk_cachesize -- 907 * Validate the cache size vs. the range. 908 */ 909static int 910__seq_chk_cachesize(env, cachesize, max, min) 911 ENV *env; 912 int32_t cachesize; 913 db_seq_t max, min; 914{ 915 /* 916 * It's an error to specify caches larger than the sequence range. 917 * 918 * The min and max of the range can be either positive or negative, 919 * the difference will fit in an unsigned variable of the same type. 920 * Assume a 2's complement machine, and simply subtract. 921 */ 922 if ((u_int32_t)cachesize > (u_int64_t)max - (u_int64_t)min) { 923 __db_errx(env, 924 "Number of items to be cached is larger than the sequence range"); 925 return (EINVAL); 926 } 927 return (0); 928} 929 930#else /* !HAVE_64BIT_TYPES */ 931 932int 933db_sequence_create(seqp, dbp, flags) 934 DB_SEQUENCE **seqp; 935 DB *dbp; 936 u_int32_t flags; 937{ 938 COMPQUIET(seqp, NULL); 939 COMPQUIET(flags, 0); 940 __db_errx(dbp->env, 941 "library build did not include support for sequences"); 942 return (DB_OPNOTSUP); 943} 944#endif /* HAVE_64BIT_TYPES */ 945