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/blob/blob_page.c | |
parent | 7a2660ba9cc2dc03a69ddfcfd95369395cc87444 (diff) | |
download | berkeleydb-db-6.1.23.tar.gz |
Diffstat (limited to 'src/blob/blob_page.c')
-rw-r--r-- | src/blob/blob_page.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/src/blob/blob_page.c b/src/blob/blob_page.c new file mode 100644 index 00000000..96a2b59b --- /dev/null +++ b/src/blob/blob_page.c @@ -0,0 +1,374 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2013, 2015 Oracle and/or its affiliates. All rights reserved. + */ + +#include "db_config.h" + +#include "db_int.h" +#include "dbinc/db_page.h" +#include "dbinc/db_am.h" +#include "dbinc/fop.h" + +/* + * Blob file data item code. + * + * Blob file data entries are stored on linked lists of pages. The initial + * reference is a structure with an encoded version of the path where the file + * is stored. The blob file contains only the users data. + */ + +/* + * __blob_bulk -- + * Dump blob file into buffer. + * The space requirements have already been checked, if the blob is + * larger than UINT32MAX then DB_BUFFER_SMALL would have already + * been returned. + * PUBLIC: int __blob_bulk + * PUBLIC: __P((DBC *, u_int32_t, db_seq_t, u_int8_t *)); + */ +int +__blob_bulk(dbc, len, blob_id, dp) + DBC *dbc; + u_int32_t len; + db_seq_t blob_id; + u_int8_t *dp; +{ + DBT dbt; + DB_FH *fhp; + ENV *env; + int ret, t_ret; + + env = dbc->dbp->env; + fhp = NULL; + memset(&dbt, 0, sizeof(dbt)); + F_SET(&dbt, DB_DBT_USERMEM); + dbt.ulen = len; + dbt.data = (void *)dp; + + if ((ret = __blob_file_open( + dbc->dbp, &fhp, blob_id, DB_FOP_READONLY, 1)) != 0) + goto err; + + if ((ret = __blob_file_read(env, fhp, &dbt, 0, len)) != 0) + goto err; + + /* Close any open file descriptors. */ +err: if (fhp != NULL) { + t_ret = __blob_file_close(dbc, fhp, 0); + if (ret == 0) + ret = t_ret; + } + return (ret); +} + +/* + * __blob_get -- + * Get a blob file item. Analogous to db_overflow.c:__db_goff. + * + * PUBLIC: int __blob_get __P((DBC *, + * PUBLIC: DBT *, db_seq_t, off_t, void **, u_int32_t *)); + */ +int +__blob_get(dbc, dbt, blob_id, file_size, bpp, bpsz) + DBC *dbc; + DBT *dbt; + db_seq_t blob_id; + off_t file_size; + void **bpp; + u_int32_t *bpsz; +{ + DB_FH *fhp; + ENV *env; + int ret, t_ret; + u_int32_t needed, start, tlen; + + env = dbc->dbp->env; + fhp = NULL; + ret = 0; + + /* + * Blobs larger than UINT32_MAX can only be read using + * the DB_STREAM API, or the DB_DBT_PARTIAL API. + */ + if (file_size > UINT32_MAX) { + if (!F_ISSET(dbt, DB_DBT_PARTIAL)) { + dbt->size = UINT32_MAX; + ret = DB_BUFFER_SMALL; + goto err; + } else + tlen = UINT32_MAX; + } else + tlen = (u_int32_t)file_size; + + if (((ret = __db_alloc_dbt( + env, dbt, tlen, &needed, &start, bpp, bpsz)) != 0) || needed == 0) + goto err; + dbt->size = needed; + + if ((ret = __blob_file_open( + dbc->dbp, &fhp, blob_id, DB_FOP_READONLY, 1)) != 0) + goto err; + + if ((ret = __blob_file_read(env, fhp, dbt, dbt->doff, needed)) != 0) + goto err; + + /* Close any open file descriptors. */ +err: if (fhp != NULL) { + t_ret = __blob_file_close(dbc, fhp, 0); + if (ret == 0) + ret = t_ret; + } + /* Does the dbt need to be cleaned on error? */ + return (ret); +} + +/* + * __blob_put -- + * Put a blob file item. + * + * PUBLIC: int __blob_put __P(( + * PUBLIC: DBC *, DBT *, db_seq_t *, off_t *size, DB_LSN *)); + */ +int +__blob_put(dbc, dbt, blob_id, size, plsn) + DBC *dbc; + DBT *dbt; + db_seq_t *blob_id; + off_t *size; + DB_LSN *plsn; +{ + DBT partial; + DB_FH *fhp; + ENV *env; + int ret, t_ret; + off_t offset; + + env = dbc->dbp->env; + fhp = NULL; + offset = 0; + DB_ASSERT(env, blob_id != NULL); + DB_ASSERT(env, *blob_id == 0); + + ZERO_LSN(*plsn); + + /* If the id didn't refer to an existing blob generate a new one. */ + if ((ret = __blob_file_create(dbc, &fhp, blob_id)) != 0) + goto err; + + /* + * If doing a partial put with dbt->doff == 0, then treat like + * a normal put. Otherwise write NULLs into the file up to doff, which + * is required by the PARTIAL API. Since the file is being created, + * its size is always 0. + */ + DB_ASSERT(env, *size == 0); + if (F_ISSET(dbt, DB_DBT_PARTIAL) && dbt->doff > 0) { + memset(&partial, 0, sizeof(partial)); + if ((ret = __os_malloc(env, dbt->doff, &partial.data)) != 0) + goto err; + memset(partial.data, 0, dbt->doff); + partial.size = dbt->doff; + ret = __blob_file_write( + dbc, fhp, &partial, 0, *blob_id, size, DB_FOP_CREATE); + offset = dbt->doff; + __os_free(env, partial.data); + if (ret != 0) + goto err; + } + + if ((ret = __blob_file_write( + dbc, fhp, dbt, offset, *blob_id, size, DB_FOP_CREATE)) != 0) + goto err; + + /* Close any open file descriptors. */ +err: if (fhp != NULL) { + t_ret = __blob_file_close(dbc, fhp, DB_FOP_WRITE); + if (ret == 0) + ret = t_ret; + } + return (ret); +} + +/* + * __blob_repl -- + * Replace a blob file contents. It would be nice if this could be done + * by truncating the file and writing in the new data, but undoing a + * truncate would require a lot of logging, so it is performed by + * deleting the old blob file, and creating a new one. + * + * PUBLIC: int __blob_repl __P((DBC *, DBT *, db_seq_t, db_seq_t *,off_t *)); + */ +int +__blob_repl(dbc, nval, blob_id, new_blob_id, size) + DBC *dbc; + DBT *nval; + db_seq_t blob_id; + db_seq_t *new_blob_id; + off_t *size; +{ + DBT partial; + DB_FH *fhp, *new_fhp; + DB_LSN lsn; + ENV *env; + int ret, t_ret; + off_t current, old_size; + + fhp = new_fhp = NULL; + *new_blob_id = 0; + old_size = *size; + env = dbc->env; + memset(&partial, 0, sizeof(partial)); + + /* + * Handling partial replace. + * 1. doff > blob file size : Pad the end of the blob file with NULLs + * up to doff, then append the data. + * 2. doff == size: Write the data to the existing blob file. + * 3. dlen == size: Write the data to the existing blob file. + * 4. Create a new blob file. Copy old blob data up to doff + * to the new file. Append the new data. Append data + * from the old file from doff + dlen to the end of the + * old file to the new file. Delete the old file. + */ + if (F_ISSET(nval, DB_DBT_PARTIAL)) { + if ((nval->doff > *size) || + ((nval->doff == *size) || (nval->dlen == nval->size))) { + /* Open the file for appending. */ + if ((ret = __blob_file_open( + dbc->dbp, &fhp, blob_id, 0, 1)) != 0) + goto err; + *new_blob_id = blob_id; + + /* Pad the end of the blob with NULLs. */ + if (nval->doff > *size) { + partial.size = nval->doff - (u_int32_t)*size; + if ((ret = __os_malloc( + env, partial.size, &partial.data)) != 0) + goto err; + memset(partial.data, 0, partial.size); + if ((ret = __blob_file_write(dbc, fhp, + &partial, *size, blob_id, size, 0)) != 0) + goto err; + } + + /* Write in the data. */ + if ((ret = __blob_file_write(dbc, fhp, + nval, nval->doff, blob_id, size, 0)) != 0) + goto err; + + /* Close the file */ + ret = __blob_file_close(dbc, fhp, DB_FOP_WRITE); + fhp = NULL; + if (ret != 0) + goto err; + } else { + /* Open the old blob file. */ + if ((ret = __blob_file_open( + dbc->dbp, &fhp, blob_id, DB_FOP_READONLY, 1)) != 0) + goto err; + /* Create the new blob file. */ + if ((ret = __blob_file_create( + dbc, &new_fhp, new_blob_id)) != 0) + goto err; + + *size = 0; + /* Copy data to the new file up to doff. */ + if (nval->doff != 0) { + partial.ulen = partial.size = nval->doff; + if ((ret = __os_malloc( + env, partial.ulen, &partial.data)) != 0) + goto err; + if ((ret = __blob_file_read( + env, fhp, &partial, 0, partial.size)) != 0) + goto err; + if ((ret = __blob_file_write( + dbc, new_fhp, &partial, 0, + *new_blob_id, size, DB_FOP_CREATE)) != 0) + goto err; + } + + /* Write the partial data into the new file. */ + if ((ret = __blob_file_write( + dbc, new_fhp, nval, nval->doff, + *new_blob_id, size, DB_FOP_CREATE)) != 0) + goto err; + + /* Copy remaining blob data into the new file. */ + current = nval->doff + nval->dlen; + while (current < old_size) { + if (partial.ulen < MEGABYTE) { + if ((ret = __os_realloc(env, + MEGABYTE, &partial.data)) != 0) + goto err; + partial.size = partial.ulen = MEGABYTE; + } + if ((old_size - current) < partial.ulen) { + partial.size = + (u_int32_t)(old_size - current); + } else + partial.size = MEGABYTE; + + if ((ret = __blob_file_read(env, fhp, + &partial, current, partial.size)) != 0) + goto err; + if ((ret = __blob_file_write( + dbc, new_fhp, &partial, *size, + *new_blob_id, size, DB_FOP_CREATE)) != 0) + goto err; + current += partial.size; + } + + /* Close the old file. */ + ret = __blob_file_close(dbc, fhp, 0); + fhp = NULL; + if (ret != 0) + goto err; + + /* Delete the old blob file. */ + if ((ret = __blob_del(dbc, blob_id)) != 0) + goto err; + } + goto err; + } + + if ((ret = __blob_del(dbc, blob_id)) != 0) + goto err; + + *size = 0; + if ((ret = __blob_put(dbc, nval, new_blob_id, size, &lsn)) != 0) + goto err; + +err: if (fhp != NULL) { + t_ret = __blob_file_close(dbc, fhp, DB_FOP_WRITE); + if (ret == 0) + ret = t_ret; + } + if (new_fhp != NULL) { + t_ret = __blob_file_close(dbc, new_fhp, DB_FOP_WRITE); + if (ret == 0) + ret = t_ret; + } + if (partial.data != NULL) + __os_free(env, partial.data); + return (ret); +} + +/* + * __blob_del -- + * Delete a blob file. The onpage record is handled separately.. + * + * PUBLIC: int __blob_del __P((DBC *, db_seq_t)); + */ +int +__blob_del(dbc, blob_id) + DBC *dbc; + db_seq_t blob_id; +{ + int ret; + + ret = __blob_file_delete(dbc, blob_id); + + return (ret); +} |