/* * __config_getraw -- * Given a config parser, find the final value for a given key. */ static int __config_getraw( WT_CONFIG *cparser, WT_CONFIG_ITEM *key, WT_CONFIG_ITEM *value, bool top) { WT_CONFIG sparser; WT_CONFIG_ITEM k, v, subk; WT_DECL_RET; bool found; found = false; while ((ret = __config_next(cparser, &k, &v)) == 0) { if (k.type != WT_CONFIG_ITEM_STRING && k.type != WT_CONFIG_ITEM_ID) continue; if (k.len == key->len && strncmp(key->str, k.str, k.len) == 0) { *value = v; found = true; } else if (k.len < key->len && key->str[k.len] == '.' && strncmp(key->str, k.str, k.len) == 0) { subk.str = key->str + k.len + 1; subk.len = (key->len - k.len) - 1; WT_RET(__wt_config_initn( cparser->session, &sparser, v.str, v.len)); if ((ret = __config_getraw( &sparser, &subk, value, false)) == 0) found = true; WT_RET_NOTFOUND_OK(ret); } } WT_RET_NOTFOUND_OK(ret); if (!found) return (WT_NOTFOUND); return (top ? __config_process_value(cparser, value) : 0); }
/* * wiredtiger_config_parser_open -- * Create a configuration parser. */ int wiredtiger_config_parser_open(WT_SESSION *wt_session, const char *config, size_t len, WT_CONFIG_PARSER **config_parserp) { static const WT_CONFIG_PARSER stds = { __config_parser_close, __config_parser_next, __config_parser_get }; WT_CONFIG_ITEM config_item = { config, len, 0, WT_CONFIG_ITEM_STRING }; WT_CONFIG_PARSER_IMPL *config_parser; WT_SESSION_IMPL *session; *config_parserp = NULL; session = (WT_SESSION_IMPL *)wt_session; WT_RET(__wt_calloc_one(session, &config_parser)); config_parser->iface = stds; config_parser->session = session; /* * Setup a WT_CONFIG_ITEM to be used for get calls and a WT_CONFIG * structure for iterations through the configuration string. */ memcpy(&config_parser->config_item, &config_item, sizeof(config_item)); __wt_config_initn(session, &config_parser->config, config, len); *config_parserp = (WT_CONFIG_PARSER *)config_parser; return (0); }
/* * __wt_struct_reformat -- * Given a table and a list of columns (which could be values in a column * group or index keys), calculate the resulting new format string. * The result will be appended to the format buffer. */ int __wt_struct_reformat(WT_SESSION_IMPL *session, WT_TABLE *table, const char *columns, size_t len, const char *extra_cols, int value_only, WT_ITEM *format) { WT_CONFIG config; WT_CONFIG_ITEM k, next_k, next_v; WT_DECL_RET; WT_PACK_VALUE pv; int have_next; WT_CLEAR(pv); /* -Wuninitialized */ WT_RET(__wt_config_initn(session, &config, columns, len)); WT_RET(__wt_config_next(&config, &next_k, &next_v)); do { k = next_k; ret = __wt_config_next(&config, &next_k, &next_v); if (ret != 0 && ret != WT_NOTFOUND) return (ret); have_next = (ret == 0); if (!have_next && extra_cols != NULL) { WT_RET(__wt_config_init(session, &config, extra_cols)); WT_RET(__wt_config_next(&config, &next_k, &next_v)); have_next = 1; extra_cols = NULL; } if ((ret = __find_column_format(session, table, &k, value_only, &pv)) != 0) { if (value_only && ret == EINVAL) WT_RET_MSG(session, EINVAL, "A column group cannot store key column " "'%.*s' in its value", (int)k.len, k.str); WT_RET_MSG(session, EINVAL, "Column '%.*s' not found", (int)k.len, k.str); } /* * Check whether we're moving an unsized WT_ITEM from the end * to the middle, or vice-versa. This determines whether the * size needs to be prepended. This is the only case where the * destination size can be larger than the source size. */ if (pv.type == 'u' && !pv.havesize && have_next) pv.type = 'U'; else if (pv.type == 'U' && !have_next) pv.type = 'u'; if (pv.havesize) WT_RET(__wt_buf_catfmt( session, format, "%d%c", (int)pv.size, pv.type)); else WT_RET(__wt_buf_catfmt(session, format, "%c", pv.type)); } while (have_next); return (0); }
/* * __wt_config_subgetraw -- * Get the value for a given key from a config string in a WT_CONFIG_ITEM. * This is useful for dealing with nested structs in config strings. */ int __wt_config_subgetraw(WT_SESSION_IMPL *session, WT_CONFIG_ITEM *cfg, WT_CONFIG_ITEM *key, WT_CONFIG_ITEM *value) { WT_CONFIG cparser; WT_RET(__wt_config_initn(session, &cparser, cfg->str, cfg->len)); return (__config_getraw(&cparser, key, value, true)); }
/* * __wt_config_init -- * Initialize a config handle, used to iterate through a NUL-terminated * config string. */ int __wt_config_init(WT_SESSION_IMPL *session, WT_CONFIG *conf, const char *str) { size_t len; len = (str == NULL) ? 0 : strlen(str); return (__wt_config_initn(session, conf, str, len)); }
/* * 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_struct_reformat -- * Given a table and a list of columns (which could be values in a column * group or index keys), calculate the resulting new format string. * The result will be appended to the format buffer. */ int __wt_struct_reformat(WT_SESSION_IMPL *session, WT_TABLE *table, const char *columns, size_t len, const char *extra_cols, bool value_only, WT_ITEM *format) { WT_CONFIG config; WT_CONFIG_ITEM k, next_k, next_v; WT_DECL_PACK_VALUE(pv); WT_DECL_RET; bool have_next; __wt_config_initn(session, &config, columns, len); /* * If an empty column list is specified, this will fail with * WT_NOTFOUND, that's okay. */ WT_RET_NOTFOUND_OK(ret = __wt_config_next(&config, &next_k, &next_v)); if (ret == WT_NOTFOUND) { if (extra_cols != NULL) { __wt_config_init(session, &config, extra_cols); WT_RET(__wt_config_next(&config, &next_k, &next_v)); extra_cols = NULL; } else if (format->size == 0) { WT_RET(__wt_buf_set(session, format, "", 1)); return (0); } } do { k = next_k; ret = __wt_config_next(&config, &next_k, &next_v); if (ret != 0 && ret != WT_NOTFOUND) return (ret); have_next = ret == 0; if (!have_next && extra_cols != NULL) { __wt_config_init(session, &config, extra_cols); WT_RET(__wt_config_next(&config, &next_k, &next_v)); have_next = true; extra_cols = NULL; } if ((ret = __find_column_format(session, table, &k, value_only, &pv)) != 0) { if (value_only && ret == EINVAL) WT_RET_MSG(session, EINVAL, "A column group cannot store key column " "'%.*s' in its value", (int)k.len, k.str); WT_RET_MSG(session, EINVAL, "Column '%.*s' not found", (int)k.len, k.str); } /* * Check whether we're moving an unsized WT_ITEM from the end * to the middle, or vice-versa. This determines whether the * size needs to be prepended. This is the only case where the * destination size can be larger than the source size. */ if (pv.type == 'u' && !pv.havesize && have_next) pv.type = 'U'; else if (pv.type == 'U' && !have_next) pv.type = 'u'; if (pv.havesize) WT_RET(__wt_buf_catfmt(session, format, "%" PRIu32 "%c", pv.size, pv.type)); else WT_RET(__wt_buf_catfmt(session, format, "%c", pv.type)); } while (have_next); return (0); }
/* * __wt_struct_plan -- * Given a table cursor containing a complete table, build the "projection * plan" to distribute the columns to dependent stores. A string * representing the plan will be appended to the plan buffer. */ int __wt_struct_plan(WT_SESSION_IMPL *session, WT_TABLE *table, const char *columns, size_t len, bool value_only, WT_ITEM *plan) { WT_CONFIG conf; WT_CONFIG_ITEM k, v; WT_DECL_RET; u_int cg, col, current_cg, current_col, i, start_cg, start_col; char coltype, current_coltype; bool have_it; start_cg = start_col = UINT_MAX; /* -Wuninitialized */ /* Work through the value columns by skipping over the key columns. */ __wt_config_initn(session, &conf, columns, len); if (value_only) for (i = 0; i < table->nkey_columns; i++) WT_RET(__wt_config_next(&conf, &k, &v)); current_cg = cg = 0; current_col = col = INT_MAX; current_coltype = coltype = WT_PROJ_KEY; /* Keep lint quiet. */ for (i = 0; (ret = __wt_config_next(&conf, &k, &v)) == 0; i++) { have_it = false; while ((ret = __find_next_col(session, table, &k, &cg, &col, &coltype)) == 0 && (!have_it || cg != start_cg || col != start_col)) { /* * First we move to the column. If that is in a * different column group to the last column we * accessed, or before the last column in the same * column group, or moving from the key to the value, * we need to switch column groups or rewind. */ if (current_cg != cg || current_col > col || current_coltype != coltype) { WT_ASSERT(session, !value_only || coltype == WT_PROJ_VALUE); WT_RET(__wt_buf_catfmt( session, plan, "%u%c", cg, coltype)); /* * Set the current column group and column * within the table. */ current_cg = cg; current_col = 0; current_coltype = coltype; } /* Now move to the column we want. */ if (current_col < col) { if (col - current_col > 1) WT_RET(__wt_buf_catfmt(session, plan, "%u", col - current_col)); WT_RET(__wt_buf_catfmt(session, plan, "%c", WT_PROJ_SKIP)); } /* * Now copy the value in / out. In the common case, * where each value is used in one column, we do a * "next" operation. If the value is used again, we do * a "reuse" operation to avoid making another copy. */ if (!have_it) { WT_RET(__wt_buf_catfmt(session, plan, "%c", WT_PROJ_NEXT)); start_cg = cg; start_col = col; have_it = true; } else WT_RET(__wt_buf_catfmt(session, plan, "%c", WT_PROJ_REUSE)); current_col = col + 1; } /* * We may fail to find a column if it is a custom extractor. * In that case, treat it as the first value column: we only * ever use such plans to extract the primary key from the * index. */ if (ret == WT_NOTFOUND) WT_RET(__wt_buf_catfmt(session, plan, "0%c%c", WT_PROJ_VALUE, WT_PROJ_NEXT)); } WT_RET_TEST(ret != WT_NOTFOUND, ret); /* Special case empty plans. */ if (i == 0 && plan->size == 0) WT_RET(__wt_buf_set(session, plan, "", 1)); return (0); }
/* * __wt_struct_plan -- * Given a table cursor containing a complete table, build the "projection * plan" to distribute the columns to dependent stores. A string * representing the plan will be appended to the plan buffer. */ int __wt_struct_plan(WT_SESSION_IMPL *session, WT_TABLE *table, const char *columns, size_t len, int value_only, WT_ITEM *plan) { WT_BTREE *saved_btree; WT_CONFIG conf; WT_CONFIG_ITEM k, v; WT_DECL_RET; int cg, col, current_cg, current_col, start_cg, start_col; int i, have_it; char coltype, current_coltype; saved_btree = session->btree; start_cg = start_col = -1; /* -Wuninitialized */ /* Work through the value columns by skipping over the key columns. */ WT_ERR(__wt_config_initn(session, &conf, columns, len)); if (value_only) for (i = 0; i < table->nkey_columns; i++) WT_ERR(__wt_config_next(&conf, &k, &v)); current_cg = cg = 0; current_col = col = INT_MAX; current_coltype = coltype = WT_PROJ_KEY; /* Keep lint quiet. */ while (__wt_config_next(&conf, &k, &v) == 0) { have_it = 0; while (__find_next_col(session, table, &k, &cg, &col, &coltype) == 0 && (!have_it || cg != start_cg || col != start_col)) { /* * First we move to the column. If that is in a * different column group to the last column we * accessed, or before the last column in the same * column group, or moving from the key to the value, * we need to switch column groups or rewind. */ if (current_cg != cg || current_col > col || current_coltype != coltype) { WT_ASSERT(session, !value_only || coltype == WT_PROJ_VALUE); WT_ERR(__wt_buf_catfmt( session, plan, "%d%c", cg, coltype)); /* * Set the current column group and column * within the table. */ current_cg = cg; current_col = 0; current_coltype = coltype; } /* Now move to the column we want. */ if (current_col < col) { if (col - current_col > 1) WT_ERR(__wt_buf_catfmt(session, plan, "%d", col - current_col)); WT_ERR(__wt_buf_catfmt(session, plan, "%c", WT_PROJ_SKIP)); } /* * Now copy the value in / out. In the common case, * where each value is used in one column, we do a * "next" operation. If the value is used again, we do * a "reuse" operation to avoid making another copy. */ if (!have_it) { WT_ERR(__wt_buf_catfmt(session, plan, "%c", WT_PROJ_NEXT)); start_cg = cg; start_col = col; have_it = 1; } else WT_ERR(__wt_buf_catfmt(session, plan, "%c", WT_PROJ_REUSE)); current_col = col + 1; } } err: session->btree = saved_btree; return (ret); }
/* * __wt_config_subinit -- * Initialize a config handle, used to iterate through a config string * extracted from another config string (used for parsing nested * structures). */ int __wt_config_subinit( WT_SESSION_IMPL *session, WT_CONFIG *conf, WT_CONFIG_ITEM *item) { return (__wt_config_initn(session, conf, item->str, item->len)); }