diff options
Diffstat (limited to 'src/third_party/wiredtiger/src/config/config_collapse.c')
-rw-r--r-- | src/third_party/wiredtiger/src/config/config_collapse.c | 621 |
1 files changed, 290 insertions, 331 deletions
diff --git a/src/third_party/wiredtiger/src/config/config_collapse.c b/src/third_party/wiredtiger/src/config/config_collapse.c index 0c4dfe7c604..a53f2c7a974 100644 --- a/src/third_party/wiredtiger/src/config/config_collapse.c +++ b/src/third_party/wiredtiger/src/config/config_collapse.c @@ -10,405 +10,364 @@ /* * __wt_config_collapse -- - * Collapse a set of configuration strings into newly allocated memory. - * - * This function takes a NULL-terminated list of configuration strings (where - * the first one contains all the defaults and the values are in order from - * least to most preferred, that is, the default values are least preferred), - * and collapses them into newly allocated memory. The algorithm is to walk - * the first of the configuration strings, and for each entry, search all of - * the configuration strings for a final value, keeping the last value found. - * - * Notes: - * Any key not appearing in the first configuration string is discarded - * from the final result, because we'll never search for it. - * - * Nested structures aren't parsed. For example, imagine a configuration - * string contains "key=(k2=v2,k3=v3)", and a subsequent string has - * "key=(k4=v4)", the result will be "key=(k4=v4)", as we search for and - * use the final value of "key", regardless of field overlap or missing - * fields in the nested value. + * Collapse a set of configuration strings into newly allocated memory. This function takes a + * NULL-terminated list of configuration strings (where the first one contains all the defaults + * and the values are in order from least to most preferred, that is, the default values are + * least preferred), and collapses them into newly allocated memory. The algorithm is to walk + * the first of the configuration strings, and for each entry, search all of the configuration + * strings for a final value, keeping the last value found. Notes: Any key not appearing in the + * first configuration string is discarded from the final result, because we'll never search for + * it. Nested structures aren't parsed. For example, imagine a configuration string contains + * "key=(k2=v2,k3=v3)", and a subsequent string has "key=(k4=v4)", the result will be + * "key=(k4=v4)", as we search for and use the final value of "key", regardless of field overlap + * or missing fields in the nested value. */ int -__wt_config_collapse( - WT_SESSION_IMPL *session, const char **cfg, char **config_ret) +__wt_config_collapse(WT_SESSION_IMPL *session, const char **cfg, char **config_ret) { - WT_CONFIG cparser; - WT_CONFIG_ITEM k, v; - WT_DECL_ITEM(tmp); - WT_DECL_RET; - - *config_ret = NULL; - - WT_RET(__wt_scr_alloc(session, 0, &tmp)); - - __wt_config_init(session, &cparser, cfg[0]); - while ((ret = __wt_config_next(&cparser, &k, &v)) == 0) { - if (k.type != WT_CONFIG_ITEM_STRING && - k.type != WT_CONFIG_ITEM_ID) - WT_ERR_MSG(session, EINVAL, - "Invalid configuration key found: '%s'", k.str); - WT_ERR(__wt_config_get(session, cfg, &k, &v)); - /* Include the quotes around string keys/values. */ - if (k.type == WT_CONFIG_ITEM_STRING) { - --k.str; - k.len += 2; - } - if (v.type == WT_CONFIG_ITEM_STRING) { - --v.str; - v.len += 2; - } - WT_ERR(__wt_buf_catfmt(session, tmp, "%.*s=%.*s,", - (int)k.len, k.str, (int)v.len, v.str)); - } - - /* We loop until error, and the expected error is WT_NOTFOUND. */ - if (ret != WT_NOTFOUND) - goto err; - - /* - * If the caller passes us no valid configuration strings, we get here - * with no bytes to copy -- that's OK, the underlying string copy can - * handle empty strings. - * - * Strip any trailing comma. - */ - if (tmp->size != 0) - --tmp->size; - ret = __wt_strndup(session, tmp->data, tmp->size, config_ret); - -err: __wt_scr_free(session, &tmp); - return (ret); + WT_CONFIG cparser; + WT_CONFIG_ITEM k, v; + WT_DECL_ITEM(tmp); + WT_DECL_RET; + + *config_ret = NULL; + + WT_RET(__wt_scr_alloc(session, 0, &tmp)); + + __wt_config_init(session, &cparser, cfg[0]); + while ((ret = __wt_config_next(&cparser, &k, &v)) == 0) { + if (k.type != WT_CONFIG_ITEM_STRING && k.type != WT_CONFIG_ITEM_ID) + WT_ERR_MSG(session, EINVAL, "Invalid configuration key found: '%s'", k.str); + WT_ERR(__wt_config_get(session, cfg, &k, &v)); + /* Include the quotes around string keys/values. */ + if (k.type == WT_CONFIG_ITEM_STRING) { + --k.str; + k.len += 2; + } + if (v.type == WT_CONFIG_ITEM_STRING) { + --v.str; + v.len += 2; + } + WT_ERR(__wt_buf_catfmt(session, tmp, "%.*s=%.*s,", (int)k.len, k.str, (int)v.len, v.str)); + } + + /* We loop until error, and the expected error is WT_NOTFOUND. */ + if (ret != WT_NOTFOUND) + goto err; + + /* + * If the caller passes us no valid configuration strings, we get here + * with no bytes to copy -- that's OK, the underlying string copy can + * handle empty strings. + * + * Strip any trailing comma. + */ + if (tmp->size != 0) + --tmp->size; + ret = __wt_strndup(session, tmp->data, tmp->size, config_ret); + +err: + __wt_scr_free(session, &tmp); + return (ret); } /* * We need a character that can't appear in a key as a separator. */ -#undef SEP /* separator key, character */ -#define SEP "[" -#undef SEPC -#define SEPC '[' +#undef SEP /* separator key, character */ +#define SEP "[" +#undef SEPC +#define SEPC '[' /* - * Individual configuration entries, including a generation number used to make - * the qsort stable. + * Individual configuration entries, including a generation number used to make the qsort stable. */ typedef struct { - char *k, *v; /* key, value */ - size_t gen; /* generation */ - bool strip; /* remove the value */ + char *k, *v; /* key, value */ + size_t gen; /* generation */ + bool strip; /* remove the value */ } WT_CONFIG_MERGE_ENTRY; /* * The array of configuration entries. */ typedef struct { - size_t entries_allocated; /* allocated */ - size_t entries_next; /* next slot */ + size_t entries_allocated; /* allocated */ + size_t entries_next; /* next slot */ - WT_CONFIG_MERGE_ENTRY *entries; /* array of entries */ + WT_CONFIG_MERGE_ENTRY *entries; /* array of entries */ } WT_CONFIG_MERGE; /* * __config_merge_scan -- - * Walk a configuration string, inserting entries into the merged array. + * Walk a configuration string, inserting entries into the merged array. */ static int -__config_merge_scan(WT_SESSION_IMPL *session, - const char *key, const char *value, bool strip, WT_CONFIG_MERGE *cp) +__config_merge_scan( + WT_SESSION_IMPL *session, const char *key, const char *value, bool strip, WT_CONFIG_MERGE *cp) { - WT_CONFIG cparser; - WT_CONFIG_ITEM k, v; - WT_DECL_ITEM(kb); - WT_DECL_ITEM(vb); - WT_DECL_RET; - size_t len; - - WT_ERR(__wt_scr_alloc(session, 0, &kb)); - WT_ERR(__wt_scr_alloc(session, 0, &vb)); - - __wt_config_init(session, &cparser, value); - while ((ret = __wt_config_next(&cparser, &k, &v)) == 0) { - if (k.type != WT_CONFIG_ITEM_STRING && - k.type != WT_CONFIG_ITEM_ID) - WT_ERR_MSG(session, EINVAL, - "Invalid configuration key found: '%s'", k.str); - - /* Include the quotes around string keys/values. */ - if (k.type == WT_CONFIG_ITEM_STRING) { - --k.str; - k.len += 2; - } - if (v.type == WT_CONFIG_ITEM_STRING) { - --v.str; - v.len += 2; - } - - /* - * !!! - * We're using a JSON quote character to separate the names we - * create for nested structures. That's not completely safe as - * it's possible to quote characters in JSON such that a quote - * character appears as a literal character in a key name. In - * a few cases, applications can create their own key namespace - * (for example, shared library extension names), and therefore - * it's possible for an application to confuse us. Error if we - * we ever see a key with a magic character. - */ - for (len = 0; len < k.len; ++len) - if (k.str[len] == SEPC) - WT_ERR_MSG(session, EINVAL, - "key %.*s contains a '%c' separator " - "character", - (int)k.len, (char *)k.str, SEPC); - - /* Build the key/value strings. */ - WT_ERR(__wt_buf_fmt(session, - kb, "%s%s%.*s", - key == NULL ? "" : key, - key == NULL ? "" : SEP, - (int)k.len, k.str)); - WT_ERR(__wt_buf_fmt(session, - vb, "%.*s", (int)v.len, v.str)); - - /* - * If the value is a structure, recursively parse it. - * - * !!! - * Don't merge unless the structure has field names. WiredTiger - * stores checkpoint LSNs in the metadata file using nested - * structures without field names: "checkpoint_lsn=(1,0)", not - * "checkpoint_lsn=(file=1,offset=0)". The value type is still - * WT_CONFIG_ITEM_STRUCT, so we check for a field name in the - * value. - */ - if (v.type == WT_CONFIG_ITEM_STRUCT && - strchr(vb->data, '=') != NULL) { - WT_ERR(__config_merge_scan( - session, kb->data, vb->data, strip, cp)); - continue; - } - - /* Insert the value into the array. */ - WT_ERR(__wt_realloc_def(session, - &cp->entries_allocated, - cp->entries_next + 1, &cp->entries)); - WT_ERR(__wt_strndup(session, - kb->data, kb->size, &cp->entries[cp->entries_next].k)); - WT_ERR(__wt_strndup(session, - vb->data, vb->size, &cp->entries[cp->entries_next].v)); - cp->entries[cp->entries_next].gen = cp->entries_next; - cp->entries[cp->entries_next].strip = strip; - ++cp->entries_next; - } - WT_ERR_NOTFOUND_OK(ret); - -err: __wt_scr_free(session, &kb); - __wt_scr_free(session, &vb); - return (ret); + WT_CONFIG cparser; + WT_CONFIG_ITEM k, v; + WT_DECL_ITEM(kb); + WT_DECL_ITEM(vb); + WT_DECL_RET; + size_t len; + + WT_ERR(__wt_scr_alloc(session, 0, &kb)); + WT_ERR(__wt_scr_alloc(session, 0, &vb)); + + __wt_config_init(session, &cparser, value); + while ((ret = __wt_config_next(&cparser, &k, &v)) == 0) { + if (k.type != WT_CONFIG_ITEM_STRING && k.type != WT_CONFIG_ITEM_ID) + WT_ERR_MSG(session, EINVAL, "Invalid configuration key found: '%s'", k.str); + + /* Include the quotes around string keys/values. */ + if (k.type == WT_CONFIG_ITEM_STRING) { + --k.str; + k.len += 2; + } + if (v.type == WT_CONFIG_ITEM_STRING) { + --v.str; + v.len += 2; + } + + /* + * !!! + * We're using a JSON quote character to separate the names we + * create for nested structures. That's not completely safe as + * it's possible to quote characters in JSON such that a quote + * character appears as a literal character in a key name. In + * a few cases, applications can create their own key namespace + * (for example, shared library extension names), and therefore + * it's possible for an application to confuse us. Error if we + * we ever see a key with a magic character. + */ + for (len = 0; len < k.len; ++len) + if (k.str[len] == SEPC) + WT_ERR_MSG(session, EINVAL, + "key %.*s contains a '%c' separator " + "character", + (int)k.len, (char *)k.str, SEPC); + + /* Build the key/value strings. */ + WT_ERR(__wt_buf_fmt(session, kb, "%s%s%.*s", key == NULL ? "" : key, key == NULL ? "" : SEP, + (int)k.len, k.str)); + WT_ERR(__wt_buf_fmt(session, vb, "%.*s", (int)v.len, v.str)); + + /* + * If the value is a structure, recursively parse it. + * + * !!! + * Don't merge unless the structure has field names. WiredTiger + * stores checkpoint LSNs in the metadata file using nested + * structures without field names: "checkpoint_lsn=(1,0)", not + * "checkpoint_lsn=(file=1,offset=0)". The value type is still + * WT_CONFIG_ITEM_STRUCT, so we check for a field name in the + * value. + */ + if (v.type == WT_CONFIG_ITEM_STRUCT && strchr(vb->data, '=') != NULL) { + WT_ERR(__config_merge_scan(session, kb->data, vb->data, strip, cp)); + continue; + } + + /* Insert the value into the array. */ + WT_ERR( + __wt_realloc_def(session, &cp->entries_allocated, cp->entries_next + 1, &cp->entries)); + WT_ERR(__wt_strndup(session, kb->data, kb->size, &cp->entries[cp->entries_next].k)); + WT_ERR(__wt_strndup(session, vb->data, vb->size, &cp->entries[cp->entries_next].v)); + cp->entries[cp->entries_next].gen = cp->entries_next; + cp->entries[cp->entries_next].strip = strip; + ++cp->entries_next; + } + WT_ERR_NOTFOUND_OK(ret); + +err: + __wt_scr_free(session, &kb); + __wt_scr_free(session, &vb); + return (ret); } /* * __strip_comma -- - * Strip a trailing comma. + * Strip a trailing comma. */ static void __strip_comma(WT_ITEM *buf) { - if (buf->size != 0 && ((char *)buf->data)[buf->size - 1] == ',') - --buf->size; + if (buf->size != 0 && ((char *)buf->data)[buf->size - 1] == ',') + --buf->size; } /* * __config_merge_format_next -- - * Walk the array, building entries. + * Walk the array, building entries. */ static int -__config_merge_format_next(WT_SESSION_IMPL *session, const char *prefix, - size_t plen, size_t *enp, WT_CONFIG_MERGE *cp, WT_ITEM *build) +__config_merge_format_next(WT_SESSION_IMPL *session, const char *prefix, size_t plen, size_t *enp, + WT_CONFIG_MERGE *cp, WT_ITEM *build) { - WT_CONFIG_MERGE_ENTRY *ep; - size_t len1, len2, next, saved_len; - const char *p; - - for (; *enp < cp->entries_next; ++*enp) { - ep = &cp->entries[*enp]; - len1 = strlen(ep->k); - - /* - * The entries are in sorted order, take the last entry for any - * key. - */ - if (*enp < (cp->entries_next - 1)) { - len2 = strlen((ep + 1)->k); - - /* Choose the last of identical keys. */ - if (len1 == len2 && - memcmp(ep->k, (ep + 1)->k, len1) == 0) - continue; - - /* - * The test is complicated by matching empty entries - * "foo=" against nested structures "foo,bar=", where - * the latter is a replacement for the former. - */ - if (len2 > len1 && - (ep + 1)->k[len1] == SEPC && - memcmp(ep->k, (ep + 1)->k, len1) == 0) - continue; - } - - /* - * If we're skipping a prefix and this entry doesn't match it, - * back off one entry and pop up a level. - */ - if (plen != 0 && - (plen > len1 || memcmp(ep->k, prefix, plen) != 0)) { - --*enp; - break; - } - - /* - * If the entry introduces a new level, recurse through that - * new level. - */ - if ((p = strchr(ep->k + plen, SEPC)) != NULL) { - /* Save the start location of the new level. */ - saved_len = build->size; - - next = WT_PTRDIFF(p, ep->k); - WT_RET(__wt_buf_catfmt(session, - build, "%.*s=(", (int)(next - plen), ep->k + plen)); - WT_RET(__config_merge_format_next( - session, ep->k, next + 1, enp, cp, build)); - __strip_comma(build); - WT_RET(__wt_buf_catfmt(session, build, "),")); - - /* - * It's possible the level contained nothing, check and - * discard empty levels. - */ - p = build->data; - if (p[build->size - 3] == '(') - build->size = saved_len; - - continue; - } - - /* Discard flagged entries. */ - if (ep->strip) - continue; - - /* Append the entry to the buffer. */ - WT_RET(__wt_buf_catfmt( - session, build, "%s=%s,", ep->k + plen, ep->v)); - } - - return (0); + WT_CONFIG_MERGE_ENTRY *ep; + size_t len1, len2, next, saved_len; + const char *p; + + for (; *enp < cp->entries_next; ++*enp) { + ep = &cp->entries[*enp]; + len1 = strlen(ep->k); + + /* + * The entries are in sorted order, take the last entry for any key. + */ + if (*enp < (cp->entries_next - 1)) { + len2 = strlen((ep + 1)->k); + + /* Choose the last of identical keys. */ + if (len1 == len2 && memcmp(ep->k, (ep + 1)->k, len1) == 0) + continue; + + /* + * The test is complicated by matching empty entries + * "foo=" against nested structures "foo,bar=", where + * the latter is a replacement for the former. + */ + if (len2 > len1 && (ep + 1)->k[len1] == SEPC && memcmp(ep->k, (ep + 1)->k, len1) == 0) + continue; + } + + /* + * If we're skipping a prefix and this entry doesn't match it, back off one entry and pop up + * a level. + */ + if (plen != 0 && (plen > len1 || memcmp(ep->k, prefix, plen) != 0)) { + --*enp; + break; + } + + /* + * If the entry introduces a new level, recurse through that new level. + */ + if ((p = strchr(ep->k + plen, SEPC)) != NULL) { + /* Save the start location of the new level. */ + saved_len = build->size; + + next = WT_PTRDIFF(p, ep->k); + WT_RET(__wt_buf_catfmt(session, build, "%.*s=(", (int)(next - plen), ep->k + plen)); + WT_RET(__config_merge_format_next(session, ep->k, next + 1, enp, cp, build)); + __strip_comma(build); + WT_RET(__wt_buf_catfmt(session, build, "),")); + + /* + * It's possible the level contained nothing, check and discard empty levels. + */ + p = build->data; + if (p[build->size - 3] == '(') + build->size = saved_len; + + continue; + } + + /* Discard flagged entries. */ + if (ep->strip) + continue; + + /* Append the entry to the buffer. */ + WT_RET(__wt_buf_catfmt(session, build, "%s=%s,", ep->k + plen, ep->v)); + } + + return (0); } /* * __config_merge_format -- - * Take the sorted array of entries, and format them into allocated memory. + * Take the sorted array of entries, and format them into allocated memory. */ static int -__config_merge_format( - WT_SESSION_IMPL *session, WT_CONFIG_MERGE *cp, const char **config_ret) +__config_merge_format(WT_SESSION_IMPL *session, WT_CONFIG_MERGE *cp, const char **config_ret) { - WT_DECL_ITEM(build); - WT_DECL_RET; - size_t entries; + WT_DECL_ITEM(build); + WT_DECL_RET; + size_t entries; - WT_RET(__wt_scr_alloc(session, 4 * 1024, &build)); + WT_RET(__wt_scr_alloc(session, 4 * 1024, &build)); - entries = 0; - WT_ERR(__config_merge_format_next(session, "", 0, &entries, cp, build)); + entries = 0; + WT_ERR(__config_merge_format_next(session, "", 0, &entries, cp, build)); - __strip_comma(build); + __strip_comma(build); - ret = __wt_strndup(session, build->data, build->size, config_ret); + ret = __wt_strndup(session, build->data, build->size, config_ret); -err: __wt_scr_free(session, &build); - return (ret); +err: + __wt_scr_free(session, &build); + return (ret); } /* * __config_merge_cmp -- - * Qsort function: sort the config merge array. + * Qsort function: sort the config merge array. */ static int WT_CDECL __config_merge_cmp(const void *a, const void *b) { - WT_CONFIG_MERGE_ENTRY *ae, *be; - int cmp; + WT_CONFIG_MERGE_ENTRY *ae, *be; + int cmp; - ae = (WT_CONFIG_MERGE_ENTRY *)a; - be = (WT_CONFIG_MERGE_ENTRY *)b; + ae = (WT_CONFIG_MERGE_ENTRY *)a; + be = (WT_CONFIG_MERGE_ENTRY *)b; - if ((cmp = strcmp(ae->k, be->k)) != 0) - return (cmp); - return (ae->gen > be->gen ? 1 : -1); + if ((cmp = strcmp(ae->k, be->k)) != 0) + return (cmp); + return (ae->gen > be->gen ? 1 : -1); } /* * __wt_config_merge -- - * Merge a set of configuration strings into newly allocated memory, - * optionally discarding configuration items. - * - * This function takes a NULL-terminated list of configuration strings (where - * the values are in order from least to most preferred), and merges them into - * newly allocated memory. The algorithm is to walk the configuration strings - * and build a table of each key/value pair. The pairs are sorted based on the - * name and the configuration string in which they were found, and a final - * configuration string is built from the result. Additionally, a configuration - * string can be specified and those configuration values are removed from the - * final string. - * - * Note: - * Nested structures are parsed and merged. For example, if configuration - * strings "key=(k1=v1,k2=v2)" and "key=(k1=v2)" appear, the result will - * be "key=(k1=v2,k2=v2)" because the nested values are merged. + * Merge a set of configuration strings into newly allocated memory, optionally discarding + * configuration items. This function takes a NULL-terminated list of configuration strings + * (where the values are in order from least to most preferred), and merges them into newly + * allocated memory. The algorithm is to walk the configuration strings and build a table of + * each key/value pair. The pairs are sorted based on the name and the configuration string in + * which they were found, and a final configuration string is built from the result. + * Additionally, a configuration string can be specified and those configuration values are + * removed from the final string. Note: Nested structures are parsed and merged. For example, if + * configuration strings "key=(k1=v1,k2=v2)" and "key=(k1=v2)" appear, the result will be + * "key=(k1=v2,k2=v2)" because the nested values are merged. */ int -__wt_config_merge(WT_SESSION_IMPL *session, - const char **cfg, const char *cfg_strip, const char **config_ret) - WT_GCC_FUNC_ATTRIBUTE((visibility("default"))) +__wt_config_merge(WT_SESSION_IMPL *session, const char **cfg, const char *cfg_strip, + const char **config_ret) WT_GCC_FUNC_ATTRIBUTE((visibility("default"))) { - WT_CONFIG_MERGE merge; - WT_DECL_RET; - size_t i; - - /* Start out with a reasonable number of entries. */ - WT_CLEAR(merge); - - WT_RET(__wt_realloc_def( - session, &merge.entries_allocated, 100, &merge.entries)); - - /* - * Scan the configuration strings, entering them into the array. The - * list of configuration values to be removed must be scanned last - * so their generation numbers are the highest. - */ - for (; *cfg != NULL; ++cfg) - WT_ERR(__config_merge_scan(session, NULL, *cfg, false, &merge)); - if (cfg_strip != NULL) - WT_ERR(__config_merge_scan( - session, NULL, cfg_strip, true, &merge)); - - /* - * Sort the array by key and, in the case of identical keys, by - * generation. - */ - __wt_qsort(merge.entries, merge.entries_next, - sizeof(WT_CONFIG_MERGE_ENTRY), __config_merge_cmp); - - /* Convert the array of entries into a string. */ - ret = __config_merge_format(session, &merge, config_ret); - -err: for (i = 0; i < merge.entries_next; ++i) { - __wt_free(session, merge.entries[i].k); - __wt_free(session, merge.entries[i].v); - } - __wt_free(session, merge.entries); - return (ret); + WT_CONFIG_MERGE merge; + WT_DECL_RET; + size_t i; + + /* Start out with a reasonable number of entries. */ + WT_CLEAR(merge); + + WT_RET(__wt_realloc_def(session, &merge.entries_allocated, 100, &merge.entries)); + + /* + * Scan the configuration strings, entering them into the array. The list of configuration + * values to be removed must be scanned last so their generation numbers are the highest. + */ + for (; *cfg != NULL; ++cfg) + WT_ERR(__config_merge_scan(session, NULL, *cfg, false, &merge)); + if (cfg_strip != NULL) + WT_ERR(__config_merge_scan(session, NULL, cfg_strip, true, &merge)); + + /* + * Sort the array by key and, in the case of identical keys, by generation. + */ + __wt_qsort( + merge.entries, merge.entries_next, sizeof(WT_CONFIG_MERGE_ENTRY), __config_merge_cmp); + + /* Convert the array of entries into a string. */ + ret = __config_merge_format(session, &merge, config_ret); + +err: + for (i = 0; i < merge.entries_next; ++i) { + __wt_free(session, merge.entries[i].k); + __wt_free(session, merge.entries[i].v); + } + __wt_free(session, merge.entries); + return (ret); } |