/* * __ckpt_last_addr -- * Return the cookie associated with the file's last checkpoint. */ static int __ckpt_last_addr( WT_SESSION_IMPL *session, const char *config, WT_ITEM *addr) { WT_CONFIG ckptconf; WT_CONFIG_ITEM a, k, v; int64_t found; WT_RET(__wt_config_getones(session, config, "checkpoint", &v)); WT_RET(__wt_config_subinit(session, &ckptconf, &v)); for (found = 0; __wt_config_next(&ckptconf, &k, &v) == 0;) { /* Ignore checkpoints before the ones we've already seen. */ WT_RET(__wt_config_subgets(session, &v, "order", &a)); if (found && a.val < found) continue; found = a.val; /* * Copy out the address; our caller wants the raw cookie, not * the hex. */ WT_RET(__wt_config_subgets(session, &v, "addr", &a)); if (a.len != 0) WT_RET(__wt_nhex_to_raw(session, a.str, a.len, addr)); } return (found ? 0 : WT_NOTFOUND); }
/* * __verify_config_offsets -- * Debugging: optionally dump specific blocks from the file. */ static int __verify_config_offsets( WT_SESSION_IMPL *session, const char *cfg[], bool *quitp) { WT_CONFIG list; WT_CONFIG_ITEM cval, k, v; WT_DECL_RET; u_long offset; *quitp = false; WT_RET(__wt_config_gets(session, cfg, "dump_offsets", &cval)); WT_RET(__wt_config_subinit(session, &list, &cval)); while ((ret = __wt_config_next(&list, &k, &v)) == 0) { /* * Quit after dumping the requested blocks. (That's hopefully * what the user wanted, all of this stuff is just hooked into * verify because that's where we "dump blocks" for debugging.) */ *quitp = true; if (v.len != 0 || sscanf(k.str, "%lu", &offset) != 1) WT_RET_MSG(session, EINVAL, "unexpected dump offset format"); #if !defined(HAVE_DIAGNOSTIC) WT_RET_MSG(session, ENOTSUP, "the WiredTiger library was not built in diagnostic mode"); #else WT_TRET( __wt_debug_offset_blind(session, (wt_off_t)offset, NULL)); #endif } return (ret == WT_NOTFOUND ? 0 : ret); }
/* * __find_column_format -- * Find the format of the named column. */ static int __find_column_format(WT_SESSION_IMPL *session, WT_TABLE *table, WT_CONFIG_ITEM *colname, bool value_only, WT_PACK_VALUE *pv) { WT_CONFIG conf; WT_CONFIG_ITEM k, v; WT_DECL_RET; WT_PACK pack; bool inkey; __wt_config_subinit(session, &conf, &table->colconf); WT_RET(__pack_init(session, &pack, table->key_format)); inkey = true; while ((ret = __wt_config_next(&conf, &k, &v)) == 0) { if ((ret = __pack_next(&pack, pv)) == WT_NOTFOUND && inkey) { ret = __pack_init(session, &pack, table->value_format); if (ret == 0) ret = __pack_next(&pack, pv); inkey = false; } if (ret != 0) return (ret); if (k.len == colname->len && strncmp(colname->str, k.str, k.len) == 0) { if (value_only && inkey) return (__wt_set_return(session, EINVAL)); return (0); } } return (ret); }
/* * __wt_meta_ckptlist_get -- * Load all available checkpoint information for a file. */ int __wt_meta_ckptlist_get( WT_SESSION_IMPL *session, const char *fname, WT_CKPT **ckptbasep) { WT_CKPT *ckpt, *ckptbase; WT_CONFIG ckptconf; WT_CONFIG_ITEM k, v; WT_DECL_ITEM(buf); WT_DECL_RET; size_t allocated, slot; char *config; *ckptbasep = NULL; ckptbase = NULL; allocated = slot = 0; config = NULL; /* Retrieve the metadata information for the file. */ WT_RET(__wt_metadata_search(session, fname, &config)); /* Load any existing checkpoints into the array. */ WT_ERR(__wt_scr_alloc(session, 0, &buf)); if (__wt_config_getones(session, config, "checkpoint", &v) == 0) { __wt_config_subinit(session, &ckptconf, &v); for (; __wt_config_next(&ckptconf, &k, &v) == 0; ++slot) { WT_ERR(__wt_realloc_def( session, &allocated, slot + 1, &ckptbase)); ckpt = &ckptbase[slot]; WT_ERR(__ckpt_load(session, &k, &v, ckpt)); } } /* * Allocate an extra slot for a new value, plus a slot to mark the end. * * This isn't very clean, but there's necessary cooperation between the * schema layer (that maintains the list of checkpoints), the btree * layer (that knows when the root page is written, creating a new * checkpoint), and the block manager (which actually creates the * checkpoint). All of that cooperation is handled in the WT_CKPT * structure referenced from the WT_BTREE structure. */ WT_ERR(__wt_realloc_def(session, &allocated, slot + 2, &ckptbase)); /* Sort in creation-order. */ qsort(ckptbase, slot, sizeof(WT_CKPT), __ckpt_compare_order); /* Return the array to our caller. */ *ckptbasep = ckptbase; if (0) { err: __wt_meta_ckptlist_free(session, &ckptbase); } __wt_free(session, config); __wt_scr_free(session, &buf); return (ret); }
/* * __snap_get_last -- * Return the cookie associated with the file's last snapshot. */ static int __snap_get_last( WT_SESSION_IMPL *session, const char *config, WT_ITEM *addr) { WT_CONFIG snapconf; WT_CONFIG_ITEM a, k, v; int64_t found; WT_RET(__wt_config_getones(session, config, "snapshot", &v)); WT_RET(__wt_config_subinit(session, &snapconf, &v)); for (found = 0; __wt_config_next(&snapconf, &k, &v) == 0;) { if (found) { WT_RET(__wt_config_subgets(session, &v, "order", &a)); if (a.val < found) continue; } WT_RET(__wt_config_subgets(session, &v, "addr", &a)); if (a.len == 0) WT_RET(EINVAL); /* Our caller wants the raw cookie, not the hex. */ WT_RET(__wt_nhex_to_raw(session, a.str, a.len, addr)); WT_RET(__wt_config_subgets(session, &v, "order", &a)); found = a.val; } return (found ? 0 : WT_NOTFOUND); }
/* * __backup_uri -- * Backup a list of objects. */ static int __backup_uri(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, const char *cfg[], int *foundp, int *log_only) { WT_CONFIG targetconf; WT_CONFIG_ITEM cval, k, v; WT_DECL_ITEM(tmp); WT_DECL_RET; int target_list; const char *uri; *foundp = 0; *log_only = 0; /* * If we find a non-empty target configuration string, we have a job, * otherwise it's not our problem. */ WT_RET(__wt_config_gets(session, cfg, "target", &cval)); WT_RET(__wt_config_subinit(session, &targetconf, &cval)); for (cb->list_next = 0, target_list = 0; (ret = __wt_config_next(&targetconf, &k, &v)) == 0; ++target_list) { /* If it is our first time through, allocate. */ if (target_list == 0) { *foundp = 1; WT_ERR(__wt_scr_alloc(session, 512, &tmp)); } WT_ERR(__wt_buf_fmt(session, tmp, "%.*s", (int)k.len, k.str)); uri = tmp->data; if (v.len != 0) WT_ERR_MSG(session, EINVAL, "%s: invalid backup target: URIs may need quoting", uri); /* * Handle log targets. We do not need to go through the * schema worker, just call the function to append them. * Set log_only only if it is our only URI target. */ if (WT_PREFIX_MATCH(uri, "log:")) { if (target_list == 0) *log_only = 1; else *log_only = 0; WT_ERR(__wt_backup_list_uri_append( session, uri, NULL)); } else WT_ERR(__wt_schema_worker(session, uri, NULL, __wt_backup_list_uri_append, cfg, 0)); } WT_ERR_NOTFOUND_OK(ret); err: __wt_scr_free(session, &tmp); return (ret); }
/* * __snap_get_name -- * Return the cookie associated with a file's named snapshot. */ static int __snap_get_name(WT_SESSION_IMPL *session, const char *name, const char *config, WT_ITEM *addr) { WT_CONFIG snapconf; WT_CONFIG_ITEM a, k, v; WT_RET(__wt_config_getones(session, config, "snapshot", &v)); WT_RET(__wt_config_subinit(session, &snapconf, &v)); while (__wt_config_next(&snapconf, &k, &v) == 0) if (strlen(name) == k.len && strncmp(name, k.str, k.len) == 0) { WT_RET(__wt_config_subgets(session, &v, "addr", &a)); WT_RET(__wt_nhex_to_raw(session, a.str, a.len, addr)); return (0); } return (WT_NOTFOUND); }
/* * __backup_uri -- * Backup a list of objects. */ static int __backup_uri(WT_SESSION_IMPL *session, WT_CURSOR_BACKUP *cb, const char *cfg[], int *foundp) { WT_CONFIG targetconf; WT_CONFIG_ITEM cval, k, v; WT_DECL_ITEM(tmp); WT_DECL_RET; int target_list; const char *uri; *foundp = target_list = 0; /* * If we find a non-empty target configuration string, we have a job, * otherwise it's not our problem. */ WT_RET(__wt_config_gets(session, cfg, "target", &cval)); WT_RET(__wt_config_subinit(session, &targetconf, &cval)); for (cb->list_next = 0; (ret = __wt_config_next(&targetconf, &k, &v)) == 0;) { if (!target_list) { target_list = *foundp = 1; WT_ERR(__wt_scr_alloc(session, 512, &tmp)); } WT_ERR(__wt_buf_fmt(session, tmp, "%.*s", (int)k.len, k.str)); uri = tmp->data; if (v.len != 0) WT_ERR_MSG(session, EINVAL, "%s: invalid backup target: URIs may need quoting", uri); WT_ERR(__wt_schema_worker( session, uri, NULL, __wt_backup_list_uri_append, cfg, 0)); } WT_ERR_NOTFOUND_OK(ret); err: __wt_scr_free(&tmp); return (ret); }
/* * __ckpt_named -- * Return the information associated with a file's named checkpoint. */ static int __ckpt_named(WT_SESSION_IMPL *session, const char *checkpoint, const char *config, WT_CKPT *ckpt) { WT_CONFIG ckptconf; WT_CONFIG_ITEM k, v; WT_RET(__wt_config_getones(session, config, "checkpoint", &v)); WT_RET(__wt_config_subinit(session, &ckptconf, &v)); /* * Take the first match: there should never be more than a single * checkpoint of any name. */ while (__wt_config_next(&ckptconf, &k, &v) == 0) if (WT_STRING_MATCH(checkpoint, k.str, k.len)) return (__ckpt_load(session, &k, &v, ckpt)); return (WT_NOTFOUND); }
/* * __ckpt_last_name -- * Return the name associated with the file's last unnamed checkpoint. */ static int __ckpt_last_name( WT_SESSION_IMPL *session, const char *config, const char **namep) { WT_CONFIG ckptconf; WT_CONFIG_ITEM a, k, v; WT_DECL_RET; int64_t found; *namep = NULL; WT_ERR(__wt_config_getones(session, config, "checkpoint", &v)); WT_ERR(__wt_config_subinit(session, &ckptconf, &v)); for (found = 0; __wt_config_next(&ckptconf, &k, &v) == 0;) { /* * We only care about unnamed checkpoints; applications may not * use any matching prefix as a checkpoint name, the comparison * is pretty simple. */ if (k.len < strlen(WT_CHECKPOINT) || strncmp(k.str, WT_CHECKPOINT, strlen(WT_CHECKPOINT)) != 0) continue; /* Ignore checkpoints before the ones we've already seen. */ WT_ERR(__wt_config_subgets(session, &v, "order", &a)); if (found && a.val < found) continue; if (*namep != NULL) __wt_free(session, *namep); WT_ERR(__wt_strndup(session, k.str, k.len, namep)); found = a.val; } if (!found) ret = WT_NOTFOUND; if (0) { err: __wt_free(session, namep); } return (ret); }
/* * __wt_schema_colcheck -- * Check that a list of columns matches a (key,value) format pair. */ int __wt_schema_colcheck(WT_SESSION_IMPL *session, const char *key_format, const char *value_format, WT_CONFIG_ITEM *colconf, u_int *kcolsp, u_int *vcolsp) { WT_CONFIG conf; WT_CONFIG_ITEM k, v; WT_DECL_PACK_VALUE(pv); WT_DECL_RET; WT_PACK pack; u_int kcols, ncols, vcols; WT_RET(__pack_init(session, &pack, key_format)); for (kcols = 0; (ret = __pack_next(&pack, &pv)) == 0; kcols++) ; WT_RET_NOTFOUND_OK(ret); WT_RET(__pack_init(session, &pack, value_format)); for (vcols = 0; (ret = __pack_next(&pack, &pv)) == 0; vcols++) ; WT_RET_TEST(ret != WT_NOTFOUND, ret); /* Walk through the named columns. */ __wt_config_subinit(session, &conf, colconf); for (ncols = 0; (ret = __wt_config_next(&conf, &k, &v)) == 0; ncols++) ; WT_RET_TEST(ret != WT_NOTFOUND, ret); if (ncols != 0 && ncols != kcols + vcols) WT_RET_MSG(session, EINVAL, "Number of columns in '%.*s' " "does not match key format '%s' plus value format '%s'", (int)colconf->len, colconf->str, key_format, value_format); if (kcolsp != NULL) *kcolsp = kcols; if (vcolsp != NULL) *vcolsp = vcols; return (0); }
/* * __wt_table_check -- * Make sure all columns appear in a column group. */ int __wt_table_check(WT_SESSION_IMPL *session, WT_TABLE *table) { WT_CONFIG conf; WT_CONFIG_ITEM k, v; WT_DECL_RET; u_int cg, col, i; char coltype; if (table->is_simple) return (0); /* Walk through the columns. */ __wt_config_subinit(session, &conf, &table->colconf); /* Skip over the key columns. */ for (i = 0; i < table->nkey_columns; i++) WT_RET(__wt_config_next(&conf, &k, &v)); cg = col = 0; coltype = 0; while ((ret = __wt_config_next(&conf, &k, &v)) == 0) { if (__find_next_col( session, table, &k, &cg, &col, &coltype) != 0) WT_RET_MSG(session, EINVAL, "Column '%.*s' in '%s' does not appear in a " "column group", (int)k.len, k.str, table->iface.name); /* * Column groups can't store key columns in their value: * __wt_struct_reformat should have already detected this case. */ WT_ASSERT(session, coltype == WT_PROJ_VALUE); } WT_RET_TEST(ret != WT_NOTFOUND, ret); return (0); }
/* * __ckpt_last -- * Return the information associated with the file's last checkpoint. */ static int __ckpt_last(WT_SESSION_IMPL *session, const char *config, WT_CKPT *ckpt) { WT_CONFIG ckptconf; WT_CONFIG_ITEM a, k, v; int64_t found; WT_RET(__wt_config_getones(session, config, "checkpoint", &v)); WT_RET(__wt_config_subinit(session, &ckptconf, &v)); for (found = 0; __wt_config_next(&ckptconf, &k, &v) == 0;) { /* Ignore checkpoints before the ones we've already seen. */ WT_RET(__wt_config_subgets(session, &v, "order", &a)); if (found) { if (a.val < found) continue; __wt_meta_checkpoint_free(session, ckpt); } found = a.val; WT_RET(__ckpt_load(session, &k, &v, ckpt)); } return (found ? 0 : WT_NOTFOUND); }
/* * __ckpt_named_addr -- * Return the cookie associated with a file's named checkpoint. */ static int __ckpt_named_addr(WT_SESSION_IMPL *session, const char *checkpoint, const char *config, WT_ITEM *addr) { WT_CONFIG ckptconf; WT_CONFIG_ITEM a, k, v; WT_RET(__wt_config_getones(session, config, "checkpoint", &v)); WT_RET(__wt_config_subinit(session, &ckptconf, &v)); /* * Take the first match: there should never be more than a single * checkpoint of any name. */ while (__wt_config_next(&ckptconf, &k, &v) == 0) if (WT_STRING_MATCH(checkpoint, k.str, k.len)) { WT_RET(__wt_config_subgets(session, &v, "addr", &a)); if (a.len != 0) WT_RET(__wt_nhex_to_raw( session, a.str, a.len, addr)); return (0); } return (WT_NOTFOUND); }
static int __find_next_col(WT_SESSION_IMPL *session, WT_TABLE *table, WT_CONFIG_ITEM *colname, int *cgnump, int *colnump, char *coltype) { WT_BTREE *cgtree; WT_CONFIG conf; WT_CONFIG_ITEM cval, k, v; WT_DECL_RET; int cg, col, foundcg, foundcol, getnext; foundcg = foundcol = -1; getnext = 1; for (cgtree = NULL, cg = 0; cg < WT_COLGROUPS(table); cg++) { WT_RET(__wt_schema_get_btree(session, table->cg_name[cg], strlen(table->cg_name[cg]), NULL, 0)); cgtree = session->btree; /* * If there is only one column group, we just scan through all * of the columns. For tables with multiple column groups, we * look at the key columns once, then go through the value * columns for each group. */ if (cg == 0) { cval = table->colconf; col = 0; } else { cgcols: WT_ERR(__wt_config_getones(session, cgtree->config, "columns", &cval)); col = table->nkey_columns; } WT_ERR(__wt_config_subinit(session, &conf, &cval)); for (; __wt_config_next(&conf, &k, &v) == 0; col++) { if (cg == *cgnump && col == *colnump) getnext = 1; if (getnext && k.len == colname->len && strncmp(colname->str, k.str, k.len) == 0) { foundcg = cg; foundcol = col; getnext = 0; } if (cg == 0 && table->ncolgroups > 0 && col == table->nkey_columns - 1) goto cgcols; } cgtree = NULL; WT_ERR(__wt_session_release_btree(session)); } err: if (cgtree != NULL) WT_TRET(__wt_session_release_btree(session)); WT_RET(ret); if (foundcg == -1) return (WT_NOTFOUND); *cgnump = foundcg; if (foundcol < table->nkey_columns) { *coltype = WT_PROJ_KEY; *colnump = foundcol; } else { *coltype = WT_PROJ_VALUE; *colnump = foundcol - table->nkey_columns; } return (0); }
/* * __create_index -- * Create an index. */ static int __create_index(WT_SESSION_IMPL *session, const char *name, int exclusive, const char *config) { WT_CONFIG pkcols; WT_CONFIG_ITEM ckey, cval, icols; WT_DECL_RET; WT_ITEM confbuf, extra_cols, fmt, namebuf; WT_TABLE *table; const char *cfg[4] = { WT_CONFIG_BASE(session, index_meta), NULL, NULL, NULL }; const char *sourcecfg[] = { config, NULL, NULL }; const char *sourceconf, *source, *idxconf, *idxname; const char *tablename; size_t tlen; u_int i; idxconf = sourceconf = NULL; WT_CLEAR(confbuf); WT_CLEAR(fmt); WT_CLEAR(extra_cols); WT_CLEAR(namebuf); tablename = name; if (!WT_PREFIX_SKIP(tablename, "index:")) return (EINVAL); idxname = strchr(tablename, ':'); if (idxname == NULL) WT_RET_MSG(session, EINVAL, "Invalid index name, " "should be <table name>:<index name>: %s", name); tlen = (size_t)(idxname++ - tablename); if ((ret = __wt_schema_get_table(session, tablename, tlen, 1, &table)) != 0) WT_RET_MSG(session, ret, "Can't create an index for a non-existent table: %.*s", (int)tlen, tablename); if (__wt_config_getones(session, config, "source", &cval) == 0) { WT_ERR(__wt_buf_fmt(session, &namebuf, "%.*s", (int)cval.len, cval.str)); source = namebuf.data; } else { WT_ERR(__wt_schema_index_source( session, table, idxname, config, &namebuf)); source = namebuf.data; /* Add the source name to the index config before collapsing. */ WT_ERR(__wt_buf_catfmt(session, &confbuf, ",source=\"%s\"", source)); } /* Calculate the key/value formats. */ if (__wt_config_getones(session, config, "columns", &icols) != 0) WT_ERR_MSG(session, EINVAL, "No 'columns' configuration for '%s'", name); /* * The key format for an index is somewhat subtle: the application * specifies a set of columns that it will use for the key, but the * engine usually adds some hidden columns in order to derive the * primary key. These hidden columns are part of the source's * key_format, which we are calculating now, but not part of an index * cursor's key_format. */ WT_ERR(__wt_config_subinit(session, &pkcols, &table->colconf)); for (i = 0; i < table->nkey_columns && (ret = __wt_config_next(&pkcols, &ckey, &cval)) == 0; i++) { /* * If the primary key column is already in the secondary key, * don't add it again. */ if (__wt_config_subgetraw(session, &icols, &ckey, &cval) == 0) continue; WT_ERR(__wt_buf_catfmt( session, &extra_cols, "%.*s,", (int)ckey.len, ckey.str)); } if (ret != 0 && ret != WT_NOTFOUND) goto err; /* * Index values are normally empty: all columns are packed into the * index key. The exception is LSM, which (currently) reserves empty * values as tombstones. Use a single padding byte in that case. */ if (WT_PREFIX_MATCH(source, "lsm:")) WT_ERR(__wt_buf_fmt(session, &fmt, "value_format=x,")); else WT_ERR(__wt_buf_fmt(session, &fmt, "value_format=,")); WT_ERR(__wt_buf_fmt(session, &fmt, "value_format=,key_format=")); WT_ERR(__wt_struct_reformat(session, table, icols.str, icols.len, (const char *)extra_cols.data, 0, &fmt)); /* Check for a record number index key, which makes no sense. */ WT_ERR(__wt_config_getones(session, fmt.data, "key_format", &cval)); if (cval.len == 1 && cval.str[0] == 'r') WT_ERR_MSG(session, EINVAL, "column-store index may not use the record number as its " "index key"); sourcecfg[1] = fmt.data; WT_ERR(__wt_config_concat(session, sourcecfg, &sourceconf)); WT_ERR(__wt_schema_create(session, source, sourceconf)); cfg[1] = sourceconf; cfg[2] = confbuf.data; WT_ERR(__wt_config_collapse(session, cfg, &idxconf)); if ((ret = __wt_metadata_insert(session, name, idxconf)) != 0) { /* * If the entry already exists in the metadata, we're done. * This is an error for exclusive creates but okay otherwise. */ if (ret == WT_DUPLICATE_KEY) ret = exclusive ? EEXIST : 0; goto err; } err: __wt_free(session, idxconf); __wt_free(session, sourceconf); __wt_buf_free(session, &confbuf); __wt_buf_free(session, &extra_cols); __wt_buf_free(session, &fmt); __wt_buf_free(session, &namebuf); __wt_schema_release_table(session, table); return (ret); }
/* * config_check -- * Check the keys in an application-supplied config string match what is * specified in an array of check strings. */ static int config_check(WT_SESSION_IMPL *session, const WT_CONFIG_CHECK *checks, const char *config, size_t config_len) { WT_CONFIG parser, cparser, sparser; WT_CONFIG_ITEM k, v, ck, cv, dummy; WT_DECL_RET; int i; bool badtype, found; /* * The config_len parameter is optional, and allows passing in strings * that are not nul-terminated. */ if (config_len == 0) WT_RET(__wt_config_init(session, &parser, config)); else WT_RET(__wt_config_initn(session, &parser, config, config_len)); while ((ret = __wt_config_next(&parser, &k, &v)) == 0) { if (k.type != WT_CONFIG_ITEM_STRING && k.type != WT_CONFIG_ITEM_ID) WT_RET_MSG(session, EINVAL, "Invalid configuration key found: '%.*s'", (int)k.len, k.str); /* Search for a matching entry. */ for (i = 0; checks[i].name != NULL; i++) if (WT_STRING_MATCH(checks[i].name, k.str, k.len)) break; if (checks[i].name == NULL) WT_RET_MSG(session, EINVAL, "unknown configuration key: '%.*s'", (int)k.len, k.str); if (strcmp(checks[i].type, "boolean") == 0) { badtype = v.type != WT_CONFIG_ITEM_BOOL && (v.type != WT_CONFIG_ITEM_NUM || (v.val != 0 && v.val != 1)); } else if (strcmp(checks[i].type, "category") == 0) { /* Deal with categories of the form: XXX=(XXX=blah). */ ret = config_check(session, checks[i].subconfigs, k.str + strlen(checks[i].name) + 1, v.len); if (ret != EINVAL) badtype = false; else badtype = true; } else if (strcmp(checks[i].type, "format") == 0) { badtype = false; } else if (strcmp(checks[i].type, "int") == 0) { badtype = v.type != WT_CONFIG_ITEM_NUM; } else if (strcmp(checks[i].type, "list") == 0) { badtype = v.len > 0 && v.type != WT_CONFIG_ITEM_STRUCT; } else if (strcmp(checks[i].type, "string") == 0) { badtype = false; } else WT_RET_MSG(session, EINVAL, "unknown configuration type: '%s'", checks[i].type); if (badtype) WT_RET_MSG(session, EINVAL, "Invalid value for key '%.*s': expected a %s", (int)k.len, k.str, checks[i].type); if (checks[i].checkf != NULL) WT_RET(checks[i].checkf(session, &v)); if (checks[i].checks == NULL) continue; /* Setup an iterator for the check string. */ WT_RET(__wt_config_init(session, &cparser, checks[i].checks)); while ((ret = __wt_config_next(&cparser, &ck, &cv)) == 0) { if (WT_STRING_MATCH("min", ck.str, ck.len)) { if (v.val < cv.val) WT_RET_MSG(session, EINVAL, "Value too small for key '%.*s' " "the minimum is %.*s", (int)k.len, k.str, (int)cv.len, cv.str); } else if (WT_STRING_MATCH("max", ck.str, ck.len)) { if (v.val > cv.val) WT_RET_MSG(session, EINVAL, "Value too large for key '%.*s' " "the maximum is %.*s", (int)k.len, k.str, (int)cv.len, cv.str); } else if (WT_STRING_MATCH("choices", ck.str, ck.len)) { if (v.len == 0) WT_RET_MSG(session, EINVAL, "Key '%.*s' requires a value", (int)k.len, k.str); if (v.type == WT_CONFIG_ITEM_STRUCT) { /* * Handle the 'verbose' case of a list * containing restricted choices. */ WT_RET(__wt_config_subinit(session, &sparser, &v)); found = true; while (found && (ret = __wt_config_next(&sparser, &v, &dummy)) == 0) { ret = __wt_config_subgetraw( session, &cv, &v, &dummy); found = ret == 0; } } else { ret = __wt_config_subgetraw(session, &cv, &v, &dummy); found = ret == 0; } if (ret != 0 && ret != WT_NOTFOUND) return (ret); if (!found) WT_RET_MSG(session, EINVAL, "Value '%.*s' not a " "permitted choice for key '%.*s'", (int)v.len, v.str, (int)k.len, k.str); } else WT_RET_MSG(session, EINVAL, "unexpected configuration description " "keyword %.*s", (int)ck.len, ck.str); } } if (ret == WT_NOTFOUND) ret = 0; return (ret); }
/* * __wt_meta_ckptlist_get -- * Load all available checkpoint information for a file. */ int __wt_meta_ckptlist_get( WT_SESSION_IMPL *session, const char *fname, WT_CKPT **ckptbasep) { WT_CKPT *ckpt, *ckptbase; WT_CONFIG ckptconf; WT_CONFIG_ITEM a, k, v; WT_DECL_RET; WT_ITEM *buf; size_t allocated, slot; const char *config; char timebuf[64]; *ckptbasep = NULL; buf = NULL; ckptbase = NULL; allocated = slot = 0; config = NULL; /* Retrieve the metadata information for the file. */ WT_RET(__wt_metadata_read(session, fname, &config)); /* Load any existing checkpoints into the array. */ WT_ERR(__wt_scr_alloc(session, 0, &buf)); if (__wt_config_getones(session, config, "checkpoint", &v) == 0 && __wt_config_subinit(session, &ckptconf, &v) == 0) for (; __wt_config_next(&ckptconf, &k, &v) == 0; ++slot) { if (slot * sizeof(WT_CKPT) == allocated) WT_ERR(__wt_realloc(session, &allocated, (slot + 50) * sizeof(WT_CKPT), &ckptbase)); ckpt = &ckptbase[slot]; /* * Copy the name, address (raw and hex), order and time * into the slot. If there's no address, it's a fake. */ WT_ERR( __wt_strndup(session, k.str, k.len, &ckpt->name)); WT_ERR(__wt_config_subgets(session, &v, "addr", &a)); WT_ERR( __wt_buf_set(session, &ckpt->addr, a.str, a.len)); if (a.len == 0) F_SET(ckpt, WT_CKPT_FAKE); else WT_ERR(__wt_nhex_to_raw( session, a.str, a.len, &ckpt->raw)); WT_ERR(__wt_config_subgets(session, &v, "order", &a)); if (a.val == 0) goto format; ckpt->order = a.val; WT_ERR(__wt_config_subgets(session, &v, "time", &a)); if (a.len == 0) goto format; if (a.len > sizeof(timebuf) - 1) goto format; memcpy(timebuf, a.str, a.len); timebuf[a.len] = '\0'; if (sscanf(timebuf, "%" SCNuMAX, &ckpt->sec) != 1) goto format; WT_ERR(__wt_config_subgets(session, &v, "size", &a)); ckpt->ckpt_size = (uint64_t)a.val; } /* * Allocate an extra slot for a new value, plus a slot to mark the end. * * This isn't very clean, but there's necessary cooperation between the * schema layer (that maintains the list of checkpoints), the btree * layer (that knows when the root page is written, creating a new * checkpoint), and the block manager (which actually creates the * checkpoint). All of that cooperation is handled in the WT_CKPT * structure referenced from the WT_BTREE structure. */ if ((slot + 2) * sizeof(WT_CKPT) > allocated) WT_ERR(__wt_realloc(session, &allocated, (slot + 2) * sizeof(WT_CKPT), &ckptbase)); /* Sort in creation-order. */ qsort(ckptbase, slot, sizeof(WT_CKPT), __ckpt_compare_order); /* Return the array to our caller. */ *ckptbasep = ckptbase; if (0) { format: WT_ERR_MSG(session, WT_ERROR, "corrupted checkpoint list"); err: __wt_meta_ckptlist_free(session, ckptbase); } __wt_free(session, config); __wt_scr_free(&buf); return (ret); }
/* * wiredtiger_open -- * Main library entry point: open a new connection to a WiredTiger * database. */ int wiredtiger_open(const char *home, WT_EVENT_HANDLER *event_handler, const char *config, WT_CONNECTION **wt_connp) { static WT_CONNECTION stdc = { __conn_close, __conn_reconfigure, __conn_get_home, __conn_is_new, __conn_open_session, __conn_load_extension, __conn_add_data_source, __conn_add_collator, __conn_add_compressor, __conn_add_extractor }; static struct { const char *name; uint32_t flag; } *ft, directio_types[] = { { "data", WT_DIRECTIO_DATA }, { "log", WT_DIRECTIO_LOG }, { NULL, 0 } }; WT_CONFIG subconfig; WT_CONFIG_ITEM cval, skey, sval; WT_CONNECTION_IMPL *conn; WT_DECL_RET; WT_ITEM *cbuf, expath, exconfig; WT_SESSION_IMPL *session; const char *cfg[] = { __wt_confdfl_wiredtiger_open, config, NULL, NULL, NULL }; int exist; *wt_connp = NULL; session = NULL; cbuf = NULL; WT_CLEAR(expath); WT_CLEAR(exconfig); WT_RET(__wt_library_init()); WT_RET(__wt_calloc_def(NULL, 1, &conn)); conn->iface = stdc; /* * Immediately link the structure into the connection structure list: * the only thing ever looked at on that list is the database name, * and a NULL value is fine. */ __wt_spin_lock(NULL, &__wt_process.spinlock); TAILQ_INSERT_TAIL(&__wt_process.connqh, conn, q); __wt_spin_unlock(NULL, &__wt_process.spinlock); session = conn->default_session = &conn->dummy_session; session->iface.connection = &conn->iface; session->name = "wiredtiger_open"; __wt_event_handler_set(session, event_handler); /* Remaining basic initialization of the connection structure. */ WT_ERR(__wt_connection_init(conn)); /* Check the configuration strings. */ WT_ERR(__wt_config_check( session, __wt_confchk_wiredtiger_open, config, 0)); /* Get the database home. */ WT_ERR(__conn_home(session, home, cfg)); /* Make sure no other thread of control already owns this database. */ WT_ERR(__conn_single(session, cfg)); /* Read the database-home configuration file. */ WT_ERR(__conn_config_file(session, cfg, &cbuf)); /* Read the environment variable configuration. */ WT_ERR(__conn_config_env(session, cfg)); WT_ERR(__wt_config_gets(session, cfg, "hazard_max", &cval)); conn->hazard_max = (uint32_t)cval.val; WT_ERR(__wt_config_gets(session, cfg, "session_max", &cval)); conn->session_size = (uint32_t)cval.val + WT_NUM_INTERNAL_SESSIONS; WT_ERR(__wt_config_gets(session, cfg, "lsm_merge", &cval)); if (cval.val) F_SET(conn, WT_CONN_LSM_MERGE); WT_ERR(__wt_config_gets(session, cfg, "sync", &cval)); if (cval.val) F_SET(conn, WT_CONN_SYNC); WT_ERR(__wt_config_gets(session, cfg, "transactional", &cval)); if (cval.val) F_SET(conn, WT_CONN_TRANSACTIONAL); /* Configure verbose flags. */ WT_ERR(__conn_verbose_config(session, cfg)); WT_ERR(__wt_conn_cache_pool_config(session, cfg)); WT_ERR(__wt_config_gets(session, cfg, "logging", &cval)); if (cval.val != 0) WT_ERR(__wt_open( session, WT_LOG_FILENAME, 1, 0, 0, &conn->log_fh)); /* Configure direct I/O and buffer alignment. */ WT_ERR(__wt_config_gets(session, cfg, "buffer_alignment", &cval)); if (cval.val == -1) conn->buffer_alignment = WT_BUFFER_ALIGNMENT_DEFAULT; else conn->buffer_alignment = (size_t)cval.val; #ifndef HAVE_POSIX_MEMALIGN if (conn->buffer_alignment != 0) WT_ERR_MSG(session, EINVAL, "buffer_alignment requires posix_memalign"); #endif /* * Configuration: direct_io, mmap, statistics. */ WT_ERR(__wt_config_gets(session, cfg, "direct_io", &cval)); for (ft = directio_types; ft->name != NULL; ft++) { ret = __wt_config_subgets(session, &cval, ft->name, &sval); if (ret == 0) { if (sval.val) FLD_SET(conn->direct_io, ft->flag); } else if (ret != WT_NOTFOUND) goto err; } WT_ERR(__wt_config_gets(session, cfg, "mmap", &cval)); conn->mmap = cval.val == 0 ? 0 : 1; WT_ERR(__wt_config_gets(session, cfg, "statistics", &cval)); conn->statistics = cval.val == 0 ? 0 : 1; /* Load any extensions referenced in the config. */ WT_ERR(__wt_config_gets(session, cfg, "extensions", &cval)); WT_ERR(__wt_config_subinit(session, &subconfig, &cval)); while ((ret = __wt_config_next(&subconfig, &skey, &sval)) == 0) { WT_ERR(__wt_buf_fmt( session, &expath, "%.*s", (int)skey.len, skey.str)); if (sval.len > 0) WT_ERR(__wt_buf_fmt(session, &exconfig, "entry=%.*s\n", (int)sval.len, sval.str)); WT_ERR(conn->iface.load_extension(&conn->iface, expath.data, (sval.len > 0) ? exconfig.data : NULL)); } WT_ERR_NOTFOUND_OK(ret); /* * Open the connection; if that fails, the connection handle has been * destroyed by the time the open function returns. */ if ((ret = __wt_connection_open(conn, cfg)) != 0) { conn = NULL; WT_ERR(ret); } /* Open the default session. */ WT_ERR(__wt_open_session(conn, 1, NULL, NULL, &conn->default_session)); session = conn->default_session; /* * Check on the turtle and metadata files, creating them if necessary * (which avoids application threads racing to create the metadata file * later). */ WT_ERR(__wt_meta_turtle_init(session, &exist)); if (!exist) { /* * We're single-threaded, but acquire the schema lock * regardless: the lower level code checks that it is * appropriately synchronized. */ WT_WITH_SCHEMA_LOCK(session, ret = __wt_schema_create(session, WT_METADATA_URI, NULL)); WT_ERR(ret); } WT_ERR(__wt_metadata_open(session)); /* If there's a hot-backup file, load it. */ WT_ERR(__wt_metadata_load_backup(session)); /* * XXX LSM initialization. * This is structured so that it could be moved to an extension. */ WT_ERR(__wt_lsm_init(&conn->iface, NULL)); STATIC_ASSERT(offsetof(WT_CONNECTION_IMPL, iface) == 0); *wt_connp = &conn->iface; /* * Destroying the connection on error will destroy our session handle, * cleanup using the session handle first, then discard the connection. */ err: if (cbuf != NULL) __wt_buf_free(session, cbuf); __wt_buf_free(session, &expath); __wt_buf_free(session, &exconfig); if (ret != 0 && conn != NULL) WT_TRET(__wt_connection_destroy(conn)); /* Let the server threads proceed. */ if (ret == 0) conn->connection_initialized = 1; return (ret); }
/* * __find_next_col -- * Find the next column to use for a plan. */ static int __find_next_col(WT_SESSION_IMPL *session, WT_TABLE *table, WT_CONFIG_ITEM *colname, u_int *cgnump, u_int *colnump, char *coltype) { WT_COLGROUP *colgroup; WT_CONFIG conf; WT_CONFIG_ITEM cval, k, v; WT_DECL_RET; u_int cg, col, foundcg, foundcol, matchcg, matchcol; bool getnext; foundcg = foundcol = UINT_MAX; matchcg = *cgnump; matchcol = (*coltype == WT_PROJ_KEY) ? *colnump : *colnump + table->nkey_columns; getnext = true; for (colgroup = NULL, cg = 0; cg < WT_COLGROUPS(table); cg++) { colgroup = table->cgroups[cg]; /* * If there is only one column group, we just scan through all * of the columns. For tables with multiple column groups, we * look at the key columns once, then go through the value * columns for each group. */ if (cg == 0) { cval = table->colconf; col = 0; } else { cgcols: cval = colgroup->colconf; col = table->nkey_columns; } __wt_config_subinit(session, &conf, &cval); for (; (ret = __wt_config_next(&conf, &k, &v)) == 0; col++) { if (k.len == colname->len && strncmp(colname->str, k.str, k.len) == 0) { if (getnext) { foundcg = cg; foundcol = col; } getnext = cg == matchcg && col == matchcol; } if (cg == 0 && table->ncolgroups > 0 && col == table->nkey_columns - 1) goto cgcols; } WT_RET_TEST(ret != WT_NOTFOUND, ret); colgroup = NULL; } if (foundcg == UINT_MAX) return (WT_NOTFOUND); *cgnump = foundcg; if (foundcol < table->nkey_columns) { *coltype = WT_PROJ_KEY; *colnump = foundcol; } else { *coltype = WT_PROJ_VALUE; *colnump = foundcol - table->nkey_columns; } return (0); }
/* * __backup_uri -- * Backup a list of objects. */ static int __backup_uri(WT_SESSION_IMPL *session, const char *cfg[], bool *foundp, bool *log_only) { WT_CONFIG targetconf; WT_CONFIG_ITEM cval, k, v; WT_DECL_ITEM(tmp); WT_DECL_RET; bool target_list; const char *uri; *foundp = *log_only = false; /* * If we find a non-empty target configuration string, we have a job, * otherwise it's not our problem. */ WT_RET(__wt_config_gets(session, cfg, "target", &cval)); __wt_config_subinit(session, &targetconf, &cval); for (target_list = false; (ret = __wt_config_next(&targetconf, &k, &v)) == 0; target_list = true) { /* If it is our first time through, allocate. */ if (!target_list) { *foundp = true; WT_ERR(__wt_scr_alloc(session, 512, &tmp)); } WT_ERR(__wt_buf_fmt(session, tmp, "%.*s", (int)k.len, k.str)); uri = tmp->data; if (v.len != 0) WT_ERR_MSG(session, EINVAL, "%s: invalid backup target: URIs may need quoting", uri); /* * Handle log targets. We do not need to go through the schema * worker, just call the function to append them. Set log_only * only if it is our only URI target. */ if (WT_PREFIX_MATCH(uri, "log:")) { /* * Log archive cannot mix with incremental backup, don't * let that happen. */ if (FLD_ISSET( S2C(session)->log_flags, WT_CONN_LOG_ARCHIVE)) WT_ERR_MSG(session, EINVAL, "incremental backup not possible when " "automatic log archival configured"); *log_only = !target_list; WT_ERR(__backup_log_append( session, session->bkp_cursor, false)); } else { *log_only = false; WT_ERR(__wt_schema_worker(session, uri, NULL, __backup_list_uri_append, cfg, 0)); } } WT_ERR_NOTFOUND_OK(ret); err: __wt_scr_free(session, &tmp); return (ret); }
/* * __create_table -- * Create a table. */ static int __create_table(WT_SESSION_IMPL *session, const char *name, bool exclusive, const char *config) { WT_CONFIG conf; WT_CONFIG_ITEM cgkey, cgval, cval; WT_DECL_RET; WT_TABLE *table; const char *cfg[4] = { WT_CONFIG_BASE(session, table_meta), config, NULL, NULL }; const char *tablename; char *tableconf, *cgname; size_t cgsize; int ncolgroups; bool exists; cgname = NULL; table = NULL; tableconf = NULL; exists = false; tablename = name; if (!WT_PREFIX_SKIP(tablename, "table:")) return (__wt_unexpected_object_type(session, name, "table:")); if ((ret = __wt_schema_get_table(session, tablename, strlen(tablename), false, &table)) == 0) { if (exclusive) WT_ERR(EEXIST); exists = true; } WT_ERR_NOTFOUND_OK(ret); WT_ERR(__wt_config_gets(session, cfg, "colgroups", &cval)); __wt_config_subinit(session, &conf, &cval); for (ncolgroups = 0; (ret = __wt_config_next(&conf, &cgkey, &cgval)) == 0; ncolgroups++) ; WT_ERR_NOTFOUND_OK(ret); WT_ERR(__wt_config_collapse(session, cfg, &tableconf)); if (!exists) { WT_ERR(__wt_metadata_insert(session, name, tableconf)); /* Attempt to open the table now to catch any errors. */ WT_ERR(__wt_schema_get_table( session, tablename, strlen(tablename), true, &table)); if (ncolgroups == 0) { cgsize = strlen("colgroup:") + strlen(tablename) + 1; WT_ERR(__wt_calloc_def(session, cgsize, &cgname)); snprintf(cgname, cgsize, "colgroup:%s", tablename); WT_ERR(__create_colgroup( session, cgname, exclusive, config)); } } if (0) { err: if (table != NULL) { WT_TRET(__wt_schema_remove_table(session, table)); table = NULL; } } if (table != NULL) __wt_schema_release_table(session, table); __wt_free(session, cgname); __wt_free(session, tableconf); return (ret); }
/* * ___open_index -- * Open an index. */ static int __open_index(WT_SESSION_IMPL *session, WT_TABLE *table, WT_INDEX *idx) { WT_CONFIG colconf; WT_CONFIG_ITEM ckey, cval; WT_DECL_ITEM(buf); WT_DECL_ITEM(plan); WT_DECL_RET; u_int cursor_key_cols, i; WT_ERR(__wt_scr_alloc(session, 0, &buf)); /* Get the data source from the index config. */ WT_ERR(__wt_config_getones(session, idx->config, "source", &cval)); WT_ERR(__wt_buf_fmt( session, buf, "%.*s", (int)cval.len, cval.str)); idx->source = __wt_buf_steal(session, buf, NULL); idx->need_value = WT_PREFIX_MATCH(idx->source, "lsm:"); WT_ERR(__wt_config_getones(session, idx->config, "key_format", &cval)); WT_ERR(__wt_buf_fmt( session, buf, "%.*s", (int)cval.len, cval.str)); idx->key_format = __wt_buf_steal(session, buf, NULL); /* * The key format for an index is somewhat subtle: the application * specifies a set of columns that it will use for the key, but the * engine usually adds some hidden columns in order to derive the * primary key. These hidden columns are part of the file's key. * * The file's key_format is stored persistently, we need to calculate * the index cursor key format (which will usually omit some of those * keys). */ WT_ERR(__wt_config_getones( session, idx->config, "columns", &idx->colconf)); /* Start with the declared index columns. */ WT_ERR(__wt_config_subinit(session, &colconf, &idx->colconf)); cursor_key_cols = 0; while ((ret = __wt_config_next(&colconf, &ckey, &cval)) == 0) { WT_ERR(__wt_buf_catfmt( session, buf, "%.*s,", (int)ckey.len, ckey.str)); ++cursor_key_cols; } if (ret != 0 && ret != WT_NOTFOUND) goto err; /* * Now add any primary key columns from the table that are not * already part of the index key. */ WT_ERR(__wt_config_subinit(session, &colconf, &table->colconf)); for (i = 0; i < table->nkey_columns && (ret = __wt_config_next(&colconf, &ckey, &cval)) == 0; i++) { /* * If the primary key column is already in the secondary key, * don't add it again. */ if (__wt_config_subgetraw( session, &idx->colconf, &ckey, &cval) == 0) continue; WT_ERR(__wt_buf_catfmt( session, buf, "%.*s,", (int)ckey.len, ckey.str)); } if (ret != 0 && ret != WT_NOTFOUND) goto err; WT_ERR(__wt_scr_alloc(session, 0, &plan)); WT_ERR(__wt_struct_plan(session, table, buf->data, buf->size, 0, plan)); idx->key_plan = __wt_buf_steal(session, plan, NULL); /* Set up the cursor key format (the visible columns). */ WT_ERR(__wt_buf_init(session, buf, 0)); WT_ERR(__wt_struct_truncate(session, idx->key_format, cursor_key_cols, buf)); idx->idxkey_format = __wt_buf_steal(session, buf, NULL); /* By default, index cursor values are the table value columns. */ /* TODO Optimize to use index columns in preference to table lookups. */ WT_ERR(__wt_struct_plan(session, table, table->colconf.str, table->colconf.len, 1, plan)); idx->value_plan = __wt_buf_steal(session, plan, NULL); err: __wt_scr_free(&buf); __wt_scr_free(&plan); return (ret); }
/* * __wt_schema_open_table -- * Open a named table. */ int __wt_schema_open_table(WT_SESSION_IMPL *session, const char *name, size_t namelen, WT_TABLE **tablep) { WT_CONFIG cparser; WT_CONFIG_ITEM ckey, cval; WT_CURSOR *cursor; WT_DECL_RET; WT_ITEM buf; WT_TABLE *table; const char *tconfig; char *tablename; cursor = NULL; table = NULL; WT_CLEAR(buf); WT_RET(__wt_buf_fmt(session, &buf, "table:%.*s", (int)namelen, name)); tablename = __wt_buf_steal(session, &buf, NULL); WT_ERR(__wt_metadata_cursor(session, NULL, &cursor)); cursor->set_key(cursor, tablename); WT_ERR(cursor->search(cursor)); WT_ERR(cursor->get_value(cursor, &tconfig)); WT_ERR(__wt_calloc_def(session, 1, &table)); table->name = tablename; tablename = NULL; WT_ERR(__wt_config_getones(session, tconfig, "columns", &cval)); WT_ERR(__wt_config_getones(session, tconfig, "key_format", &cval)); WT_ERR(__wt_strndup(session, cval.str, cval.len, &table->key_format)); WT_ERR(__wt_config_getones(session, tconfig, "value_format", &cval)); WT_ERR(__wt_strndup(session, cval.str, cval.len, &table->value_format)); WT_ERR(__wt_strdup(session, tconfig, &table->config)); /* Point to some items in the copy to save re-parsing. */ WT_ERR(__wt_config_getones(session, table->config, "columns", &table->colconf)); /* * Count the number of columns: tables are "simple" if the columns * are not named. */ WT_ERR(__wt_config_subinit(session, &cparser, &table->colconf)); table->is_simple = 1; while ((ret = __wt_config_next(&cparser, &ckey, &cval)) == 0) table->is_simple = 0; if (ret != WT_NOTFOUND) goto err; /* Check that the columns match the key and value formats. */ if (!table->is_simple) WT_ERR(__wt_schema_colcheck(session, table->key_format, table->value_format, &table->colconf, &table->nkey_columns, NULL)); WT_ERR(__wt_config_getones(session, table->config, "colgroups", &table->cgconf)); /* Count the number of column groups. */ WT_ERR(__wt_config_subinit(session, &cparser, &table->cgconf)); table->ncolgroups = 0; while ((ret = __wt_config_next(&cparser, &ckey, &cval)) == 0) ++table->ncolgroups; if (ret != WT_NOTFOUND) goto err; WT_ERR(__wt_calloc_def(session, WT_COLGROUPS(table), &table->cgroups)); WT_ERR(__wt_schema_open_colgroups(session, table)); *tablep = table; if (0) { err: if (table != NULL) __wt_schema_destroy_table(session, table); } if (cursor != NULL) WT_TRET(cursor->close(cursor)); __wt_free(session, tablename); return (ret); }
/* * __wt_schema_open_colgroups -- * Open the column groups for a table. */ int __wt_schema_open_colgroups(WT_SESSION_IMPL *session, WT_TABLE *table) { WT_COLGROUP *colgroup; WT_CONFIG cparser; WT_CONFIG_ITEM ckey, cval; WT_DECL_RET; WT_DECL_ITEM(buf); char *cgconfig; u_int i; WT_ASSERT(session, F_ISSET(session, WT_SESSION_LOCKED_TABLE)); if (table->cg_complete) return (0); colgroup = NULL; cgconfig = NULL; WT_RET(__wt_scr_alloc(session, 0, &buf)); WT_ERR(__wt_config_subinit(session, &cparser, &table->cgconf)); /* Open each column group. */ for (i = 0; i < WT_COLGROUPS(table); i++) { if (table->ncolgroups > 0) WT_ERR(__wt_config_next(&cparser, &ckey, &cval)); else WT_CLEAR(ckey); /* * Always open from scratch: we may have failed part of the way * through opening a table, or column groups may have changed. */ __wt_schema_destroy_colgroup(session, &table->cgroups[i]); WT_ERR(__wt_buf_init(session, buf, 0)); WT_ERR(__wt_schema_colgroup_name(session, table, ckey.str, ckey.len, buf)); if ((ret = __wt_metadata_search( session, buf->data, &cgconfig)) != 0) { /* It is okay if the table is incomplete. */ if (ret == WT_NOTFOUND) ret = 0; goto err; } WT_ERR(__wt_calloc_one(session, &colgroup)); WT_ERR(__wt_strndup( session, buf->data, buf->size, &colgroup->name)); colgroup->config = cgconfig; cgconfig = NULL; WT_ERR(__wt_config_getones(session, colgroup->config, "columns", &colgroup->colconf)); WT_ERR(__wt_config_getones( session, colgroup->config, "source", &cval)); WT_ERR(__wt_strndup( session, cval.str, cval.len, &colgroup->source)); table->cgroups[i] = colgroup; colgroup = NULL; } if (!table->is_simple) { WT_ERR(__wt_table_check(session, table)); WT_ERR(__wt_buf_init(session, buf, 0)); WT_ERR(__wt_struct_plan(session, table, table->colconf.str, table->colconf.len, true, buf)); WT_ERR(__wt_strndup( session, buf->data, buf->size, &table->plan)); } table->cg_complete = true; err: __wt_scr_free(session, &buf); __wt_schema_destroy_colgroup(session, &colgroup); if (cgconfig != NULL) __wt_free(session, cgconfig); return (ret); }
/* * __wt_lsm_meta_read -- * Read the metadata for an LSM tree. */ int __wt_lsm_meta_read(WT_SESSION_IMPL *session, WT_LSM_TREE *lsm_tree) { WT_CONFIG cparser, lparser; WT_CONFIG_ITEM ck, cv, fileconf, lk, lv, metadata; WT_DECL_RET; WT_LSM_CHUNK *chunk; char *lsmconfig; u_int nchunks; chunk = NULL; /* -Wconditional-uninitialized */ /* LSM trees inherit the merge setting from the connection. */ if (F_ISSET(S2C(session), WT_CONN_LSM_MERGE)) F_SET(lsm_tree, WT_LSM_TREE_MERGES); WT_RET(__wt_metadata_search(session, lsm_tree->name, &lsmconfig)); WT_ERR(__wt_config_init(session, &cparser, lsmconfig)); while ((ret = __wt_config_next(&cparser, &ck, &cv)) == 0) { if (WT_STRING_MATCH("key_format", ck.str, ck.len)) { __wt_free(session, lsm_tree->key_format); WT_ERR(__wt_strndup(session, cv.str, cv.len, &lsm_tree->key_format)); } else if (WT_STRING_MATCH("value_format", ck.str, ck.len)) { __wt_free(session, lsm_tree->value_format); WT_ERR(__wt_strndup(session, cv.str, cv.len, &lsm_tree->value_format)); } else if (WT_STRING_MATCH("collator", ck.str, ck.len)) { if (cv.len == 0 || WT_STRING_MATCH("none", cv.str, cv.len)) continue; /* * Extract the application-supplied metadata (if any) * from the file configuration. */ WT_ERR(__wt_config_getones( session, lsmconfig, "file_config", &fileconf)); WT_CLEAR(metadata); WT_ERR_NOTFOUND_OK(__wt_config_subgets( session, &fileconf, "app_metadata", &metadata)); WT_ERR(__wt_collator_config(session, lsm_tree->name, &cv, &metadata, &lsm_tree->collator, &lsm_tree->collator_owned)); WT_ERR(__wt_strndup(session, cv.str, cv.len, &lsm_tree->collator_name)); } else if (WT_STRING_MATCH("bloom_config", ck.str, ck.len)) { __wt_free(session, lsm_tree->bloom_config); /* Don't include the brackets. */ WT_ERR(__wt_strndup(session, cv.str + 1, cv.len - 2, &lsm_tree->bloom_config)); } else if (WT_STRING_MATCH("file_config", ck.str, ck.len)) { __wt_free(session, lsm_tree->file_config); /* Don't include the brackets. */ WT_ERR(__wt_strndup(session, cv.str + 1, cv.len - 2, &lsm_tree->file_config)); } else if (WT_STRING_MATCH("auto_throttle", ck.str, ck.len)) { if (cv.val) F_SET(lsm_tree, WT_LSM_TREE_THROTTLE); else F_CLR(lsm_tree, WT_LSM_TREE_THROTTLE); } else if (WT_STRING_MATCH("bloom", ck.str, ck.len)) lsm_tree->bloom = (uint32_t)cv.val; else if (WT_STRING_MATCH("bloom_bit_count", ck.str, ck.len)) lsm_tree->bloom_bit_count = (uint32_t)cv.val; else if (WT_STRING_MATCH("bloom_hash_count", ck.str, ck.len)) lsm_tree->bloom_hash_count = (uint32_t)cv.val; else if (WT_STRING_MATCH("chunk_count_limit", ck.str, ck.len)) { lsm_tree->chunk_count_limit = (uint32_t)cv.val; if (cv.val != 0) F_CLR(lsm_tree, WT_LSM_TREE_MERGES); } else if (WT_STRING_MATCH("chunk_max", ck.str, ck.len)) lsm_tree->chunk_max = (uint64_t)cv.val; else if (WT_STRING_MATCH("chunk_size", ck.str, ck.len)) lsm_tree->chunk_size = (uint64_t)cv.val; else if (WT_STRING_MATCH("merge_max", ck.str, ck.len)) lsm_tree->merge_max = (uint32_t)cv.val; else if (WT_STRING_MATCH("merge_min", ck.str, ck.len)) lsm_tree->merge_min = (uint32_t)cv.val; else if (WT_STRING_MATCH("last", ck.str, ck.len)) lsm_tree->last = (u_int)cv.val; else if (WT_STRING_MATCH("chunks", ck.str, ck.len)) { WT_ERR(__wt_config_subinit(session, &lparser, &cv)); for (nchunks = 0; (ret = __wt_config_next(&lparser, &lk, &lv)) == 0; ) { if (WT_STRING_MATCH("id", lk.str, lk.len)) { WT_ERR(__wt_realloc_def(session, &lsm_tree->chunk_alloc, nchunks + 1, &lsm_tree->chunk)); WT_ERR( __wt_calloc_one(session, &chunk)); lsm_tree->chunk[nchunks++] = chunk; chunk->id = (uint32_t)lv.val; WT_ERR(__wt_lsm_tree_chunk_name(session, lsm_tree, chunk->id, &chunk->uri)); F_SET(chunk, WT_LSM_CHUNK_ONDISK | WT_LSM_CHUNK_STABLE); } else if (WT_STRING_MATCH( "bloom", lk.str, lk.len)) { WT_ERR(__wt_lsm_tree_bloom_name( session, lsm_tree, chunk->id, &chunk->bloom_uri)); F_SET(chunk, WT_LSM_CHUNK_BLOOM); continue; } else if (WT_STRING_MATCH( "chunk_size", lk.str, lk.len)) { chunk->size = (uint64_t)lv.val; continue; } else if (WT_STRING_MATCH( "count", lk.str, lk.len)) { chunk->count = (uint64_t)lv.val; continue; } else if (WT_STRING_MATCH( "generation", lk.str, lk.len)) { chunk->generation = (uint32_t)lv.val; continue; } } WT_ERR_NOTFOUND_OK(ret); lsm_tree->nchunks = nchunks; } else if (WT_STRING_MATCH("old_chunks", ck.str, ck.len)) { WT_ERR(__wt_config_subinit(session, &lparser, &cv)); for (nchunks = 0; (ret = __wt_config_next(&lparser, &lk, &lv)) == 0; ) { if (WT_STRING_MATCH("bloom", lk.str, lk.len)) { WT_ERR(__wt_strndup(session, lv.str, lv.len, &chunk->bloom_uri)); F_SET(chunk, WT_LSM_CHUNK_BLOOM); continue; } WT_ERR(__wt_realloc_def(session, &lsm_tree->old_alloc, nchunks + 1, &lsm_tree->old_chunks)); WT_ERR(__wt_calloc_one(session, &chunk)); lsm_tree->old_chunks[nchunks++] = chunk; WT_ERR(__wt_strndup(session, lk.str, lk.len, &chunk->uri)); F_SET(chunk, WT_LSM_CHUNK_ONDISK); } WT_ERR_NOTFOUND_OK(ret); lsm_tree->nold_chunks = nchunks; } /* * Ignore any other values: the metadata entry might have been * created by a future release, with unknown options. */ } WT_ERR_NOTFOUND_OK(ret); /* * If the default merge_min was not overridden, calculate it now. We * do this here so that trees created before merge_min was added get a * sane value. */ if (lsm_tree->merge_min < 2) lsm_tree->merge_min = WT_MAX(2, lsm_tree->merge_max / 2); err: __wt_free(session, lsmconfig); return (ret); }
/* * __open_index -- * Open an index. */ static int __open_index(WT_SESSION_IMPL *session, WT_TABLE *table, WT_INDEX *idx) { WT_CONFIG colconf; WT_CONFIG_ITEM ckey, cval, metadata; WT_DECL_ITEM(buf); WT_DECL_ITEM(plan); WT_DECL_RET; u_int npublic_cols, i; WT_ERR(__wt_scr_alloc(session, 0, &buf)); /* Get the data source from the index config. */ WT_ERR(__wt_config_getones(session, idx->config, "source", &cval)); WT_ERR(__wt_strndup(session, cval.str, cval.len, &idx->source)); WT_ERR(__wt_config_getones(session, idx->config, "immutable", &cval)); if (cval.val) F_SET(idx, WT_INDEX_IMMUTABLE); /* * Compatibility: we didn't always maintain collator information in * index metadata, cope when it isn't found. */ WT_CLEAR(cval); WT_ERR_NOTFOUND_OK(__wt_config_getones( session, idx->config, "collator", &cval)); if (cval.len != 0) { WT_CLEAR(metadata); WT_ERR_NOTFOUND_OK(__wt_config_getones( session, idx->config, "app_metadata", &metadata)); WT_ERR(__wt_collator_config( session, idx->name, &cval, &metadata, &idx->collator, &idx->collator_owned)); } WT_ERR(__wt_extractor_config( session, idx->name, idx->config, &idx->extractor, &idx->extractor_owned)); WT_ERR(__wt_config_getones(session, idx->config, "key_format", &cval)); WT_ERR(__wt_strndup(session, cval.str, cval.len, &idx->key_format)); /* * The key format for an index is somewhat subtle: the application * specifies a set of columns that it will use for the key, but the * engine usually adds some hidden columns in order to derive the * primary key. These hidden columns are part of the file's key. * * The file's key_format is stored persistently, we need to calculate * the index cursor key format (which will usually omit some of those * keys). */ WT_ERR(__wt_buf_init(session, buf, 0)); WT_ERR(__wt_config_getones( session, idx->config, "columns", &idx->colconf)); /* Start with the declared index columns. */ WT_ERR(__wt_config_subinit(session, &colconf, &idx->colconf)); for (npublic_cols = 0; (ret = __wt_config_next(&colconf, &ckey, &cval)) == 0; ++npublic_cols) WT_ERR(__wt_buf_catfmt( session, buf, "%.*s,", (int)ckey.len, ckey.str)); if (ret != WT_NOTFOUND) goto err; /* * If we didn't find any columns, the index must have an extractor. * We don't rely on this unconditionally because it was only added to * the metadata after version 2.3.1. */ if (npublic_cols == 0) { WT_ERR(__wt_config_getones( session, idx->config, "index_key_columns", &cval)); npublic_cols = (u_int)cval.val; WT_ASSERT(session, npublic_cols != 0); for (i = 0; i < npublic_cols; i++) WT_ERR(__wt_buf_catfmt(session, buf, "\"bad col\",")); } /* * Now add any primary key columns from the table that are not * already part of the index key. */ WT_ERR(__wt_config_subinit(session, &colconf, &table->colconf)); for (i = 0; i < table->nkey_columns && (ret = __wt_config_next(&colconf, &ckey, &cval)) == 0; i++) { /* * If the primary key column is already in the secondary key, * don't add it again. */ if (__wt_config_subgetraw( session, &idx->colconf, &ckey, &cval) == 0) continue; WT_ERR(__wt_buf_catfmt( session, buf, "%.*s,", (int)ckey.len, ckey.str)); } WT_ERR_NOTFOUND_OK(ret); /* * If the table doesn't yet have its column groups, don't try to * calculate a plan: we are just checking that the index creation is * sane. */ if (!table->cg_complete) goto err; WT_ERR(__wt_scr_alloc(session, 0, &plan)); WT_ERR(__wt_struct_plan( session, table, buf->data, buf->size, false, plan)); WT_ERR(__wt_strndup(session, plan->data, plan->size, &idx->key_plan)); /* Set up the cursor key format (the visible columns). */ WT_ERR(__wt_buf_init(session, buf, 0)); WT_ERR(__wt_struct_truncate(session, idx->key_format, npublic_cols, buf)); WT_ERR(__wt_strndup( session, buf->data, buf->size, &idx->idxkey_format)); /* * Add a trailing padding byte to the format. This ensures that there * will be no special optimization of the last column, so the primary * key columns can be simply appended. */ WT_ERR(__wt_buf_catfmt(session, buf, "x")); WT_ERR(__wt_strndup(session, buf->data, buf->size, &idx->exkey_format)); /* By default, index cursor values are the table value columns. */ /* TODO Optimize to use index columns in preference to table lookups. */ WT_ERR(__wt_buf_init(session, plan, 0)); WT_ERR(__wt_struct_plan(session, table, table->colconf.str, table->colconf.len, true, plan)); WT_ERR(__wt_strndup(session, plan->data, plan->size, &idx->value_plan)); err: __wt_scr_free(session, &buf); __wt_scr_free(session, &plan); return (ret); }
/* * __create_index -- * Create an index. */ static int __create_index(WT_SESSION_IMPL *session, const char *name, int exclusive, const char *config) { WT_CONFIG kcols, pkcols; WT_CONFIG_ITEM ckey, cval, icols, kval; WT_DECL_PACK_VALUE(pv); WT_DECL_RET; WT_ITEM confbuf, extra_cols, fmt, namebuf; WT_PACK pack; WT_TABLE *table; const char *cfg[4] = { WT_CONFIG_BASE(session, index_meta), NULL, NULL, NULL }; const char *sourcecfg[] = { config, NULL, NULL }; const char *source, *sourceconf, *idxname, *tablename; char *idxconf; size_t tlen; int have_extractor; u_int i, npublic_cols; sourceconf = NULL; idxconf = NULL; WT_CLEAR(confbuf); WT_CLEAR(fmt); WT_CLEAR(extra_cols); WT_CLEAR(namebuf); have_extractor = 0; tablename = name; if (!WT_PREFIX_SKIP(tablename, "index:")) return (EINVAL); idxname = strchr(tablename, ':'); if (idxname == NULL) WT_RET_MSG(session, EINVAL, "Invalid index name, " "should be <table name>:<index name>: %s", name); tlen = (size_t)(idxname++ - tablename); if ((ret = __wt_schema_get_table(session, tablename, tlen, 1, &table)) != 0) WT_RET_MSG(session, ret, "Can't create an index for a non-existent table: %.*s", (int)tlen, tablename); if (table->is_simple) WT_RET_MSG(session, EINVAL, "%s requires a table with named columns", name); if (__wt_config_getones(session, config, "source", &cval) == 0) { WT_ERR(__wt_buf_fmt(session, &namebuf, "%.*s", (int)cval.len, cval.str)); source = namebuf.data; } else { WT_ERR(__wt_schema_index_source( session, table, idxname, config, &namebuf)); source = namebuf.data; /* Add the source name to the index config before collapsing. */ WT_ERR(__wt_buf_catfmt(session, &confbuf, ",source=\"%s\"", source)); } if (__wt_config_getones_none( session, config, "extractor", &cval) == 0 && cval.len != 0) { have_extractor = 1; /* Custom extractors must supply a key format. */ if ((ret = __wt_config_getones( session, config, "key_format", &kval)) != 0) WT_ERR_MSG(session, EINVAL, "%s: custom extractors require a key_format", name); } /* Calculate the key/value formats. */ WT_CLEAR(icols); if (__wt_config_getones(session, config, "columns", &icols) != 0 && !have_extractor) WT_ERR_MSG(session, EINVAL, "%s: requires 'columns' configuration", name); /* * Count the public columns using the declared columns for normal * indices or the key format for custom extractors. */ npublic_cols = 0; if (!have_extractor) { WT_ERR(__wt_config_subinit(session, &kcols, &icols)); while ((ret = __wt_config_next(&kcols, &ckey, &cval)) == 0) ++npublic_cols; WT_ERR_NOTFOUND_OK(ret); } else { WT_ERR(__pack_initn(session, &pack, kval.str, kval.len)); while ((ret = __pack_next(&pack, &pv)) == 0) ++npublic_cols; WT_ERR_NOTFOUND_OK(ret); } /* * The key format for an index is somewhat subtle: the application * specifies a set of columns that it will use for the key, but the * engine usually adds some hidden columns in order to derive the * primary key. These hidden columns are part of the source's * key_format, which we are calculating now, but not part of an index * cursor's key_format. */ WT_ERR(__wt_config_subinit(session, &pkcols, &table->colconf)); for (i = 0; i < table->nkey_columns && (ret = __wt_config_next(&pkcols, &ckey, &cval)) == 0; i++) { /* * If the primary key column is already in the secondary key, * don't add it again. */ if (__wt_config_subgetraw(session, &icols, &ckey, &cval) == 0) { if (have_extractor) WT_ERR_MSG(session, EINVAL, "an index with a custom extractor may not " "include primary key columns"); continue; } WT_ERR(__wt_buf_catfmt( session, &extra_cols, "%.*s,", (int)ckey.len, ckey.str)); } if (ret != 0 && ret != WT_NOTFOUND) goto err; /* Index values are empty: all columns are packed into the index key. */ WT_ERR(__wt_buf_fmt(session, &fmt, "value_format=,key_format=")); if (have_extractor) { WT_ERR(__wt_buf_catfmt(session, &fmt, "%.*s", (int)kval.len, kval.str)); WT_CLEAR(icols); } /* * Construct the index key format, or append the primary key columns * for custom extractors. */ WT_ERR(__wt_struct_reformat(session, table, icols.str, icols.len, (const char *)extra_cols.data, 0, &fmt)); /* Check for a record number index key, which makes no sense. */ WT_ERR(__wt_config_getones(session, fmt.data, "key_format", &cval)); if (cval.len == 1 && cval.str[0] == 'r') WT_ERR_MSG(session, EINVAL, "column-store index may not use the record number as its " "index key"); WT_ERR(__wt_buf_catfmt( session, &fmt, ",index_key_columns=%u", npublic_cols)); sourcecfg[1] = fmt.data; WT_ERR(__wt_config_merge(session, sourcecfg, NULL, &sourceconf)); WT_ERR(__wt_schema_create(session, source, sourceconf)); cfg[1] = sourceconf; cfg[2] = confbuf.data; WT_ERR(__wt_config_collapse(session, cfg, &idxconf)); if ((ret = __wt_metadata_insert(session, name, idxconf)) != 0) { /* * If the entry already exists in the metadata, we're done. * This is an error for exclusive creates but okay otherwise. */ if (ret == WT_DUPLICATE_KEY) ret = exclusive ? EEXIST : 0; goto err; } /* Make sure that the configuration is valid. */ WT_ERR(__wt_schema_open_index( session, table, idxname, strlen(idxname), NULL)); err: __wt_free(session, idxconf); __wt_free(session, sourceconf); __wt_buf_free(session, &confbuf); __wt_buf_free(session, &extra_cols); __wt_buf_free(session, &fmt); __wt_buf_free(session, &namebuf); __wt_schema_release_table(session, table); return (ret); }
/* * __schema_open_table -- * Open a named table (internal version). */ static int __schema_open_table(WT_SESSION_IMPL *session, const char *name, size_t namelen, bool ok_incomplete, WT_TABLE **tablep) { WT_CONFIG cparser; WT_CONFIG_ITEM ckey, cval; WT_CURSOR *cursor; WT_DECL_ITEM(buf); WT_DECL_RET; WT_TABLE *table; const char *tconfig; char *tablename; cursor = NULL; table = NULL; tablename = NULL; WT_ASSERT(session, F_ISSET(session, WT_SESSION_LOCKED_TABLE)); WT_ERR(__wt_scr_alloc(session, 0, &buf)); WT_ERR(__wt_buf_fmt(session, buf, "table:%.*s", (int)namelen, name)); WT_ERR(__wt_strndup(session, buf->data, buf->size, &tablename)); WT_ERR(__wt_metadata_cursor(session, &cursor)); cursor->set_key(cursor, tablename); WT_ERR(cursor->search(cursor)); WT_ERR(cursor->get_value(cursor, &tconfig)); WT_ERR(__wt_calloc_one(session, &table)); table->name = tablename; tablename = NULL; table->name_hash = __wt_hash_city64(name, namelen); WT_ERR(__wt_config_getones(session, tconfig, "columns", &cval)); WT_ERR(__wt_config_getones(session, tconfig, "key_format", &cval)); WT_ERR(__wt_strndup(session, cval.str, cval.len, &table->key_format)); WT_ERR(__wt_config_getones(session, tconfig, "value_format", &cval)); WT_ERR(__wt_strndup(session, cval.str, cval.len, &table->value_format)); WT_ERR(__wt_strdup(session, tconfig, &table->config)); /* Point to some items in the copy to save re-parsing. */ WT_ERR(__wt_config_getones(session, table->config, "columns", &table->colconf)); /* * Count the number of columns: tables are "simple" if the columns * are not named. */ WT_ERR(__wt_config_subinit(session, &cparser, &table->colconf)); table->is_simple = true; while ((ret = __wt_config_next(&cparser, &ckey, &cval)) == 0) table->is_simple = false; if (ret != WT_NOTFOUND) goto err; /* Check that the columns match the key and value formats. */ if (!table->is_simple) WT_ERR(__wt_schema_colcheck(session, table->key_format, table->value_format, &table->colconf, &table->nkey_columns, NULL)); WT_ERR(__wt_config_getones(session, table->config, "colgroups", &table->cgconf)); /* Count the number of column groups. */ WT_ERR(__wt_config_subinit(session, &cparser, &table->cgconf)); table->ncolgroups = 0; while ((ret = __wt_config_next(&cparser, &ckey, &cval)) == 0) ++table->ncolgroups; if (ret != WT_NOTFOUND) goto err; if (table->ncolgroups > 0 && table->is_simple) WT_ERR_MSG(session, EINVAL, "%s requires a table with named columns", tablename); WT_ERR(__wt_calloc_def(session, WT_COLGROUPS(table), &table->cgroups)); WT_ERR(__wt_schema_open_colgroups(session, table)); if (!ok_incomplete && !table->cg_complete) WT_ERR_MSG(session, EINVAL, "'%s' cannot be used " "until all column groups are created", table->name); /* Copy the schema generation into the new table. */ table->schema_gen = S2C(session)->schema_gen; *tablep = table; if (0) { err: WT_TRET(__wt_schema_destroy_table(session, &table)); } WT_TRET(__wt_metadata_cursor_release(session, &cursor)); __wt_free(session, tablename); __wt_scr_free(session, &buf); return (ret); }
/* * __create_table -- * Create a table. */ static int __create_table(WT_SESSION_IMPL *session, const char *name, int exclusive, const char *config) { WT_CONFIG conf; WT_CONFIG_ITEM cgkey, cgval, cval; WT_DECL_RET; WT_TABLE *table; const char *cfg[4] = { WT_CONFIG_BASE(session, table_meta), config, NULL, NULL }; const char *tablename; char *tableconf, *cgname; size_t cgsize; int ncolgroups; cgname = NULL; table = NULL; tableconf = NULL; tablename = name; if (!WT_PREFIX_SKIP(tablename, "table:")) return (EINVAL); if ((ret = __wt_schema_get_table(session, tablename, strlen(tablename), 0, &table)) == 0) { __wt_schema_release_table(session, table); return (exclusive ? EEXIST : 0); } WT_RET_NOTFOUND_OK(ret); WT_ERR(__wt_config_gets(session, cfg, "colgroups", &cval)); WT_ERR(__wt_config_subinit(session, &conf, &cval)); for (ncolgroups = 0; (ret = __wt_config_next(&conf, &cgkey, &cgval)) == 0; ncolgroups++) ; WT_ERR_NOTFOUND_OK(ret); WT_ERR(__wt_config_collapse(session, cfg, &tableconf)); if ((ret = __wt_metadata_insert(session, name, tableconf)) != 0) { /* * If the entry already exists in the metadata, we're done. * This is an error for exclusive creates but okay otherwise. */ if (ret == WT_DUPLICATE_KEY) ret = exclusive ? EEXIST : 0; goto err; } /* Attempt to open the table now to catch any errors. */ WT_ERR(__wt_schema_get_table( session, tablename, strlen(tablename), 1, &table)); if (ncolgroups == 0) { cgsize = strlen("colgroup:") + strlen(tablename) + 1; WT_ERR(__wt_calloc_def(session, cgsize, &cgname)); snprintf(cgname, cgsize, "colgroup:%s", tablename); WT_ERR(__create_colgroup(session, cgname, exclusive, config)); } if (0) { err: if (table != NULL) { WT_TRET(__wt_schema_remove_table(session, table)); table = NULL; } } if (table != NULL) __wt_schema_release_table(session, table); __wt_free(session, cgname); __wt_free(session, tableconf); return (ret); }