/* * __wt_backup_list_uri_append -- * Append a new file name to the list, allocate space as necessary. * Called via the schema_worker function. */ int __wt_backup_list_uri_append( WT_SESSION_IMPL *session, const char *name, int *skip) { WT_CURSOR_BACKUP *cb; char *value; cb = session->bkp_cursor; WT_UNUSED(skip); if (WT_PREFIX_MATCH(name, "log:")) { WT_RET(__backup_log_append(session, cb, 0)); return (0); } /* Add the metadata entry to the backup file. */ WT_RET(__wt_metadata_search(session, name, &value)); WT_RET_TEST( (fprintf(cb->bfp, "%s\n%s\n", name, value) < 0), __wt_errno()); __wt_free(session, value); /* Add file type objects to the list of files to be copied. */ if (WT_PREFIX_MATCH(name, "file:")) WT_RET(__backup_list_append(session, cb, name)); return (0); }
/* * __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); }
/* * __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); }
/* * __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); }
/* * __wt_statlog_dump_spinlock -- * Log the spin-lock statistics. */ int __wt_statlog_dump_spinlock(WT_CONNECTION_IMPL *conn, const char *tag) { WT_SPINLOCK *spin; WT_CONNECTION_STATS_SPINLOCK *p, *t; uint64_t block_manager, btree_page, ignore; u_int i, j; /* * Ignore rare acquisition of a spinlock using a base value of 10 per * second so we don't create graphs we don't care about. */ ignore = (uint64_t)(conn->stat_usecs / 1000000) * 10; /* Output the number of times each spinlock was acquired. */ block_manager = btree_page = 0; for (i = 0; i < WT_ELEMENTS(conn->spinlock_list); ++i) { if ((spin = conn->spinlock_list[i]) == NULL) continue; /* * There are two sets of spinlocks we aggregate, the btree page * locks and the block manager per-file locks. The reason is * the block manager locks grow with the number of files open * (and LSM and bloom filters can open a lot of files), and * there are 16 btree page locks and splitting them out has not * historically been that informative. */ if (strcmp(spin->name, "block manager") == 0) { block_manager += spin->counter; if (conn->stat_clear) spin->counter = 0; continue; } if (strcmp(spin->name, "btree page") == 0) { btree_page += spin->counter; if (conn->stat_clear) spin->counter = 0; continue; } WT_RET_TEST((fprintf(conn->stat_fp, "%s %" PRIu64 " %s spinlock %s: acquisitions\n", conn->stat_stamp, spin->counter <= ignore ? 0 : spin->counter, tag, spin->name) < 0), __wt_errno()); if (conn->stat_clear) spin->counter = 0; } WT_RET_TEST((fprintf(conn->stat_fp, "%s %" PRIu64 " %s spinlock %s: acquisitions\n", conn->stat_stamp, block_manager <= ignore ? 0 : block_manager, tag, "block manager") < 0), __wt_errno()); WT_RET_TEST((fprintf(conn->stat_fp, "%s %" PRIu64 " %s spinlock %s: acquisitions\n", conn->stat_stamp, btree_page <= ignore ? 0 : btree_page, tag, "btree page") < 0), __wt_errno()); /* * Output the number of times each location acquires its spinlock and * the blocking matrix. */ for (i = 0; i < WT_ELEMENTS(conn->spinlock_block); ++i) { p = &conn->spinlock_block[i]; if (p->name == NULL) continue; WT_RET_TEST((fprintf(conn->stat_fp, "%s %d %s spinlock %s acquired by %s(%d)\n", conn->stat_stamp, p->total <= ignore ? 0 : p->total, tag, p->name, p->file, p->line) < 0), __wt_errno()); if (conn->stat_clear) p->total = 0; for (j = 0; j < WT_ELEMENTS(conn->spinlock_block); ++j) { t = &conn->spinlock_block[j]; if (t->name == NULL) continue; WT_RET_TEST((fprintf(conn->stat_fp, "%s %d %s spinlock %s: %s(%d) blocked by %s(%d)\n", conn->stat_stamp, p->blocked[j] <= ignore ? 0 : p->blocked[j], tag, p->name, p->file, p->line, t->file, t->line) < 0), __wt_errno()); if (conn->stat_clear) p->blocked[j] = 0; } } WT_FULL_BARRIER(); /* Minimize the window. */ return (0); }