]>
sourceware.org Git - glibc.git/blob - db2/hash/hash.c
2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997
5 * Sleepycat Software. All rights reserved.
8 * Copyright (c) 1990, 1993, 1994
9 * Margo Seltzer. All rights reserved.
12 * Copyright (c) 1990, 1993, 1994
13 * The Regents of the University of California. All rights reserved.
15 * This code is derived from software contributed to Berkeley by
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions
21 * 1. Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
23 * 2. Redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in the
25 * documentation and/or other materials provided with the distribution.
26 * 3. All advertising materials mentioning features or use of this software
27 * must display the following acknowledgement:
28 * This product includes software developed by the University of
29 * California, Berkeley and its contributors.
30 * 4. Neither the name of the University nor the names of its contributors
31 * may be used to endorse or promote products derived from this software
32 * without specific prior written permission.
34 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
35 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50 static const char sccsid
[] = "@(#)hash.c 10.25 (Sleepycat) 8/24/97";
53 #ifndef NO_SYSTEM_INCLUDES
54 #include <sys/types.h>
73 static int __ham_c_close
__P((DBC
*));
74 static int __ham_c_del
__P((DBC
*, int));
75 static int __ham_c_get
__P((DBC
*, DBT
*, DBT
*, int));
76 static int __ham_c_put
__P((DBC
*, DBT
*, DBT
*, int));
77 static int __ham_c_init
__P((DB
*, DB_TXN
*, DBC
**));
78 static int __ham_cursor
__P((DB
*, DB_TXN
*, DBC
**));
79 static int __ham_delete
__P((DB
*, DB_TXN
*, DBT
*, int));
80 static int __ham_dup_return
__P((HTAB
*, HASH_CURSOR
*, DBT
*, int));
81 static int __ham_get
__P((DB
*, DB_TXN
*, DBT
*, DBT
*, int));
82 static void __ham_init_htab
__P((HTAB
*));
83 static int __ham_lookup
__P((HTAB
*,
84 HASH_CURSOR
*, const DBT
*, u_int32_t
, db_lockmode_t
));
85 static int __ham_overwrite
__P((HTAB
*, HASH_CURSOR
*, DBT
*));
86 static int __ham_put
__P((DB
*, DB_TXN
*, DBT
*, DBT
*, int));
87 static int __ham_sync
__P((DB
*, int));
89 /************************** INTERFACE ROUTINES ***************************/
95 * PUBLIC: int __ham_open __P((DB *, DB_INFO *));
98 __ham_open(dbp
, dbinfo
)
105 int file_existed
, ret
;
109 if ((hashp
= (HTAB
*)calloc(1, sizeof(HTAB
))) == NULL
)
113 /* Set the hash function if specified by the user. */
114 if (dbinfo
!= NULL
&& dbinfo
->h_hash
!= NULL
)
115 hashp
->hash
= dbinfo
->h_hash
;
118 * Initialize the remaining fields of the dbp. The type, close and
119 * fd functions are all set in db_open.
121 dbp
->internal
= hashp
;
122 dbp
->cursor
= __ham_cursor
;
123 dbp
->del
= __ham_delete
;
124 dbp
->get
= __ham_get
;
125 dbp
->put
= __ham_put
;
126 dbp
->sync
= __ham_sync
;
128 /* If locking is turned on, lock the meta data page. */
129 if (F_ISSET(dbp
, DB_AM_LOCKING
)) {
130 dbp
->lock
.pgno
= BUCKET_INVALID
;
131 if ((ret
= lock_get(dbenv
->lk_info
, dbp
->locker
,
132 0, &dbp
->lock_dbt
, DB_LOCK_READ
, &hashp
->hlock
)) != 0) {
140 * Now, we can try to read the meta-data page and figure out
141 * if we set up locking and get the meta-data page properly.
142 * If this is a new file, initialize it, and put it back dirty.
144 if ((ret
= __ham_get_page(hashp
->dbp
, 0, (PAGE
**)&hashp
->hdr
)) != 0)
147 /* Initialize the hashp structure */
148 if (hashp
->hdr
->magic
== DB_HASHMAGIC
) {
150 /* File exists, verify the data in the header. */
151 if (hashp
->hash
== NULL
)
153 hashp
->hdr
->version
< 5 ? __ham_func4
: __ham_func5
;
154 if (hashp
->hash(CHARKEY
, sizeof(CHARKEY
)) !=
155 hashp
->hdr
->h_charkey
) {
156 __db_err(hashp
->dbp
->dbenv
,
157 "hash: incompatible hash function");
161 if (F_ISSET(hashp
->hdr
, DB_HASH_DUP
))
162 F_SET(dbp
, DB_AM_DUP
);
165 * File does not exist, we must initialize the header. If
166 * locking is enabled that means getting a write lock first.
169 if (F_ISSET(dbp
, DB_AM_LOCKING
) &&
170 ((ret
= lock_put(dbenv
->lk_info
, hashp
->hlock
)) != 0 ||
171 (ret
= lock_get(dbenv
->lk_info
, dbp
->locker
, 0,
172 &dbp
->lock_dbt
, DB_LOCK_WRITE
, &hashp
->hlock
)) != 0)) {
178 hashp
->hdr
->nelem
= dbinfo
!= NULL
? dbinfo
->h_nelem
: 0;
179 hashp
->hdr
->ffactor
=
180 dbinfo
!= NULL
&& dbinfo
->h_ffactor
? dbinfo
->h_ffactor
: 0;
181 __ham_init_htab(hashp
);
182 if (F_ISSET(dbp
, DB_AM_DUP
))
183 F_SET(hashp
->hdr
, DB_HASH_DUP
);
184 if ((ret
= __ham_dirty_page(hashp
, (PAGE
*)hashp
->hdr
)) != 0)
188 /* Initialize the default cursor. */
189 __ham_c_init(dbp
, NULL
, &curs
);
190 TAILQ_INSERT_TAIL(&dbp
->curs_queue
, curs
, links
);
192 /* Allocate memory for our split buffer. */
193 if ((hashp
->split_buf
= (PAGE
*)malloc(dbp
->pgsize
)) == NULL
) {
198 #ifdef NO_STATISTICS_FOR_DB_ERR
200 "%s%lx\n%s%ld\n%s%ld\n%s%ld\n%s%ld\n%s0x%lx\n%s0x%lx\n%s%ld\n%s%ld\n%s0x%lx",
201 "TABLE POINTER ", (long)hashp
,
202 "BUCKET SIZE ", (long)hashp
->hdr
->pagesize
,
203 "FILL FACTOR ", (long)hashp
->hdr
->ffactor
,
204 "MAX BUCKET ", (long)hashp
->hdr
->max_bucket
,
205 "OVFL POINT ", (long)hashp
->hdr
->ovfl_point
,
206 "LAST FREED ", (long)hashp
->hdr
->last_freed
,
207 "HIGH MASK ", (long)hashp
->hdr
->high_mask
,
208 "LOW MASK ", (long)hashp
->hdr
->low_mask
,
209 "NELEM ", (long)hashp
->hdr
->nelem
,
210 "FLAGS ", (long)hashp
->hdr
->flags
);
213 /* Release the meta data page */
214 (void)__ham_put_page(hashp
->dbp
, (PAGE
*)hashp
->hdr
, 0);
215 if (F_ISSET(dbp
, DB_AM_LOCKING
) &&
216 (ret
= lock_put(dbenv
->lk_info
, hashp
->hlock
)) != 0) {
224 /* Sync the file so that we know that the meta data goes to disk. */
225 if (!file_existed
&& (ret
= dbp
->sync(dbp
, 0)) != 0)
229 out
: (void)__ham_close(dbp
);
234 * PUBLIC: int __ham_close __P((DB *));
243 DEBUG_LWRITE(dbp
, NULL
, "ham_close", NULL
, NULL
, 0);
244 hashp
= (HTAB
*)dbp
->internal
;
247 /* Free the split page. */
248 if (hashp
->split_buf
)
249 FREE(hashp
->split_buf
, dbp
->pgsize
);
251 if (hashp
->hdr
&& (t_ret
= __ham_put_page(hashp
->dbp
,
252 (PAGE
*)hashp
->hdr
, 0)) != 0 && ret
== 0)
254 if (hashp
->hlock
&& (t_ret
= lock_put(hashp
->dbp
->dbenv
->lk_info
,
255 hashp
->hlock
)) != 0 && ret
== 0)
258 FREE(hashp
, sizeof(HTAB
));
259 dbp
->internal
= NULL
;
263 /************************** LOCAL CREATION ROUTINES **********************/
265 * Returns 0 on No Error
268 __ham_init_htab(hashp
)
272 int32_t l2
, nbuckets
;
274 nelem
= hashp
->hdr
->nelem
;
275 hashp
->hdr
->pagesize
= hashp
->dbp
->pgsize
;
276 ZERO_LSN(hashp
->hdr
->lsn
);
277 hashp
->hdr
->magic
= DB_HASHMAGIC
;
278 hashp
->hdr
->version
= DB_HASHVERSION
;
279 if (hashp
->hash
== NULL
)
281 hashp
->hdr
->version
< 5 ? __ham_func4
: __ham_func5
;
282 hashp
->hdr
->h_charkey
= hashp
->hash(CHARKEY
, sizeof(CHARKEY
));
283 if (nelem
!= 0 && hashp
->hdr
->ffactor
!= 0) {
284 nelem
= (nelem
- 1) / hashp
->hdr
->ffactor
+ 1;
285 l2
= __db_log2(nelem
> 2 ? nelem
: 2);
291 hashp
->hdr
->spares
[l2
] = 0;
292 hashp
->hdr
->spares
[l2
+ 1] = 0;
293 hashp
->hdr
->ovfl_point
= l2
;
294 hashp
->hdr
->last_freed
= PGNO_INVALID
;
296 hashp
->hdr
->max_bucket
= hashp
->hdr
->high_mask
= nbuckets
- 1;
297 hashp
->hdr
->low_mask
= (nbuckets
>> 1) - 1;
298 memcpy(hashp
->hdr
->uid
, hashp
->dbp
->lock
.fileid
, DB_FILE_ID_LEN
);
301 /********************** DESTROY/CLOSE ROUTINES ************************/
305 * Write modified pages to disk
312 __ham_sync(dbp
, flags
)
318 DEBUG_LWRITE(dbp
, NULL
, "ham_sync", NULL
, NULL
, flags
);
319 if ((ret
= __db_syncchk(dbp
, flags
)) != 0)
321 if (F_ISSET(dbp
, DB_AM_RDONLY
))
324 if ((ret
= memp_fsync(dbp
->mpf
)) == DB_INCOMPLETE
)
330 /*******************************SEARCH ROUTINES *****************************/
332 * All the access routines return
336 * 1 to indicate an external ERROR (i.e. key not found, etc)
337 * -1 to indicate an internal ERROR (i.e. out of memory, etc)
341 __ham_get(dbp
, txn
, key
, data
, flags
)
354 DEBUG_LREAD(dbp
, txn
, "ham_get", key
, NULL
, flags
);
355 if ((ret
= __db_getchk(dbp
, key
, data
, flags
)) != 0)
359 if (F_ISSET(dbp
, DB_AM_THREAD
) &&
360 (ret
= __db_gethandle(dbp
, __ham_hdup
, &ldbp
)) != 0)
363 hashp
= (HTAB
*)ldbp
->internal
;
364 SET_LOCKER(ldbp
, txn
);
365 GET_META(ldbp
, hashp
);
366 cp
= TAILQ_FIRST(&ldbp
->curs_queue
);
368 hashp
->hash_accesses
++;
369 hcp
= (HASH_CURSOR
*)TAILQ_FIRST(&ldbp
->curs_queue
)->internal
;
370 if ((ret
= __ham_lookup(hashp
, hcp
, key
, 0, DB_LOCK_READ
)) == 0)
371 if (F_ISSET(hcp
, H_OK
))
372 ret
= __ham_dup_return(hashp
, hcp
, data
, DB_FIRST
);
373 else /* Key was not found */
376 if ((t_ret
= __ham_item_done(hashp
, hcp
, 0)) != 0 && ret
== 0)
378 RELEASE_META(ldbp
, hashp
);
379 if (F_ISSET(dbp
, DB_AM_THREAD
))
380 __db_puthandle(ldbp
);
385 __ham_put(dbp
, txn
, key
, data
, flags
)
399 DEBUG_LWRITE(dbp
, txn
, "ham_put", key
, data
, flags
);
400 if ((ret
= __db_putchk(dbp
, key
, data
,
401 flags
, F_ISSET(dbp
, DB_AM_RDONLY
), F_ISSET(dbp
, DB_AM_DUP
))) != 0)
405 if (F_ISSET(dbp
, DB_AM_THREAD
) &&
406 (ret
= __db_gethandle(dbp
, __ham_hdup
, &ldbp
)) != 0)
409 hashp
= (HTAB
*)ldbp
->internal
;
410 SET_LOCKER(ldbp
, txn
);
411 GET_META(ldbp
, hashp
);
412 hcp
= TAILQ_FIRST(&ldbp
->curs_queue
)->internal
;
414 nbytes
= (ISBIG(hashp
, key
->size
) ? HOFFPAGE_PSIZE
:
415 HKEYDATA_PSIZE(key
->size
)) +
416 (ISBIG(hashp
, data
->size
) ? HOFFPAGE_PSIZE
:
417 HKEYDATA_PSIZE(data
->size
));
419 hashp
->hash_accesses
++;
420 ret
= __ham_lookup(hashp
, hcp
, key
, nbytes
, DB_LOCK_WRITE
);
422 if (ret
== DB_NOTFOUND
) {
424 if (hcp
->seek_found_page
!= PGNO_INVALID
&&
425 hcp
->seek_found_page
!= hcp
->pgno
) {
426 if ((ret
= __ham_item_done(hashp
, hcp
, 0)) != 0)
428 hcp
->pgno
= hcp
->seek_found_page
;
429 hcp
->bndx
= NDX_INVALID
;
432 if (F_ISSET(data
, DB_DBT_PARTIAL
) && data
->doff
!= 0) {
434 * Doing a partial put, but the key does not exist
435 * and we are not beginning the write at 0. We
436 * must create a data item padded up to doff and
437 * then write the new bytes represented by val.
439 ret
= __ham_init_dbt(&tmp_val
, data
->size
+ data
->doff
,
440 &hcp
->big_data
, &hcp
->big_datalen
);
442 memset(tmp_val
.data
, 0, data
->doff
);
443 memcpy((u_int8_t
*)tmp_val
.data
+ data
->doff
,
444 data
->data
, data
->size
);
451 ret
= __ham_add_el(hashp
, hcp
, key
, myval
, H_KEYDATA
);
452 } else if (ret
== 0 && F_ISSET(hcp
, H_OK
)) {
453 if (flags
== DB_NOOVERWRITE
)
455 else if (F_ISSET(ldbp
, DB_AM_DUP
))
456 ret
= __ham_add_dup(hashp
, hcp
, data
, DB_KEYLAST
);
458 ret
= __ham_overwrite(hashp
, hcp
, data
);
461 /* Free up all the cursor pages. */
462 if ((t_ret
= __ham_item_done(hashp
, hcp
, ret
== 0)) != 0 && ret
== 0)
464 /* Now check if we have to grow. */
465 out
: if (ret
== 0 && F_ISSET(hcp
, H_EXPAND
)) {
466 ret
= __ham_expand_table(hashp
);
467 F_CLR(hcp
, H_EXPAND
);
470 if ((t_ret
= __ham_item_done(hashp
, hcp
, ret
== 0)) != 0 && ret
== 0)
472 RELEASE_META(ldbp
, hashp
);
473 if (F_ISSET(dbp
, DB_AM_THREAD
))
474 __db_puthandle(ldbp
);
479 __ham_cursor(dbp
, txnid
, dbcp
)
486 DEBUG_LWRITE(dbp
, txnid
, "ham_cursor", NULL
, NULL
, 0);
487 if ((ret
= __ham_c_init(dbp
, txnid
, dbcp
)) != 0)
491 TAILQ_INSERT_TAIL(&dbp
->curs_queue
, *dbcp
, links
);
492 DB_THREAD_UNLOCK(dbp
);
497 __ham_c_init(dbp
, txnid
, dbcp
)
503 HASH_CURSOR
*new_curs
;
505 if ((db_curs
= (DBC
*)calloc(sizeof(DBC
), 1)) == NULL
)
509 (HASH_CURSOR
*)calloc(sizeof(struct cursor_t
), 1)) == NULL
) {
510 FREE(db_curs
, sizeof(DBC
));
514 db_curs
->internal
= new_curs
;
515 db_curs
->c_close
= __ham_c_close
;
516 db_curs
->c_del
= __ham_c_del
;
517 db_curs
->c_get
= __ham_c_get
;
518 db_curs
->c_put
= __ham_c_put
;
519 db_curs
->txn
= txnid
;
522 new_curs
->db_cursor
= db_curs
;
523 __ham_item_init(new_curs
);
531 __ham_delete(dbp
, txn
, key
, flags
)
542 DEBUG_LWRITE(dbp
, txn
, "ham_delete", key
, NULL
, flags
);
543 if ((ret
= __db_delchk(dbp
, flags
, F_ISSET(dbp
, DB_AM_RDONLY
))) != 0)
547 if (F_ISSET(dbp
, DB_AM_THREAD
) &&
548 (ret
= __db_gethandle(dbp
, __ham_hdup
, &ldbp
)) != 0)
550 hashp
= (HTAB
*)ldbp
->internal
;
551 SET_LOCKER(ldbp
, txn
);
552 GET_META(ldbp
, hashp
);
553 hcp
= TAILQ_FIRST(&ldbp
->curs_queue
)->internal
;
555 hashp
->hash_accesses
++;
556 if ((ret
= __ham_lookup(hashp
, hcp
, key
, 0, DB_LOCK_WRITE
)) == 0)
557 if (F_ISSET(hcp
, H_OK
))
558 ret
= __ham_del_pair(hashp
, hcp
);
562 if ((t_ret
= __ham_item_done(hashp
, hcp
, ret
== 0)) != 0 && ret
== 0)
564 RELEASE_META(ldbp
, hashp
);
565 if (F_ISSET(dbp
, DB_AM_THREAD
))
566 __db_puthandle(ldbp
);
570 /* ****************** CURSORS ********************************** */
572 __ham_c_close(cursor
)
580 DEBUG_LWRITE(cursor
->dbp
, cursor
->txn
, "ham_c_close", NULL
, NULL
, 0);
582 * If the pagep, dpagep, and lock fields of the cursor are all NULL,
583 * then there really isn't a need to get a handle here. However,
584 * the normal case is that at least one of those fields is non-NULL,
585 * and putting those checks in here would couple the ham_item_done
586 * functionality with cursor close which would be pretty disgusting.
587 * Instead, we pay the overhead here of always getting the handle.
590 if (F_ISSET(cursor
->dbp
, DB_AM_THREAD
) &&
591 (ret
= __db_gethandle(cursor
->dbp
, __ham_hdup
, &ldbp
)) != 0)
593 hashp
= (HTAB
*)ldbp
->internal
;
594 hcp
= (HASH_CURSOR
*)cursor
->internal
;
595 ret
= __ham_item_done(hashp
, hcp
, 0);
598 FREE(hcp
->big_key
, hcp
->big_keylen
);
600 FREE(hcp
->big_data
, hcp
->big_datalen
);
603 * All cursors (except the default ones) are linked off the master.
604 * Therefore, when we close the cursor, we have to remove it from
605 * the master, not the local one. When we are closing the file in
606 * its entirety, then we clear the THREAD bit and the master and
607 * local are identical, so we remove the correct one.
609 DB_THREAD_LOCK(cursor
->dbp
);
610 TAILQ_REMOVE(&cursor
->dbp
->curs_queue
, cursor
, links
);
611 DB_THREAD_UNLOCK(cursor
->dbp
);
613 if (F_ISSET(cursor
->dbp
, DB_AM_THREAD
))
614 __db_puthandle(ldbp
);
616 FREE(hcp
, sizeof(HASH_CURSOR
));
617 FREE(cursor
, sizeof(DBC
));
622 __ham_c_del(cursor
, flags
)
629 HASH_CURSOR save_curs
;
630 db_pgno_t ppgno
, chg_pgno
;
633 DEBUG_LWRITE(cursor
->dbp
, cursor
->txn
, "ham_c_del", NULL
, NULL
, flags
);
635 if (F_ISSET(cursor
->dbp
, DB_AM_THREAD
) &&
636 (ret
= __db_gethandle(cursor
->dbp
, __ham_hdup
, &ldbp
)) != 0)
638 hashp
= (HTAB
*)ldbp
->internal
;
639 hcp
= (HASH_CURSOR
*)cursor
->internal
;
641 if ((ret
= __db_cdelchk(ldbp
, flags
,
642 F_ISSET(ldbp
, DB_AM_RDONLY
), IS_VALID(hcp
))) != 0)
644 if (F_ISSET(hcp
, H_DELETED
))
645 return (DB_NOTFOUND
);
647 SET_LOCKER(hashp
->dbp
, cursor
->txn
);
648 GET_META(hashp
->dbp
, hashp
);
649 hashp
->hash_accesses
++;
650 if ((ret
= __ham_get_cpage(hashp
, hcp
, DB_LOCK_WRITE
)) != 0)
652 if (F_ISSET(hcp
, H_ISDUP
) && hcp
->dpgno
!= PGNO_INVALID
) {
653 ppgno
= PREV_PGNO(hcp
->dpagep
);
655 /* Remove item from duplicate page. */
656 chg_pgno
= hcp
->dpgno
;
657 if ((ret
= __db_drem(hashp
->dbp
,
658 &hcp
->dpagep
, hcp
->dndx
, __ham_del_page
)) != 0)
663 * 1. We removed an item on a page, but nothing else changed.
664 * 2. We removed the last item on a page, but there is a
665 * following page of duplicates.
666 * 3. We removed the last item on a page, this page was the
667 * last page in a duplicate set, but there were dups before
669 * 4. We removed the last item on a page, removing the last
671 * In case 1 hcp->dpagep is unchanged.
672 * In case 2 hcp->dpagep comes back pointing to the next dup
674 * In case 3 hcp->dpagep comes back NULL.
675 * In case 4 hcp->dpagep comes back NULL.
677 if (hcp
->dpagep
== NULL
) {
678 if (ppgno
!= PGNO_INVALID
) { /* Case 3 */
680 if ((ret
= __ham_get_cpage(hashp
, hcp
,
683 hcp
->dndx
= NUM_ENT(hcp
->dpagep
);
684 F_SET(hcp
, H_DELETED
);
685 } else { /* Case 4 */
686 ret
= __ham_del_pair(hashp
, hcp
);
687 hcp
->dpgno
= PGNO_INVALID
;
689 * Delpair updated the cursor queue, so we
690 * don't have to do that here.
692 chg_pgno
= PGNO_INVALID
;
694 } else if (PGNO(hcp
->dpagep
) != hcp
->dpgno
) {
695 hcp
->dndx
= 0; /* Case 2 */
696 hcp
->dpgno
= PGNO(hcp
->dpagep
);
697 if (ppgno
== PGNO_INVALID
)
698 memcpy(P_ENTRY(hcp
->pagep
,
699 H_DATAINDEX(hcp
->bndx
)) +
700 SSZ(HOFFDUP
, pgno
), &hcp
->dpgno
,
702 F_SET(hcp
, H_DELETED
);
704 F_SET(hcp
, H_DELETED
);
705 if (chg_pgno
!= PGNO_INVALID
)
706 __ham_c_update(hashp
, hcp
, chg_pgno
, 0, 0, 1);
707 } else if (F_ISSET(hcp
, H_ISDUP
)) { /* on page */
708 if (hcp
->dup_off
== 0 && DUP_SIZE(hcp
->dup_len
) ==
709 LEN_HDATA(hcp
->pagep
, hashp
->hdr
->pagesize
, hcp
->bndx
))
710 ret
= __ham_del_pair(hashp
, hcp
);
715 F_SET(&repldbt
, DB_DBT_PARTIAL
);
716 repldbt
.doff
= hcp
->dup_off
;
717 repldbt
.dlen
= DUP_SIZE(hcp
->dup_len
);
719 ret
= __ham_replpair(hashp
, hcp
, &repldbt
, 0);
720 hcp
->dup_tlen
-= DUP_SIZE(hcp
->dup_len
);
721 __ham_c_update(hashp
, hcp
, hcp
->pgno
,
722 DUP_SIZE(hcp
->dup_len
), 0, 1);
723 F_SET(hcp
, H_DELETED
);
727 /* Not a duplicate */
728 ret
= __ham_del_pair(hashp
, hcp
);
730 out
: if ((t_ret
= __ham_item_done(hashp
, hcp
, ret
== 0)) != 0 && ret
== 0)
734 RELEASE_META(hashp
->dbp
, hashp
);
735 if (F_ISSET(cursor
->dbp
, DB_AM_THREAD
))
736 __db_puthandle(ldbp
);
741 __ham_c_get(cursor
, key
, data
, flags
)
749 HASH_CURSOR
*hcp
, save_curs
;
750 int get_key
, ret
, t_ret
;
752 DEBUG_LREAD(cursor
->dbp
, cursor
->txn
, "ham_c_get",
753 flags
== DB_SET
|| flags
== DB_SET_RANGE
? key
: NULL
,
756 if (F_ISSET(cursor
->dbp
, DB_AM_THREAD
) &&
757 (ret
= __db_gethandle(cursor
->dbp
, __ham_hdup
, &ldbp
)) != 0)
759 hashp
= (HTAB
*)(ldbp
->internal
);
760 hcp
= (HASH_CURSOR
*)cursor
->internal
;
763 __db_cgetchk(hashp
->dbp
, key
, data
, flags
, IS_VALID(hcp
))) != 0)
766 SET_LOCKER(hashp
->dbp
, cursor
->txn
);
767 GET_META(hashp
->dbp
, hashp
);
768 hashp
->hash_accesses
++;
776 if (hcp
->bucket
!= BUCKET_INVALID
) {
777 ret
= __ham_item_prev(hashp
, hcp
, DB_LOCK_READ
);
782 ret
= __ham_item_last(hashp
, hcp
, DB_LOCK_READ
);
785 ret
= __ham_item_first(hashp
, hcp
, DB_LOCK_READ
);
788 if (hcp
->bucket
== BUCKET_INVALID
)
790 ret
= __ham_item_next(hashp
, hcp
, DB_LOCK_READ
);
794 ret
= __ham_lookup(hashp
, hcp
, key
, 0, DB_LOCK_READ
);
798 if (F_ISSET(hcp
, H_DELETED
)) {
803 ret
= __ham_item(hashp
, hcp
, DB_LOCK_READ
);
808 * Must always enter this loop to do error handling and
809 * check for big key/data pair.
812 if (ret
!= 0 && ret
!= DB_NOTFOUND
)
814 else if (F_ISSET(hcp
, H_OK
)) {
816 if (get_key
&& (ret
= __db_ret(hashp
->dbp
, hcp
->pagep
,
817 H_KEYINDEX(hcp
->bndx
), key
, &hcp
->big_key
,
818 &hcp
->big_keylen
)) != 0)
821 ret
= __ham_dup_return(hashp
, hcp
, data
, flags
);
823 } else if (!F_ISSET(hcp
, H_NOMORE
)) {
829 * Ran out of entries in a bucket; change buckets.
834 ret
= __ham_item_done(hashp
, hcp
, 0);
835 if (hcp
->bucket
== 0) {
840 hcp
->bndx
= NDX_INVALID
;
842 ret
= __ham_item_prev(hashp
,
847 ret
= __ham_item_done(hashp
, hcp
, 0);
848 hcp
->bndx
= NDX_INVALID
;
850 hcp
->pgno
= PGNO_INVALID
;
852 if (hcp
->bucket
> hashp
->hdr
->max_bucket
) {
857 ret
= __ham_item_next(hashp
,
867 out1
: if ((t_ret
= __ham_item_done(hashp
, hcp
, 0)) != 0 && ret
== 0)
871 RELEASE_META(hashp
->dbp
, hashp
);
872 if (F_ISSET(cursor
->dbp
, DB_AM_THREAD
))
873 __db_puthandle(ldbp
);
878 __ham_c_put(cursor
, key
, data
, flags
)
886 HASH_CURSOR
*hcp
, save_curs
;
890 DEBUG_LWRITE(cursor
->dbp
, cursor
->txn
, "ham_c_put",
891 flags
== DB_KEYFIRST
|| flags
== DB_KEYLAST
? key
: NULL
,
894 if (F_ISSET(cursor
->dbp
, DB_AM_THREAD
) &&
895 (ret
= __db_gethandle(cursor
->dbp
, __ham_hdup
, &ldbp
)) != 0)
897 hashp
= (HTAB
*)(ldbp
->internal
);
898 hcp
= (HASH_CURSOR
*)cursor
->internal
;
901 if ((ret
= __db_cputchk(hashp
->dbp
, key
, data
, flags
,
902 F_ISSET(ldbp
, DB_AM_RDONLY
), IS_VALID(hcp
))) != 0)
904 if (F_ISSET(hcp
, H_DELETED
))
905 return (DB_NOTFOUND
);
907 SET_LOCKER(hashp
->dbp
, cursor
->txn
);
908 GET_META(hashp
->dbp
, hashp
);
914 nbytes
= (ISBIG(hashp
, key
->size
) ? HOFFPAGE_PSIZE
:
915 HKEYDATA_PSIZE(key
->size
)) +
916 (ISBIG(hashp
, data
->size
) ? HOFFPAGE_PSIZE
:
917 HKEYDATA_PSIZE(data
->size
));
918 ret
= __ham_lookup(hashp
, hcp
, key
, nbytes
, DB_LOCK_WRITE
);
923 ret
= __ham_item(hashp
, hcp
, DB_LOCK_WRITE
);
928 if (flags
== DB_CURRENT
&& !F_ISSET(ldbp
, DB_AM_DUP
))
929 ret
= __ham_overwrite(hashp
, hcp
, data
);
931 ret
= __ham_add_dup(hashp
, hcp
, data
, flags
);
934 if (ret
== 0 && F_ISSET(hcp
, H_EXPAND
)) {
935 ret
= __ham_expand_table(hashp
);
936 F_CLR(hcp
, H_EXPAND
);
939 if ((t_ret
= __ham_item_done(hashp
, hcp
, ret
== 0)) != 0 && ret
== 0)
943 RELEASE_META(hashp
->dbp
, hashp
);
944 if (F_ISSET(cursor
->dbp
, DB_AM_THREAD
))
945 __db_puthandle(ldbp
);
949 /********************************* UTILITIES ************************/
952 * __ham_expand_table --
954 * PUBLIC: int __ham_expand_table __P((HTAB *));
957 __ham_expand_table(hashp
)
960 u_int32_t old_bucket
, new_bucket
;
965 DIRTY_META(hashp
, ret
);
969 if (DB_LOGGING(hashp
->dbp
)) {
972 if ((ret
= __ham_splitmeta_log(hashp
->dbp
->dbenv
->lg_info
,
973 (DB_TXN
*)hashp
->dbp
->txn
, &new_lsn
, 0,
974 hashp
->dbp
->log_fileid
,
975 hashp
->hdr
->max_bucket
, hashp
->hdr
->ovfl_point
,
976 hashp
->hdr
->spares
[hashp
->hdr
->ovfl_point
],
977 &hashp
->hdr
->lsn
)) != 0)
980 hashp
->hdr
->lsn
= new_lsn
;
983 hashp
->hash_expansions
++;
984 new_bucket
= ++hashp
->hdr
->max_bucket
;
985 old_bucket
= (hashp
->hdr
->max_bucket
& hashp
->hdr
->low_mask
);
988 * If the split point is increasing (hdr.max_bucket's log base 2
989 * increases), max sure that we have enough extra pages, then
990 * copy the current contents of the spare split bucket to the
993 spare_ndx
= __db_log2(hashp
->hdr
->max_bucket
+ 1);
994 if (spare_ndx
> hashp
->hdr
->ovfl_point
) {
996 * We are about to shift the split point. Make sure that
997 * if the next doubling is going to be big (more than 8
998 * pages), we have some extra pages around.
1000 if (hashp
->hdr
->spares
[hashp
->hdr
->ovfl_point
] == 0 &&
1002 __ham_init_ovflpages(hashp
);
1004 hashp
->hdr
->spares
[spare_ndx
] =
1005 hashp
->hdr
->spares
[hashp
->hdr
->ovfl_point
];
1006 hashp
->hdr
->ovfl_point
= spare_ndx
;
1009 if (new_bucket
> hashp
->hdr
->high_mask
) {
1010 /* Starting a new doubling */
1011 hashp
->hdr
->low_mask
= hashp
->hdr
->high_mask
;
1012 hashp
->hdr
->high_mask
= new_bucket
| hashp
->hdr
->low_mask
;
1015 if (BUCKET_TO_PAGE(hashp
, new_bucket
) > MAX_PAGES(hashp
)) {
1016 __db_err(hashp
->dbp
->dbenv
,
1017 "hash: Cannot allocate new bucket. Pages exhausted.");
1021 /* Relocate records to the new bucket */
1022 return (__ham_split_page(hashp
, old_bucket
, new_bucket
));
1026 * PUBLIC: u_int32_t __ham_call_hash __P((HTAB *, u_int8_t *, int32_t));
1029 __ham_call_hash(hashp
, k
, len
)
1034 u_int32_t n
, bucket
;
1036 n
= (u_int32_t
)hashp
->hash(k
, len
);
1037 bucket
= n
& hashp
->hdr
->high_mask
;
1038 if (bucket
> hashp
->hdr
->max_bucket
)
1039 bucket
= bucket
& hashp
->hdr
->low_mask
;
1044 * Check for duplicates, and call __db_ret appropriately. Release
1045 * everything held by the cursor.
1048 __ham_dup_return(hashp
, hcp
, val
, flags
)
1056 DBT
*myval
, tmp_val
;
1063 /* Check for duplicate and return the first one. */
1064 ndx
= H_DATAINDEX(hcp
->bndx
);
1065 type
= GET_HKEYDATA(hcp
->pagep
, ndx
)->type
;
1070 * There are 3 cases:
1071 * 1. We are not in duplicate, simply call db_ret.
1072 * 2. We are looking at keys and stumbled onto a duplicate.
1073 * 3. We are in the middle of a duplicate set. (ISDUP set)
1077 * Here we check for the case where we just stumbled onto a
1078 * duplicate. In this case, we do initialization and then
1079 * let the normal duplicate code handle it.
1081 if (!F_ISSET(hcp
, H_ISDUP
))
1082 if (type
== H_DUPLICATE
) {
1083 F_SET(hcp
, H_ISDUP
);
1084 hcp
->dup_tlen
= LEN_HDATA(hcp
->pagep
,
1085 hashp
->hdr
->pagesize
, hcp
->bndx
);
1086 hk
= H_PAIRDATA(hcp
->pagep
, hcp
->bndx
);
1087 if (flags
== DB_LAST
|| flags
== DB_PREV
) {
1091 memcpy(&len
, hk
->data
+ hcp
->dup_off
,
1093 hcp
->dup_off
+= DUP_SIZE(len
);
1095 } while (hcp
->dup_off
< hcp
->dup_tlen
);
1096 hcp
->dup_off
-= DUP_SIZE(len
);
1099 memcpy(&len
, hk
->data
, sizeof(db_indx_t
));
1104 } else if (type
== H_OFFDUP
) {
1105 F_SET(hcp
, H_ISDUP
);
1107 P_ENTRY(hcp
->pagep
, ndx
) + SSZ(HOFFDUP
, pgno
),
1109 if (flags
== DB_LAST
|| flags
== DB_PREV
) {
1110 indx
= (int)hcp
->dndx
;
1111 if ((ret
= __db_dend(hashp
->dbp
,
1112 pgno
, &hcp
->dpagep
)) != 0)
1114 hcp
->dpgno
= PGNO(hcp
->dpagep
);
1115 hcp
->dndx
= NUM_ENT(hcp
->dpagep
) - 1;
1116 } else if ((ret
= __ham_next_cpage(hashp
,
1117 hcp
, pgno
, 0, H_ISDUP
)) != 0)
1123 * Now, everything is initialized, grab a duplicate if
1126 if (F_ISSET(hcp
, H_ISDUP
))
1127 if (hcp
->dpgno
!= PGNO_INVALID
) {
1132 * Copy the DBT in case we are retrieving into
1133 * user memory and we need the parameters for
1136 memcpy(&tmp_val
, val
, sizeof(*val
));
1137 F_SET(&tmp_val
, DB_DBT_PARTIAL
);
1138 tmp_val
.dlen
= hcp
->dup_len
;
1139 tmp_val
.doff
= hcp
->dup_off
+ sizeof(db_indx_t
);
1145 * Finally, if we had a duplicate, pp, ndx, and myval should be
1146 * set appropriately.
1148 if ((ret
= __db_ret(hashp
->dbp
, pp
, ndx
, myval
, &hcp
->big_data
,
1149 &hcp
->big_datalen
)) != 0)
1153 * In case we sent a temporary off to db_ret, set the real
1156 val
->data
= myval
->data
;
1157 val
->size
= myval
->size
;
1163 __ham_overwrite(hashp
, hcp
, nval
)
1168 DBT
*myval
, tmp_val
;
1171 if (F_ISSET(hashp
->dbp
, DB_AM_DUP
))
1172 return (__ham_add_dup(hashp
, hcp
, nval
, DB_KEYLAST
));
1173 else if (!F_ISSET(nval
, DB_DBT_PARTIAL
)) {
1175 memcpy(&tmp_val
, nval
, sizeof(*nval
));
1176 F_SET(&tmp_val
, DB_DBT_PARTIAL
);
1178 hk
= H_PAIRDATA(hcp
->pagep
, hcp
->bndx
);
1179 if (hk
->type
== H_OFFPAGE
)
1180 memcpy(&tmp_val
.dlen
,
1181 (u_int8_t
*)hk
+ SSZ(HOFFPAGE
, tlen
),
1184 tmp_val
.dlen
= LEN_HDATA(hcp
->pagep
,
1185 hashp
->hdr
->pagesize
,hcp
->bndx
);
1187 } else /* Regular partial put */
1190 return (__ham_replpair(hashp
, hcp
, myval
, 0));
1194 * Given a key and a cursor, sets the cursor to the page/ndx on which
1195 * the key resides. If the key is found, the cursor H_OK flag is set
1196 * and the pagep, bndx, pgno (dpagep, dndx, dpgno) fields are set.
1197 * If the key is not found, the H_OK flag is not set. If the sought
1198 * field is non-0, the pagep, bndx, pgno (dpagep, dndx, dpgno) fields
1199 * are set indicating where an add might take place. If it is 0,
1200 * non of the cursor pointer field are valid.
1203 __ham_lookup(hashp
, hcp
, key
, sought
, mode
)
1213 int match
, ret
, t_ret
;
1216 * Set up cursor so that we're looking for space to add an item
1217 * as we cycle through the pages looking for the key.
1219 if ((ret
= __ham_item_reset(hashp
, hcp
)) != 0)
1221 hcp
->seek_size
= sought
;
1223 hcp
->bucket
= __ham_call_hash(hashp
, (u_int8_t
*)key
->data
, key
->size
);
1225 if ((ret
= __ham_item_next(hashp
, hcp
, mode
)) != 0)
1228 if (F_ISSET(hcp
, H_NOMORE
))
1231 hk
= H_PAIRKEY(hcp
->pagep
, hcp
->bndx
);
1234 memcpy(&tlen
, (u_int8_t
*)hk
+ SSZ(HOFFPAGE
, tlen
),
1236 if (tlen
== key
->size
) {
1238 (u_int8_t
*)hk
+ SSZ(HOFFPAGE
, pgno
),
1240 match
= __db_moff(hashp
->dbp
, key
, pgno
);
1248 if (key
->size
== LEN_HKEY(hcp
->pagep
,
1249 hashp
->hdr
->pagesize
, hcp
->bndx
) &&
1250 memcmp(key
->data
, hk
->data
, key
->size
) == 0) {
1258 * These are errors because keys are never
1259 * duplicated, only data items are.
1261 return (__db_pgfmt(hashp
->dbp
, PGNO(hcp
->pagep
)));
1263 hashp
->hash_collisions
++;
1267 * Item was not found, adjust cursor properly.
1273 if ((t_ret
= __ham_item_done(hashp
, hcp
, 0)) != 0 && ret
== 0)
1279 * Initialize a dbt using some possibly already allocated storage
1281 * PUBLIC: int __ham_init_dbt __P((DBT *, u_int32_t, void **, u_int32_t *));
1284 __ham_init_dbt(dbt
, size
, bufp
, sizep
)
1290 memset(dbt
, 0, sizeof(*dbt
));
1291 if (*sizep
< size
) {
1292 if ((*bufp
= (void *)(*bufp
== NULL
?
1293 malloc(size
) : realloc(*bufp
, size
))) == NULL
) {
1305 * Adjust the cursor after an insert or delete. The cursor passed is
1306 * the one that was operated upon; we just need to check any of the
1309 * len indicates the length of the item added/deleted
1310 * add indicates if the item indicated by the cursor has just been
1311 * added (add == 1) or deleted (add == 0).
1312 * dup indicates if the addition occurred into a duplicate set.
1314 * PUBLIC: void __ham_c_update __P((HTAB *,
1315 * PUBLIC: HASH_CURSOR *, db_pgno_t, u_int32_t, int, int));
1318 __ham_c_update(hashp
, hcp
, chg_pgno
, len
, add
, dup
)
1332 * Regular adds are always at the end of a given page,
1333 * so we never have to adjust anyone's cursor after
1339 page_deleted
= chg_pgno
!= PGNO_INVALID
&&
1340 ((!dup
&& chg_pgno
!= hcp
->pgno
) ||
1341 (dup
&& chg_pgno
!= hcp
->dpgno
));
1343 hp
= hcp
->db_cursor
->dbp
->master
->internal
;
1344 DB_THREAD_LOCK(hp
->dbp
);
1346 for (cp
= TAILQ_FIRST(&hp
->dbp
->curs_queue
); cp
!= NULL
;
1347 cp
= TAILQ_NEXT(cp
, links
)) {
1348 if (cp
->internal
== hcp
)
1351 lcp
= (HASH_CURSOR
*)cp
->internal
;
1353 if (!dup
&& lcp
->pgno
!= chg_pgno
)
1356 if (dup
&& F_ISSET(hcp
, H_DELETED
) && lcp
->pgno
!= chg_pgno
)
1359 if (dup
&& !F_ISSET(hcp
, H_DELETED
) && lcp
->dpgno
!= chg_pgno
)
1364 lcp
->dpgno
= hcp
->dpgno
;
1365 lcp
->dndx
= hcp
->dndx
;
1367 lcp
->pgno
= hcp
->pgno
;
1368 lcp
->bndx
= hcp
->bndx
;
1369 lcp
->bucket
= hcp
->bucket
;
1371 F_CLR(lcp
, H_ISDUP
);
1375 if (!dup
&& lcp
->bndx
> hcp
->bndx
)
1377 else if (!dup
&& lcp
->bndx
== hcp
->bndx
)
1378 F_SET(lcp
, H_DELETED
);
1379 else if (dup
&& lcp
->bndx
== hcp
->bndx
) {
1380 /* Assign dpgno in case there was page conversion. */
1381 lcp
->dpgno
= hcp
->dpgno
;
1382 if (add
&& lcp
->dndx
>= hcp
->dndx
)
1384 else if (!add
&& lcp
->dndx
> hcp
->dndx
)
1386 else if (!add
&& lcp
->dndx
== hcp
->dndx
)
1387 F_SET(lcp
, H_DELETED
);
1389 /* Now adjust on-page information. */
1390 if (lcp
->dpgno
== PGNO_INVALID
)
1392 lcp
->dup_tlen
+= len
;
1393 if (lcp
->dndx
> hcp
->dndx
)
1394 lcp
->dup_off
+= len
;
1396 lcp
->dup_tlen
-= len
;
1397 if (lcp
->dndx
> hcp
->dndx
)
1398 lcp
->dup_off
-= len
;
1402 DB_THREAD_UNLOCK(hp
->dbp
);
1407 * This function gets called when we create a duplicate handle for a
1408 * threaded DB. It should create the private part of the DB structure.
1409 * PUBLIC: int __ham_hdup __P((DB *, DB *));
1412 __ham_hdup(orig
, new)
1419 if ((hashp
= (HTAB
*)malloc(sizeof(HTAB
))) == NULL
)
1422 new->internal
= hashp
;
1427 hashp
->hash
= ((HTAB
*)orig
->internal
)->hash
;
1428 if ((hashp
->split_buf
= (PAGE
*)malloc(orig
->pgsize
)) == NULL
)
1430 hashp
->local_errno
= 0;
1431 hashp
->hash_accesses
= 0;
1432 hashp
->hash_collisions
= 0;
1433 hashp
->hash_expansions
= 0;
1434 hashp
->hash_overflows
= 0;
1435 hashp
->hash_bigpages
= 0;
1436 /* Initialize the cursor queue. */
1437 ret
= __ham_c_init(new, NULL
, &curs
);
1438 TAILQ_INSERT_TAIL(&new->curs_queue
, curs
, links
);
This page took 0.106562 seconds and 5 git commands to generate.