/* * __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); }
/* * __rename_tree -- * Rename an index or colgroup reference. */ static int __rename_tree(WT_SESSION_IMPL *session, WT_TABLE *table, const char *newuri, const char *name, const char *cfg[]) { WT_CONFIG_ITEM cval; WT_DECL_ITEM(nn); WT_DECL_ITEM(ns); WT_DECL_ITEM(nv); WT_DECL_ITEM(os); WT_DECL_RET; bool is_colgroup; const char *newname, *olduri, *suffix; char *value; olduri = table->name; value = NULL; newname = newuri; (void)WT_PREFIX_SKIP(newname, "table:"); /* * Create the new data source URI and update the schema value. * * 'name' has the format (colgroup|index):<tablename>[:<suffix>]; * we need the suffix. */ is_colgroup = WT_PREFIX_MATCH(name, "colgroup:"); if (!is_colgroup && !WT_PREFIX_MATCH(name, "index:")) WT_ERR_MSG(session, EINVAL, "expected a 'colgroup:' or 'index:' source: '%s'", name); suffix = strchr(name, ':'); /* An existing table should have a well formed name. */ WT_ASSERT(session, suffix != NULL); suffix = strchr(suffix + 1, ':'); WT_ERR(__wt_scr_alloc(session, 0, &nn)); WT_ERR(__wt_buf_fmt(session, nn, "%s%s%s", is_colgroup ? "colgroup:" : "index:", newname, (suffix == NULL) ? "" : suffix)); /* Skip the colon, if any. */ if (suffix != NULL) ++suffix; /* Read the old schema value. */ WT_ERR(__wt_metadata_search(session, name, &value)); /* * Calculate the new data source URI. Use the existing table structure * and substitute the new name temporarily. */ WT_ERR(__wt_scr_alloc(session, 0, &ns)); table->name = newuri; if (is_colgroup) WT_ERR(__wt_schema_colgroup_source( session, table, suffix, value, ns)); else WT_ERR(__wt_schema_index_source( session, table, suffix, value, ns)); if ((ret = __wt_config_getones(session, value, "source", &cval)) != 0) WT_ERR_MSG(session, EINVAL, "index or column group has no data source: %s", value); /* Take a copy of the old data source. */ WT_ERR(__wt_scr_alloc(session, 0, &os)); WT_ERR(__wt_buf_fmt(session, os, "%.*s", (int)cval.len, cval.str)); /* Overwrite it with the new data source. */ WT_ERR(__wt_scr_alloc(session, 0, &nv)); WT_ERR(__wt_buf_fmt(session, nv, "%.*s%s%s", (int)WT_PTRDIFF(cval.str, value), value, (const char *)ns->data, cval.str + cval.len)); /* * Do the rename before updating the metadata to avoid leaving the * metadata inconsistent if the rename fails. */ WT_ERR(__wt_schema_rename(session, os->data, ns->data, cfg)); /* * Remove the old metadata entry. * Insert the new metadata entry. */ WT_ERR(__wt_metadata_remove(session, name)); WT_ERR(__wt_metadata_insert(session, nn->data, nv->data)); err: __wt_scr_free(session, &nn); __wt_scr_free(session, &ns); __wt_scr_free(session, &nv); __wt_scr_free(session, &os); __wt_free(session, value); table->name = olduri; 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 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); }