diff options
| author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2015-02-17 17:25:57 +0000 |
|---|---|---|
| committer | <> | 2015-03-17 16:26:24 +0000 |
| commit | 780b92ada9afcf1d58085a83a0b9e6bc982203d1 (patch) | |
| tree | 598f8b9fa431b228d29897e798de4ac0c1d3d970 /src/heap/heap.c | |
| parent | 7a2660ba9cc2dc03a69ddfcfd95369395cc87444 (diff) | |
| download | berkeleydb-master.tar.gz | |
Diffstat (limited to 'src/heap/heap.c')
| -rw-r--r-- | src/heap/heap.c | 592 |
1 files changed, 355 insertions, 237 deletions
diff --git a/src/heap/heap.c b/src/heap/heap.c index ab404658..7aec416b 100644 --- a/src/heap/heap.c +++ b/src/heap/heap.c @@ -1,7 +1,7 @@ /*- * See the file LICENSE for redistribution information. * - * Copyright (c) 2010, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2015 Oracle and/or its affiliates. All rights reserved. * * $Id$ */ @@ -24,6 +24,8 @@ static int __heapc_get __P((DBC *, DBT *, DBT *, u_int32_t, db_pgno_t *)); static int __heapc_put __P((DBC *, DBT *, DBT *, u_int32_t, db_pgno_t *)); static int __heapc_reloc __P((DBC *, DBT *, DBT *)); static int __heapc_reloc_partial __P((DBC *, DBT *, DBT *)); +static void __heapc_search __P((DBC *, HEAPPG *, db_indx_t, + int, db_indx_t *, int *)); static int __heapc_split __P((DBC *, DBT *, DBT *, int)); /* @@ -134,12 +136,15 @@ __heap_bulk(dbc, data, flags) DB_HEAP_RID prev_rid, rid; DBT sdata; HEAP_CURSOR *cp; + HEAPBLOBHDR bhdr; HEAPHDR *hdr; HEAPSPLITHDR *shdr; PAGE *pg; db_lockmode_t lock_type; int is_key, ret; int32_t *offp; + off_t blob_size; + db_seq_t blob_id; u_int32_t data_size, key_size, needed, space; u_int8_t *dbuf, *np; @@ -183,6 +188,7 @@ __heap_bulk(dbc, data, flags) next_pg: rid.indx = cp->indx; rid.pgno = cp->pgno; + prev_rid = rid; pg = cp->page; /* @@ -213,6 +219,14 @@ next_pg: if (F_ISSET(hdr, HEAP_RECSPLIT)) { shdr = (HEAPSPLITHDR *)hdr; data_size = DB_ALIGN(shdr->tsize, sizeof(u_int32_t)); + } else if (F_ISSET(hdr, HEAP_RECBLOB)) { + memcpy(&bhdr, hdr, HEAPBLOBREC_SIZE); + GET_BLOB_SIZE(dbp->env, bhdr, blob_size, ret); + if (ret != 0) + return (ret); + if (blob_size > UINT32_MAX) + return (DB_BUFFER_SMALL); + data_size = (u_int32_t)blob_size; } else data_size = DB_ALIGN(hdr->size, sizeof(u_int32_t)); needed += 2 * sizeof(*offp) + data_size; @@ -250,13 +264,21 @@ next_pg: if ((ret = __heapc_gsplit( dbc, &sdata, NULL, NULL)) != 0) return (ret); - } else { + } else if (F_ISSET(hdr, HEAP_RECBLOB)) { + memcpy(&bhdr, hdr, HEAPBLOBREC_SIZE); + blob_id = (db_seq_t)bhdr.id; + if ((ret = __blob_bulk( + dbc, data_size, blob_id, np)) != 0) + return (ret); + }else { memcpy(np, (u_int8_t *)hdr + sizeof(HEAPHDR), hdr->size); } *offp-- = (int32_t)(np - dbuf); if (F_ISSET(hdr, HEAP_RECSPLIT)) *offp-- = (int32_t)shdr->tsize; + else if (F_ISSET(hdr, HEAP_RECBLOB)) + *offp-- = (int32_t)data_size; else *offp-- = (int32_t)hdr->size; np += data_size; @@ -296,7 +318,6 @@ __heapc_close(dbc, root_pgno, rmroot) db_pgno_t root_pgno; int *rmroot; { - DB_MPOOLFILE *mpf; HEAP_CURSOR *cp; int ret; @@ -304,7 +325,6 @@ __heapc_close(dbc, root_pgno, rmroot) COMPQUIET(rmroot, 0); cp = (HEAP_CURSOR *)dbc->internal; - mpf = dbc->dbp->mpf; ret = 0; /* Release the page/lock held by the cursor. */ @@ -325,11 +345,14 @@ __heapc_del(dbc, flags) DB_MPOOLFILE *mpf; DBT hdr_dbt, log_dbt; HEAP *h; + HEAPBLOBHDR bhdr; HEAPHDR *hdr; HEAPPG *rpage; HEAP_CURSOR *cp; db_pgno_t region_pgno; - int oldspacebits, ret, spacebits, t_ret; + int ret, t_ret; + db_seq_t blob_id; + u_int32_t oldspacebits, spacebits; u_int16_t data_size, size; dbp = dbc->dbp; @@ -337,6 +360,7 @@ __heapc_del(dbc, flags) h = dbp->heap_internal; cp = (HEAP_CURSOR *)dbc->internal; rpage = NULL; + ret = 0; COMPQUIET(flags, 0); /* @@ -377,6 +401,14 @@ start: if (STD_LOCKING(dbc) && (ret = __db_lget(dbc, next_rid.indx = 0; } + /* Delete the blob file. */ + if (F_ISSET(hdr, HEAP_RECBLOB)) { + memcpy(&bhdr, hdr, HEAPBLOBREC_SIZE); + blob_id = (db_seq_t)bhdr.id; + if ((ret = __blob_del(dbc, blob_id)) != 0) + return (ret); + } + /* Log the deletion. */ if (DBC_LOGGING(dbc)) { hdr_dbt.data = hdr; @@ -384,8 +416,9 @@ start: if (STD_LOCKING(dbc) && (ret = __db_lget(dbc, log_dbt.data = (u_int8_t *)hdr + hdr_dbt.size; log_dbt.size = data_size; if ((ret = __heap_addrem_log(dbp, dbc->txn, &LSN(cp->page), - 0, DB_REM_HEAP, cp->pgno, (u_int32_t)cp->indx, - size, &hdr_dbt, &log_dbt, &LSN(cp->page))) != 0) + 0, OP_SET(DB_REM_HEAP, cp->page), + cp->pgno, (u_int32_t)cp->indx, size, + &hdr_dbt, &log_dbt, &LSN(cp->page))) != 0) goto err; } else LSN_NOT_LOGGED(LSN(cp->page)); @@ -414,7 +447,7 @@ start: if (STD_LOCKING(dbc) && (ret = __db_lget(dbc, dbc->thread_info, NULL, DB_MPOOL_DIRTY, &rpage)) != 0) goto err; HEAP_SETSPACE(dbp, rpage, - cp->pgno - region_pgno - 1, spacebits); + (cp->pgno - region_pgno) - 1, spacebits); } err: DB_ASSERT(dbp->env, ret != DB_PAGE_NOTFOUND); @@ -443,7 +476,8 @@ err: DB_ASSERT(dbp->env, ret != DB_PAGE_NOTFOUND); /* * __heap_ditem -- - * Remove an item from a page. + * Remove an item from a page. Note when deleting blob records that the file + * has to be deleted separate from calling this function. * * PUBLIC: int __heap_ditem * PUBLIC: __P((DBC *, PAGE *, u_int32_t, u_int32_t)); @@ -537,19 +571,21 @@ __heapc_get(dbc, key, data, flags, pgnop) DB_MPOOLFILE *mpf; DB_LOCK meta_lock; DBT tmp_val; - HEAP *h; + HEAPBLOBHDR bhdr; HEAPHDR *hdr; HEAPMETA *meta; HEAPPG *dpage; HEAP_CURSOR *cp; db_lockmode_t lock_type; db_pgno_t pgno; - int cmp, f_indx, found, getpage, indx, ret; + int cmp, np_inc, f_indx, found, getpage, indx, ret; + off_t blob_size; + db_seq_t blob_id; dbp = dbc->dbp; mpf = dbp->mpf; - h = dbp->heap_internal; cp = (HEAP_CURSOR *)dbc->internal; + pgno = PGNO_INVALID; LOCK_INIT(meta_lock); COMPQUIET(pgnop, NULL); @@ -564,7 +600,7 @@ __heapc_get(dbc, key, data, flags, pgnop) else lock_type = DB_LOCK_READ; - ret = 0; + np_inc = ret = 0; found = getpage = FALSE; meta = NULL; dpage = NULL; @@ -579,7 +615,7 @@ __heapc_get(dbc, key, data, flags, pgnop) ACQUIRE_CUR(dbc, lock_type, cp->pgno, 0, 0, ret); if (ret != 0) { if (ret == DB_PAGE_NOTFOUND) - ret = DB_NOTFOUND; + ret = DBC_ERR(dbc, DB_NOTFOUND); goto err; } @@ -591,7 +627,7 @@ __heapc_get(dbc, key, data, flags, pgnop) hdr = (HEAPHDR *)P_ENTRY(dbp, dpage, cp->indx); if (F_ISSET(hdr, HEAP_RECSPLIT) && !F_ISSET(hdr, HEAP_RECFIRST)) { - ret = DB_NOTFOUND; + ret = DBC_ERR(dbc, DB_NOTFOUND); goto err; } @@ -610,7 +646,7 @@ first: pgno = FIRST_HEAP_DPAGE; ACQUIRE_CUR(dbc, lock_type, pgno, 0, 0, ret); if (ret != 0 ) { if (ret == DB_PAGE_NOTFOUND) - ret = DB_NOTFOUND; + ret = DBC_ERR(dbc, DB_NOTFOUND); goto err; } dpage = (HEAPPG *)cp->page; @@ -620,25 +656,10 @@ first: pgno = FIRST_HEAP_DPAGE; * finding first non-split record or first piece of a * split record, then set up cursor. */ - if (TYPE(dpage) == P_HEAP && NUM_ENT(dpage) != 0) { - for (indx = 0; - indx <= HEAP_HIGHINDX(dpage); indx++) { - if (HEAP_OFFSETTBL( - dbp, dpage)[indx] == 0) - continue; - hdr = (HEAPHDR *)P_ENTRY( - dbp, dpage, indx); - if (!F_ISSET(hdr, HEAP_RECSPLIT) || - F_ISSET(hdr, HEAP_RECFIRST)) { - found = TRUE; - cp->pgno = pgno; - cp->indx = indx; - break; - } - } - if (!found) - pgno++; - } else + __heapc_search(dbc, dpage, 0, 1, &cp->indx, &found); + if (found) + cp->pgno = pgno; + else pgno++; } break; @@ -668,7 +689,7 @@ last: pgno = PGNO_BASE_MD; while (!found) { /* Don't look earlier than the first data page. */ if (pgno < FIRST_HEAP_DPAGE) { - ret = DB_NOTFOUND; + ret = DBC_ERR(dbc, DB_NOTFOUND); goto err; } @@ -683,33 +704,33 @@ last: pgno = PGNO_BASE_MD; * non-split record or the first piece of a split record * is found. */ - if (TYPE(dpage) == P_HEAP && NUM_ENT(dpage) != 0) { - for (indx = HEAP_HIGHINDX(dpage); - indx >= 0; indx--) { - if (HEAP_OFFSETTBL( - dbp, dpage)[indx] == 0) - continue; - hdr = (HEAPHDR *)P_ENTRY( - dbp, dpage, indx); - if (!F_ISSET(hdr, HEAP_RECSPLIT) || - F_ISSET(hdr, HEAP_RECFIRST)) { - found = TRUE; - cp->pgno = pgno; - cp->indx = indx; - break; - } - } - if (!found) - pgno--; - } else + __heapc_search(dbc, + dpage, HEAP_HIGHINDX(dpage), 1, &cp->indx, &found); + if (found) + cp->pgno = pgno; + else pgno--; } break; case DB_NEXT_NODUP: case DB_NEXT: - /* If cursor not initialize, behave as DB_FIRST */ - if (dbc->internal->pgno == PGNO_INVALID) - goto first; + case DB_PREV_NODUP: + case DB_PREV: + /* + * np_inc stores whether to increment or decrement when + * iterating through records on a page and pages in the file. + */ + if (flags == DB_NEXT_NODUP || flags == DB_NEXT) + np_inc = 1; + else + np_inc = -1; + /* If cursor not initialized, behave as DB_FIRST/DB_LAST */ + if (dbc->internal->pgno == PGNO_INVALID) { + if (np_inc == 1) + goto first; + else + goto last; + } /* * Acquire the current page with the lock we have already, @@ -720,108 +741,49 @@ last: pgno = PGNO_BASE_MD; goto err; dpage = (HEAPPG *)cp->page; - /* At end of current page, must get next page */ - if (cp->indx >= HEAP_HIGHINDX(dpage)) + if (np_inc == 1 && cp->indx >= HEAP_HIGHINDX(dpage)) + /* At end of current page, must get next page. */ getpage = TRUE; - - while (!found) { - if (getpage) { - pgno = cp->pgno + 1; - - /* Put current page/lock and get next one */ - ACQUIRE_CUR(dbc, lock_type, pgno, 0, 0, ret); - if (ret != 0) { - /* Beyond last page? */ - if (ret == DB_PAGE_NOTFOUND) - ret = DB_NOTFOUND; - goto err; - } - dpage = (HEAPPG *)cp->page; - - /* - * If page is a spam page or its a data - * page without entries, try again. - */ - if (TYPE(dpage) != P_HEAP || - (TYPE(dpage) == P_HEAP && - NUM_ENT(dpage) == 0)) - continue; - - /* When searching, indx gets bumped to 0 */ - cp->indx = -1; - getpage = FALSE; - } - + else if (np_inc == -1) { /* - * Bump index and loop through the offset table finding - * first nonzero entry. If the offset is for a split - * record, make sure it's the first piece of the split - * record. HEAP_HIGHINDX always points to highest filled - * entry on page. + * Loop through indexes and find first used slot. Check + * if already at the first slot. */ - cp->indx++; - for (indx=cp->indx; - indx <= HEAP_HIGHINDX(dpage); indx++) { - if (HEAP_OFFSETTBL(dbp, dpage)[indx] == 0) - continue; - hdr = (HEAPHDR *)P_ENTRY(dbp, dpage, indx); - if (!F_ISSET(hdr, HEAP_RECSPLIT) || - F_ISSET(hdr, HEAP_RECFIRST)) { - found = TRUE; - cp->indx = indx; - break; - } + for (f_indx=0; (f_indx <= HEAP_HIGHINDX(dpage)) && + (HEAP_OFFSETTBL(dbp, dpage)[f_indx] == 0); f_indx++) + { + /* No-op. */ } - /* Nothing of interest on page, so try next */ - if (!found) + /* At the beginning of current page, get new page */ + if (cp->indx == 0 || cp->indx <= f_indx) { + if (cp->pgno == FIRST_HEAP_DPAGE) { + ret = DBC_ERR(dbc, DB_NOTFOUND); + goto err; + } getpage = TRUE; - } - break; - case DB_PREV_NODUP: - case DB_PREV: - /* If cursor not initialize, behave as DB_LAST */ - if (dbc->internal->pgno == PGNO_INVALID) - goto last; - - /* - * Acquire the current page with the lock we have already, - * unless user has asked for a write lock. - */ - ACQUIRE_CUR(dbc, lock_type, cp->pgno, 0, 0, ret); - if (ret != 0) - goto err; - dpage = (HEAPPG *)cp->page; - - /* - * Loop through indexes and find first used slot. Check if - * already at the first slot. - */ - for (f_indx=0; (f_indx <= HEAP_HIGHINDX(dpage)) && - (HEAP_OFFSETTBL(dbp, dpage)[f_indx] == 0); f_indx++) ; - - /* At the beginning of current page, must get new page */ - if (cp->indx == 0 || cp->indx <= f_indx) { - if (cp->pgno == FIRST_HEAP_DPAGE) { - ret = DB_NOTFOUND; - goto err; } - getpage = TRUE; } while (!found) { if (getpage) { - pgno = cp->pgno - 1; - /* Do not go past first page */ + if (np_inc == -1) + pgno = cp->pgno - 1; + else if (np_inc == 1) + pgno = cp->pgno + 1; if (pgno < FIRST_HEAP_DPAGE) { - ret = DB_NOTFOUND; + ret = DBC_ERR(dbc, DB_NOTFOUND); goto err; } - /* Put current page/lock and get prev page. */ + /* Put current page/lock and get next one */ ACQUIRE_CUR(dbc, lock_type, pgno, 0, 0, ret); - if (ret != 0) + if (ret != 0) { + if (np_inc == 1 && + ret == DB_PAGE_NOTFOUND) + /* Beyond last page */ + ret = DBC_ERR(dbc, DB_NOTFOUND); goto err; - + } dpage = (HEAPPG *)cp->page; /* @@ -833,31 +795,36 @@ last: pgno = PGNO_BASE_MD; NUM_ENT(dpage) == 0)) continue; - /* When search, this gets bumped to high indx */ - cp->indx = HEAP_HIGHINDX(dpage) + 1; + if (np_inc == 1) + /* + * When searching, indx gets + * bumped to 0 + */ + cp->indx = UINT16_MAX; + else + /* + * When searching, indx gets bumped to + * high indx + */ + cp->indx = HEAP_HIGHINDX(dpage) + 1; getpage = FALSE; } /* - * Decrement index and loop through the offset table - * finding previous nonzero entry. + * Bump index and loop through the offset table finding + * first nonzero entry. If the offset is for a split + * record, make sure it's the first piece of the split + * record. HEAP_HIGHINDX always points to highest filled + * entry on page. */ - cp->indx--; - for (indx=cp->indx; - indx >= 0; indx--) { - if (HEAP_OFFSETTBL(dbp, dpage)[indx] == 0) - continue; - hdr = (HEAPHDR *)P_ENTRY(dbp, dpage, indx); - if (!F_ISSET(hdr, HEAP_RECSPLIT) || - F_ISSET(hdr, HEAP_RECFIRST)) { - found = TRUE; - cp->indx = indx; - break; - } - } - - /* Nothing of interest on page, so try previous */ + if (np_inc == -1) + cp->indx--; + else if (np_inc == 1) + cp->indx++; + __heapc_search(dbc, + dpage, cp->indx, np_inc, &cp->indx, &found); if (!found) + /* Nothing of interest on page, so try next */ getpage = TRUE; } break; @@ -871,7 +838,7 @@ last: pgno = PGNO_BASE_MD; /* First make sure we're trying to get a data page. */ if (pgno == PGNO_BASE_MD || pgno == HEAP_REGION_PGNO(dbp, pgno)) { - ret = DB_NOTFOUND; + ret = DBC_ERR(dbc, DB_NOTFOUND); goto err; } @@ -880,7 +847,7 @@ last: pgno = PGNO_BASE_MD; if (ret != 0) { if (ret == DB_PAGE_NOTFOUND) - ret = DB_NOTFOUND; + ret = DBC_ERR(dbc, DB_NOTFOUND); goto err; } dpage = (HEAPPG *)cp->page; @@ -889,14 +856,14 @@ last: pgno = PGNO_BASE_MD; if ((indx > HEAP_HIGHINDX(dpage)) || (HEAP_OFFSETTBL(dbp, dpage)[indx] == 0)) { DISCARD(dbc, cp->page, cp->lock, 0, ret); - ret = DB_NOTFOUND; + ret = DBC_ERR(dbc, DB_NOTFOUND); goto err; } hdr = (HEAPHDR *)P_ENTRY(dbp, dpage, indx); if (F_ISSET(hdr, HEAP_RECSPLIT) && !F_ISSET(hdr, HEAP_RECFIRST)) { DISCARD(dbc, cp->page, cp->lock, 0, ret); - ret = DB_NOTFOUND; + ret = DBC_ERR(dbc, DB_NOTFOUND); goto err; } @@ -911,16 +878,30 @@ last: pgno = PGNO_BASE_MD; if ((ret = __heapc_gsplit( dbc, &tmp_val, NULL, 0)) != 0) goto err; + } else if (F_ISSET(hdr, HEAP_RECBLOB)) { + memcpy(&bhdr, hdr, HEAPBLOBREC_SIZE); + blob_id = (db_seq_t)bhdr.id; + GET_BLOB_SIZE(dbc->env, bhdr, blob_size, ret); + if (ret != 0) + goto err; + if (blob_size > UINT32_MAX) { + ret = DB_BUFFER_SMALL; + goto err; + } + tmp_val.flags = DB_DBT_MALLOC; + if ((ret = __blob_get(dbc, &tmp_val, + blob_id, blob_size, NULL, 0)) != 0) + goto err; } else { tmp_val.data = (void *)((u_int8_t *)hdr + sizeof(HEAPHDR)); tmp_val.size = hdr->size; } - cmp = __bam_defcmp(dbp, &tmp_val, data); + cmp = __bam_defcmp(dbp, &tmp_val, data, NULL); if (F_ISSET(&tmp_val, DB_DBT_MALLOC)) __os_ufree(dbp->env, tmp_val.data); if (cmp != 0) { - ret = DB_NOTFOUND; + ret = DBC_ERR(dbc, DB_NOTFOUND); goto err; } } @@ -928,7 +909,7 @@ last: pgno = PGNO_BASE_MD; break; case DB_NEXT_DUP: case DB_PREV_DUP: - ret = DB_NOTFOUND; + ret = DBC_ERR(dbc, DB_NOTFOUND); goto err; default: /* DB_GET_RECNO, DB_JOIN_ITEM, DB_SET_RECNO are invalid */ @@ -959,6 +940,53 @@ err: if (ret == 0 ) { return (ret); } +/* + * __heapc_search -- + * Search a given a heap page, starting at a given index, for a viable heap + * record. Return the index of the found record in indxp. + */ +static void +__heapc_search(dbc, dpage, begin, dir, indxp, found) + DBC *dbc; + HEAPPG *dpage; + db_indx_t begin; + int dir; + db_indx_t *indxp; + int *found; +{ + DB *dbp; + HEAPHDR *hdr; + db_indx_t indx; + + dbp = dbc->dbp; + DB_ASSERT(dbp->env, dir == -1 || dir == 1); + + *found = FALSE; + if (TYPE(dpage) != P_HEAP || NUM_ENT(dpage) == 0) + return; + + indx = begin; + for (;;) { + if (HEAP_OFFSETTBL(dbp, dpage)[indx] != 0) { + hdr = (HEAPHDR *)P_ENTRY(dbp, dpage, indx); + if (!F_ISSET(hdr, HEAP_RECSPLIT) || + F_ISSET(hdr, HEAP_RECFIRST)) { + *found = TRUE; + *indxp = indx; + break; + } + } + if ((dir == -1 && indx == 0) || + (dir == 1 && indx == HEAP_HIGHINDX(dpage))) + break; + + if (dir == -1) + indx--; + else + indx++; + } +} + #undef IS_FIRST #define IS_FIRST (last_rid.pgno == PGNO_INVALID) /* @@ -993,6 +1021,7 @@ __heapc_reloc_partial(dbc, key, data) /* We only work on partial puts. */ DB_ASSERT(dbp->env, F_ISSET(data, DB_DBT_PARTIAL)); + DB_ASSERT(dbp->env, !F_ISSET(old_hdr, HEAP_RECBLOB)); /* * Start by calculating the data_size, total size of the new record, and @@ -1014,7 +1043,7 @@ __heapc_reloc_partial(dbc, key, data) dlen = old_size - doff; else dlen = data->dlen; - data_size = old_size - dlen + data->size; + data_size = (old_size - dlen) + data->size; } /* @@ -1075,8 +1104,8 @@ __heapc_reloc_partial(dbc, key, data) */ data_size = doff + (add_bytes ? data->size : 0); else - data_size = old_hdr->size - - dlen + (add_bytes ? data->size : 0); + data_size = (old_hdr->size - + dlen) + (add_bytes ? data->size : 0); data_size += remaining; if (data_size > buflen) { @@ -1120,7 +1149,7 @@ __heapc_reloc_partial(dbc, key, data) if (doff + dlen < old_hdr->size) { olddata += dlen; memcpy(buf, - olddata, old_hdr->size - doff - dlen); + olddata, (old_hdr->size - doff) - dlen); dlen = 0; } else /* @@ -1145,8 +1174,8 @@ __heapc_reloc_partial(dbc, key, data) log_dbt.size = DB_ALIGN( old_hdr->size, sizeof(u_int32_t)); if ((ret = __heap_addrem_log(dbp, dbc->txn, - &LSN(cp->page), 0, DB_REM_HEAP, cp->pgno, - (u_int32_t)cp->indx, old_size, + &LSN(cp->page), 0, OP_SET(DB_REM_HEAP, cp->page), + cp->pgno, (u_int32_t)cp->indx, old_size, &hdr_dbt, &log_dbt, &LSN(cp->page))) != 0) goto err; } else @@ -1185,7 +1214,8 @@ __heapc_reloc_partial(dbc, key, data) log_dbt.size = DB_ALIGN( old_hdr->size, sizeof(u_int32_t)); if ((ret = __heap_addrem_log(dbp, dbc->txn, - &LSN(cp->page), 0, DB_REM_HEAP, cp->pgno, + &LSN(cp->page), 0, + OP_SET(DB_REM_HEAP, cp->page), cp->pgno, (u_int32_t)cp->indx, old_size, &hdr_dbt, &log_dbt, &LSN(cp->page))) != 0) goto err; @@ -1197,7 +1227,8 @@ __heapc_reloc_partial(dbc, key, data) if (DBC_LOGGING(dbc)) { if ((ret = __heap_addrem_log(dbp, dbc->txn, - &LSN(cp->page), 0, DB_ADD_HEAP, cp->pgno, + &LSN(cp->page), 0, + OP_SET(DB_ADD_HEAP, cp->page), cp->pgno, (u_int32_t)cp->indx, old_size, &hdr_dbt, &log_dbt, &LSN(cp->page))) != 0) goto err; @@ -1231,7 +1262,7 @@ __heapc_reloc_partial(dbc, key, data) size -= sizeof(db_indx_t); /* Round down to a multiple of 4. */ size = DB_ALIGN( - size - sizeof(u_int32_t) + 1, sizeof(u_int32_t)); + (size - sizeof(u_int32_t)) + 1, sizeof(u_int32_t)); DB_ASSERT(dbp->env, size >= sizeof(HEAPSPLITHDR)); /* @@ -1261,7 +1292,8 @@ __heapc_reloc_partial(dbc, key, data) if (DBC_LOGGING(dbc)) { if ((ret = __heap_addrem_log(dbp, dbc->txn, &LSN(cp->page), 0, - DB_ADD_HEAP, cp->pgno, (u_int32_t)cp->indx, + OP_SET(DB_ADD_HEAP, cp->page), cp->pgno, + (u_int32_t)cp->indx, size, &hdr_dbt, &t_data, &LSN(cp->page))) != 0) goto err; } else @@ -1343,7 +1375,8 @@ next_pg: last_rid.pgno = cp->pgno; log_dbt.size = DB_ALIGN( old_hdr->size, sizeof(u_int32_t)); if ((ret = __heap_addrem_log(dbp, dbc->txn, - &LSN(cp->page), 0, DB_REM_HEAP, cp->pgno, + &LSN(cp->page), 0, + OP_SET(DB_REM_HEAP, cp->page), cp->pgno, (u_int32_t)cp->indx, old_size, &hdr_dbt, &log_dbt, &LSN(cp->page))) != 0) goto err; @@ -1355,7 +1388,8 @@ next_pg: last_rid.pgno = cp->pgno; if (DBC_LOGGING(dbc)) { if ((ret = __heap_addrem_log(dbp, dbc->txn, - &LSN(cp->page), 0, DB_ADD_HEAP, cp->pgno, + &LSN(cp->page), 0, + OP_SET(DB_ADD_HEAP, cp->page), cp->pgno, (u_int32_t)cp->indx, old_size, &hdr_dbt, &log_dbt, &LSN(cp->page))) != 0) goto err; @@ -1397,6 +1431,8 @@ __heapc_reloc(dbc, key, data) memset(&hdr_dbt, 0, sizeof(DBT)); memset(&log_dbt, 0, sizeof(DBT)); COMPQUIET(key, NULL); + /* Blob database records never change size. */ + DB_ASSERT(dbp->env, !F_ISSET(old_hdr, HEAP_RECBLOB)); /* * We are updating an existing record, which will grow into a split @@ -1436,7 +1472,8 @@ __heapc_reloc(dbc, key, data) log_dbt.size = DB_ALIGN( old_hdr->size, sizeof(u_int32_t)); if ((ret = __heap_addrem_log(dbp, dbc->txn, - &LSN(cp->page), 0, DB_REM_HEAP, cp->pgno, + &LSN(cp->page), 0, + OP_SET(DB_REM_HEAP, cp->page), cp->pgno, (u_int32_t)cp->indx, old_size, &hdr_dbt, &log_dbt, &LSN(cp->page))) != 0) goto err; @@ -1471,7 +1508,7 @@ __heapc_reloc(dbc, key, data) size -= sizeof(db_indx_t); /* Round down to a multiple of 4. */ size = DB_ALIGN( - size - sizeof(u_int32_t) + 1, sizeof(u_int32_t)); + (size - sizeof(u_int32_t)) + 1, sizeof(u_int32_t)); DB_ASSERT(dbp->env, size >= sizeof(HEAPSPLITHDR)); new_hdr.std_hdr.size = (u_int16_t)(size - sizeof(HEAPSPLITHDR)); @@ -1495,7 +1532,8 @@ __heapc_reloc(dbc, key, data) if (DBC_LOGGING(dbc)) { if ((ret = __heap_addrem_log(dbp, dbc->txn, &LSN(cp->page), 0, - DB_ADD_HEAP, cp->pgno, (u_int32_t)cp->indx, + OP_SET(DB_ADD_HEAP, cp->page), + cp->pgno, (u_int32_t)cp->indx, size, &hdr_dbt, &t_data, &LSN(cp->page))) != 0) goto err; } else @@ -1565,7 +1603,8 @@ next_pg: if (next_rid.pgno != PGNO_INVALID) { log_dbt.size = DB_ALIGN( old_hdr->size, sizeof(u_int32_t)); if ((ret = __heap_addrem_log(dbp, dbc->txn, - &LSN(cp->page), 0, DB_REM_HEAP, cp->pgno, + &LSN(cp->page), 0, + OP_SET(DB_REM_HEAP, cp->page), cp->pgno, (u_int32_t)cp->indx, old_size, &hdr_dbt, &log_dbt, &LSN(cp->page))) != 0) goto err; @@ -1577,7 +1616,8 @@ next_pg: if (next_rid.pgno != PGNO_INVALID) { if (DBC_LOGGING(dbc)) { if ((ret = __heap_addrem_log(dbp, dbc->txn, - &LSN(cp->page), 0, DB_ADD_HEAP, cp->pgno, + &LSN(cp->page), 0, + OP_SET(DB_ADD_HEAP, cp->page), cp->pgno, (u_int32_t)cp->indx,old_size, &hdr_dbt, &log_dbt, &LSN(cp->page))) != 0) goto err; @@ -1608,20 +1648,26 @@ __heapc_put(dbc, key, data, flags, pgnop) DB *dbp; DBT hdr_dbt, log_dbt, new_data; DB_MPOOLFILE *mpf; + HEAPBLOBHDR bhdr; HEAPHDR hdr, *old_hdr; HEAP_CURSOR *cp; PAGE *rpage; db_pgno_t region_pgno; - int oldspace, ret, space, t_ret; - u_int32_t data_size, dlen, new_size, old_flags, old_size, tot_size; - u_int8_t *buf, *olddata, *src, *dest; + int buf_alloc, ret, t_ret; + off_t blob_size; + db_seq_t blob_id, new_blob_id; + u_int32_t data_size, dlen, new_size, old_flags, old_size; + u_int32_t oldspace, space, tot_size; + u_int8_t *buf, *olddata; dbp = dbc->dbp; mpf = dbp->mpf; cp = (HEAP_CURSOR *)dbc->internal; rpage = NULL; - buf = dest = src = NULL; + buf = NULL; + buf_alloc = 0; dlen = 0; + blob_id = new_blob_id = 0; if (flags != DB_CURRENT) { /* We're going to write following the get, so use RMW. */ @@ -1668,7 +1714,8 @@ __heapc_put(dbc, key, data, flags, pgnop) DB_ALIGN(old_hdr->size + HEAP_HDRSIZE(old_hdr), sizeof(u_int32_t)); if (old_size < sizeof(HEAPSPLITHDR)) old_size = sizeof(HEAPSPLITHDR); - if (F_ISSET(data, DB_DBT_PARTIAL)) { + /* Partial puts on blobs are dealt with in the blob code. */ + if (F_ISSET(data, DB_DBT_PARTIAL) && !F_ISSET(old_hdr, HEAP_RECBLOB)) { if (F_ISSET(old_hdr, HEAP_RECSPLIT)) tot_size = ((HEAPSPLITHDR *)old_hdr)->tsize; else @@ -1682,9 +1729,11 @@ __heapc_put(dbc, key, data, flags, pgnop) dlen = tot_size - data->doff; else dlen = data->dlen; - data_size = tot_size - dlen + data->size; + data_size = (tot_size - dlen) + data->size; } - } else + } else if F_ISSET(old_hdr, HEAP_RECBLOB) + data_size = HEAPBLOBREC_DSIZE; + else data_size = data->size; new_size = DB_ALIGN(data_size + sizeof(HEAPHDR), sizeof(u_int32_t)); if (new_size < sizeof(HEAPSPLITHDR)) @@ -1694,6 +1743,8 @@ __heapc_put(dbc, key, data, flags, pgnop) if (F_ISSET(old_hdr, HEAP_RECSPLIT) || (new_size > old_size && new_size - old_size > HEAP_FREESPACE(dbp, cp->page))) { + /* Blob database records never change size. */ + DB_ASSERT(dbp->env, !F_ISSET(old_hdr, HEAP_RECBLOB)); /* * We've got to split the record, not enough room on the * page. Splitting the record will remove old_size bytes and @@ -1707,13 +1758,14 @@ __heapc_put(dbc, key, data, flags, pgnop) memset(&new_data, 0, sizeof(DBT)); new_data.size = data_size; - if (F_ISSET(data, DB_DBT_PARTIAL)) { + if (F_ISSET(data, DB_DBT_PARTIAL) && !F_ISSET(old_hdr, HEAP_RECBLOB)) { /* * Before replacing the old data, we need to use it to build the * new data. */ if ((ret = __os_malloc(dbp->env, data_size, &buf)) != 0) goto err; + buf_alloc = 1; new_data.data = buf; /* @@ -1736,10 +1788,32 @@ __heapc_put(dbc, key, data, flags, pgnop) buf += data->size; /* Fill in remaining data from the old record, skipping dlen. */ - if (data->doff < old_hdr->size) { + if ((data->doff + data->dlen) < old_hdr->size) { olddata += data->doff + data->dlen; - memcpy(buf, - olddata, old_hdr->size - data->doff - data->dlen); + memcpy(buf, olddata, + (old_hdr->size - data->doff) - data->dlen); + } + } else if (F_ISSET(old_hdr, HEAP_RECBLOB)) { + data_size = HEAPBLOBREC_DSIZE; + new_data.size = HEAPBLOBREC_DSIZE; + if (F_ISSET(data, DB_DBT_BLOB_REC)) { + DB_ASSERT(dbp->env, + F_ISSET(((HEAPHDR *)data->data), HEAP_RECBLOB)); + new_data.data = HEAPBLOBREC_DATA(data->data); + } else { + memcpy(&bhdr, old_hdr, HEAPBLOBREC_SIZE); + blob_id = (db_seq_t)bhdr.id; + GET_BLOB_SIZE(dbp->env, bhdr, blob_size, ret); + if (ret != 0) + goto err; + if ((ret = __blob_repl(dbc, + data, blob_id, &new_blob_id, &blob_size)) != 0) + goto err; + bhdr.std_hdr.flags = HEAP_RECBLOB; + bhdr.std_hdr.size = HEAPBLOBREC_DSIZE; + SET_BLOB_SIZE(&bhdr, blob_size, HEAPBLOBHDR); + SET_BLOB_ID(&bhdr, new_blob_id, HEAPBLOBHDR); + new_data.data = HEAPBLOBREC_DATA(&bhdr); } } else { new_data.data = data->data; @@ -1751,19 +1825,23 @@ __heapc_put(dbc, key, data, flags, pgnop) */ memset(&hdr, 0, sizeof(HEAPHDR)); hdr.size = data_size; + if (F_ISSET(old_hdr, HEAP_RECBLOB)) + hdr.flags = HEAP_RECBLOB; if (DBC_LOGGING(dbc)) { hdr_dbt.data = old_hdr; hdr_dbt.size = HEAP_HDRSIZE(old_hdr); log_dbt.data = (u_int8_t *)old_hdr + hdr_dbt.size; log_dbt.size = DB_ALIGN(old_hdr->size, sizeof(u_int32_t)); if ((ret = __heap_addrem_log(dbp, dbc->txn, &LSN(cp->page), - 0, DB_REM_HEAP, cp->pgno, (u_int32_t)cp->indx, + 0, OP_SET(DB_REM_HEAP, cp->page), cp->pgno, + (u_int32_t)cp->indx, old_size, &hdr_dbt, &log_dbt, &LSN(cp->page))) != 0) goto err; hdr_dbt.data = &hdr; hdr_dbt.size = HEAP_HDRSIZE(&hdr); if ((ret = __heap_addrem_log(dbp, dbc->txn, &LSN(cp->page), - 0, DB_ADD_HEAP, cp->pgno, (u_int32_t)cp->indx, + 0, OP_SET(DB_ADD_HEAP, cp->page), cp->pgno, + (u_int32_t)cp->indx, new_size, &hdr_dbt, &new_data, &LSN(cp->page))) != 0) goto err; } else @@ -1788,14 +1866,14 @@ __heapc_put(dbc, key, data, flags, pgnop) dbc->thread_info, NULL, DB_MPOOL_DIRTY, &rpage)) != 0) goto err; - HEAP_SETSPACE(dbp, rpage, cp->pgno - region_pgno - 1, space); + HEAP_SETSPACE(dbp, rpage, (cp->pgno - region_pgno) - 1, space); } err: DB_ASSERT(dbp->env, ret != DB_PAGE_NOTFOUND); if (rpage != NULL && (t_ret = __memp_fput(mpf, dbc->thread_info, rpage, dbc->priority)) != 0 && ret == 0) ret = t_ret; - if (F_ISSET(data, DB_DBT_PARTIAL)) + if (buf_alloc) __os_free(dbp->env, new_data.data); if (ret != 0 && LOCK_ISSET(cp->lock)) @@ -1823,18 +1901,21 @@ __heap_getpage(dbc, size, avail) HEAP *h; HEAPPG *rpage; HEAP_CURSOR *cp; - db_pgno_t data_pgno, *lkd_pgs, meta_pgno, region_pgno, start_region; - int i, lk_mode, max, p, ret, space, start, t_ret; + db_pgno_t data_pgno, i, max, meta_pgno, p, region_pgno, start; + db_pgno_t start_region; + int ret, t_ret; + u_int32_t lk_mode, space; LOCK_INIT(meta_lock); + data_pgno = PGNO_INVALID; dbp = dbc->dbp; mpf = dbp->mpf; cp = (HEAP_CURSOR *)dbc->internal; h = dbp->heap_internal; start_region = region_pgno = h->curregion; max = HEAP_REGION_SIZE(dbp); - i = ret = t_ret = 0; - lkd_pgs = NULL; + i = 0; + ret = t_ret = 0; /* * The algorithm for finding a page: @@ -1897,10 +1978,10 @@ find: while ((ret = __memp_fget(mpf, ®ion_pgno, max = h->maxpgno - region_pgno; /* * Look in the bitmap for a page with sufficient free space. We use i - * in a slightly strange way. Because the 2-bits in the bitmap are only - * an estimate, there is a chance the data won't fit on the page we - * choose. In that case, we re-start the process and want to be able to - * resume this loop where we left off. + * in a slightly strange way. Because the 2-bits in the bitmap are + * only an estimate, there is a chance the data won't fit on the page + * we choose. In that case, we re-start the process and want to be + * able to resume this loop where we left off. */ for (; i < max; i++) { p = start + i; @@ -1908,7 +1989,7 @@ find: while ((ret = __memp_fget(mpf, ®ion_pgno, p -= max; if ((*avail = HEAP_SPACE(dbp, rpage, p)) > space) continue; - data_pgno = region_pgno + p + 1; + data_pgno = (region_pgno + p) + 1; ACQUIRE_CUR(dbc, DB_LOCK_WRITE, data_pgno, DB_LOCK_NOWAIT, 0, ret); /* @@ -2071,7 +2152,7 @@ pg_err: if (p != 0) { if (ret == DB_LOCK_NOTGRANTED) ret = 0; else if (ret != 0) { - /* + /* * Free up the metadata lock. If this was an error * other than a missing region page, bail. */ @@ -2165,7 +2246,7 @@ check: if (size + sizeof(db_indx_t) > HEAP_FREESPACE(dbp, cp->page)) { } } - h->curpgindx = data_pgno - region_pgno - 1; + h->curpgindx = (data_pgno - region_pgno) - 1; err: DB_ASSERT(dbp->env, ret != DB_PAGE_NOTFOUND); if (rpage != NULL && (t_ret = __memp_fput(mpf, dbc->thread_info, rpage, dbc->priority)) != 0 && ret == 0) @@ -2187,26 +2268,40 @@ __heap_append(dbc, key, data) DBT *data, *key; { DB *dbp; - DBT tmp_dbt; + DBT tmp_dbt, data_dbt; DB_HEAP_RID rid; + DB_LSN lsn; DB_MPOOLFILE *mpf; HEAPPG *rpage; + HEAPBLOBHDR bhdr; HEAPHDR hdr; HEAP_CURSOR *cp; db_indx_t indx; db_pgno_t region_pgno; - int ret, space, t_ret; + int is_blob, ret, t_ret; + off_t blob_size; + db_seq_t blob_id; u_int8_t avail; - u_int32_t data_size; + u_int32_t data_size, space; dbp = dbc->dbp; mpf = dbp->mpf; ret = t_ret = 0; rpage = NULL; cp = (HEAP_CURSOR *)dbc->internal; + blob_size = 0; + blob_id = 0; + + if (dbp->blob_threshold && + (data->size >= dbp->blob_threshold || F_ISSET(data, DB_DBT_BLOB))) + is_blob = 1; + else + is_blob = 0; /* Need data.size + header size, 4-byte aligned. */ - if (F_ISSET(data, DB_DBT_PARTIAL)) + if (is_blob) + data_size = HEAPBLOBREC_SIZE; + else if (F_ISSET(data, DB_DBT_PARTIAL)) data_size = DB_ALIGN(data->doff + data->size + sizeof(HEAPHDR), sizeof(u_int32_t)); else @@ -2222,24 +2317,42 @@ __heap_append(dbc, key, data) goto err; indx = HEAP_FREEINDX(cp->page); - memset(&hdr, 0, sizeof(HEAPHDR)); - hdr.size = data->size; - if (F_ISSET(data, DB_DBT_PARTIAL)) - hdr.size += data->doff; - tmp_dbt.data = &hdr; - tmp_dbt.size = sizeof(HEAPHDR); + if (is_blob) { + if ((ret = __blob_put( + dbc, data, &blob_id, &blob_size, &lsn)) != 0) + goto err; + memset(&bhdr, 0, HEAPBLOBREC_SIZE); + bhdr.std_hdr.flags = HEAP_RECBLOB; + bhdr.std_hdr.size = HEAPBLOBREC_DSIZE; + SET_BLOB_SIZE(&bhdr, blob_size, HEAPBLOBHDR); + SET_BLOB_ID(&bhdr, blob_id, HEAPBLOBHDR); + SET_BLOB_FILE_ID(&bhdr, dbp->blob_file_id, HEAPBLOBHDR); + tmp_dbt.data = &bhdr; + tmp_dbt.size = sizeof(HEAPHDR); + memset(&data_dbt, 0, sizeof(DBT)); + data_dbt.data = HEAPBLOBREC_DATA((&bhdr)); + data_dbt.size = HEAPBLOBREC_DSIZE; + } else { + memset(&hdr, 0, sizeof(HEAPHDR)); + hdr.size = data->size; + if (F_ISSET(data, DB_DBT_PARTIAL)) + hdr.size += data->doff; + tmp_dbt.data = &hdr; + tmp_dbt.size = sizeof(HEAPHDR); + memcpy(&data_dbt, data, sizeof(DBT)); + } /* Log the write. */ if (DBC_LOGGING(dbc)) { if ((ret = __heap_addrem_log(dbp, dbc->txn, &LSN(cp->page), - 0, DB_ADD_HEAP, cp->pgno, (u_int32_t)indx, - data_size, &tmp_dbt, data, &LSN(cp->page))) != 0) + 0, OP_SET(DB_ADD_HEAP, cp->page), cp->pgno, (u_int32_t)indx, + data_size, &tmp_dbt, &data_dbt, &LSN(cp->page))) != 0) goto err; } else LSN_NOT_LOGGED(LSN(cp->page)); if ((ret = __heap_pitem( - dbc, (PAGE *)cp->page, indx, data_size, &tmp_dbt, data)) != 0) + dbc, (PAGE *)cp->page, indx, data_size, &tmp_dbt, &data_dbt)) != 0) goto err; rid.pgno = cp->pgno; @@ -2256,7 +2369,7 @@ __heap_append(dbc, key, data) dbc->thread_info, NULL, DB_MPOOL_DIRTY, &rpage)) != 0) goto err; - HEAP_SETSPACE(dbp, rpage, cp->pgno - region_pgno - 1, space); + HEAP_SETSPACE(dbp, rpage, (cp->pgno - region_pgno) - 1, space); } err: DB_ASSERT(dbp->env, ret != DB_PAGE_NOTFOUND); @@ -2292,8 +2405,8 @@ __heapc_split(dbc, key, data, is_first) HEAP_CURSOR *cp; db_indx_t indx; db_pgno_t region_pgno; - int ret, spacebits, t_ret; - u_int32_t buflen, doff, left, size; + int ret, t_ret; + u_int32_t buflen, doff, left, size, spacebits; u_int8_t availbits, *buf; dbp = dbc->dbp; @@ -2308,7 +2421,6 @@ __heapc_split(dbc, key, data, is_first) ret = t_ret = 0; indx = 0; buf = NULL; - buflen = 0; /* * Write the record to multiple pages, in chunks starting from the end. @@ -2322,6 +2434,9 @@ __heapc_split(dbc, key, data, is_first) left += data->doff; } hdrs.tsize = left; + buflen = 1; + if ((ret = __os_malloc(dbp->env, buflen, &buf)) != 0) + return (ret); while (left > 0) { size = DB_ALIGN(left + sizeof(HEAPSPLITHDR), sizeof(u_int32_t)); if (size < sizeof(HEAPSPLITHDR)) @@ -2336,8 +2451,10 @@ __heapc_split(dbc, key, data, is_first) else hdrs.std_hdr.flags |= HEAP_RECFIRST; - if ((ret = __heap_getpage(dbc, size, &availbits)) != 0) + if ((ret = __heap_getpage(dbc, size, &availbits)) != 0) { + __os_free(dbp->env, buf); return (ret); + } /* * size is the total number of bytes being written to the page. @@ -2363,7 +2480,7 @@ __heapc_split(dbc, key, data, is_first) size -= sizeof(db_indx_t); /* Round down to a multiple of 4. */ size = DB_ALIGN( - size - sizeof(u_int32_t) + 1, sizeof(u_int32_t)); + (size - sizeof(u_int32_t)) + 1, sizeof(u_int32_t)); DB_ASSERT(dbp->env, size >= sizeof(HEAPSPLITHDR)); hdrs.std_hdr.size = (u_int16_t)(size - sizeof(HEAPSPLITHDR)); @@ -2401,10 +2518,10 @@ __heapc_split(dbc, key, data, is_first) * page minus the bytes we're taking from data. */ t_data.data = buf; - memset(buf, '\0', t_data.size - left + doff); - buf += t_data.size - left + doff; + memset(buf, 0, (t_data.size - left) + doff); + buf += (t_data.size - left) + doff; memcpy(buf, data->data, left - doff); - doff -= t_data.size - left + doff; + doff -= (t_data.size - left) + doff; buf = t_data.data; } hdr_dbt.data = &hdrs; @@ -2415,7 +2532,8 @@ __heapc_split(dbc, key, data, is_first) if (DBC_LOGGING(dbc)) { if ((ret = __heap_addrem_log(dbp, dbc->txn, &LSN(cp->page), 0, - DB_ADD_HEAP, cp->pgno, (u_int32_t)indx, + OP_SET(DB_ADD_HEAP, cp->page), + cp->pgno, (u_int32_t)indx, size, &hdr_dbt, &t_data, &LSN(cp->page))) != 0) goto err; } else @@ -2447,7 +2565,7 @@ __heapc_split(dbc, key, data, is_first) goto err; HEAP_SETSPACE(dbp, - rpage, cp->pgno - region_pgno - 1, spacebits); + rpage, (cp->pgno - region_pgno) - 1, spacebits); ret = __memp_fput(mpf, dbc->thread_info, rpage, dbc->priority); rpage = NULL; |
