/* * __session_compact_worker -- * Worker function to do the actual compaction call. */ static int __session_compact_worker( WT_SESSION *wt_session, const char *uri, const char *config) { WT_DECL_RET; WT_SESSION_IMPL *session; session = (WT_SESSION_IMPL *)wt_session; SESSION_API_CALL(session, compact, config, cfg); WT_WITH_SCHEMA_LOCK(session, ret = __wt_schema_worker(session, uri, __wt_compact, cfg, 0)); err: API_END_NOTFOUND_MAP(session, ret); }
/* * __session_drop -- * WT_SESSION->drop method. */ static int __session_drop(WT_SESSION *wt_session, const char *uri, const char *config) { WT_DECL_RET; WT_SESSION_IMPL *session; session = (WT_SESSION_IMPL *)wt_session; SESSION_API_CALL(session, drop, config, cfg); WT_WITH_SCHEMA_LOCK(session, ret = __wt_schema_drop(session, uri, cfg)); err: /* Note: drop operations cannot be unrolled (yet?). */ API_END_NOTFOUND_MAP(session, ret); }
/* * __session_verify -- * WT_SESSION->verify method. */ static int __session_verify(WT_SESSION *wt_session, const char *uri, const char *config) { WT_SESSION_IMPL *session; int ret; session = (WT_SESSION_IMPL *)wt_session; SESSION_API_CALL(session, verify, config, cfg); ret = __wt_schema_worker(session, uri, cfg, __wt_verify, WT_BTREE_EXCLUSIVE | WT_BTREE_VERIFY); err: API_END_NOTFOUND_MAP(session, ret); }
/* * __session_rename -- * WT_SESSION->rename method. */ static int __session_rename(WT_SESSION *wt_session, const char *uri, const char *newname, const char *config) { WT_DECL_RET; WT_SESSION_IMPL *session; session = (WT_SESSION_IMPL *)wt_session; SESSION_API_CALL(session, rename, config, cfg); WT_WITH_SCHEMA_LOCK(session, ret = __wt_schema_rename(session, uri, newname, cfg)); err: API_END_NOTFOUND_MAP(session, ret); }
/* * __session_upgrade -- * WT_SESSION->upgrade method. */ static int __session_upgrade(WT_SESSION *wt_session, const char *uri, const char *config) { WT_SESSION_IMPL *session; int ret; session = (WT_SESSION_IMPL *)wt_session; SESSION_API_CALL(session, upgrade, config, cfg); ret = __wt_schema_worker(session, uri, cfg, __wt_upgrade, WT_BTREE_EXCLUSIVE | WT_BTREE_UPGRADE); err: API_END_NOTFOUND_MAP(session, ret); }
/* * __session_rollback_transaction -- * WT_SESSION->rollback_transaction method. */ static int __session_rollback_transaction(WT_SESSION *wt_session, const char *config) { WT_DECL_RET; WT_SESSION_IMPL *session; session = (WT_SESSION_IMPL *)wt_session; SESSION_API_CALL(session, rollback_transaction, config, cfg); WT_CSTAT_INCR(session, txn_rollback); WT_TRET(__session_reset_cursors(session)); WT_TRET(__wt_txn_rollback(session, cfg)); err: API_END(session); return (ret); }
/* * __session_create -- * WT_SESSION->create method. */ static int __session_create(WT_SESSION *wt_session, const char *uri, const char *config) { WT_DECL_RET; WT_SESSION_IMPL *session; session = (WT_SESSION_IMPL *)wt_session; SESSION_API_CALL(session, create, config, cfg); WT_UNUSED(cfg); /* Disallow objects in the WiredTiger name space. */ WT_ERR(__wt_schema_name_check(session, uri)); WT_WITH_SCHEMA_LOCK(session, ret = __wt_schema_create(session, uri, config)); err: API_END_NOTFOUND_MAP(session, ret); }
/* * __session_checkpoint -- * WT_SESSION->checkpoint method. */ static int __session_checkpoint(WT_SESSION *wt_session, const char *config) { WT_DECL_RET; WT_SESSION_IMPL *session; WT_TXN *txn; session = (WT_SESSION_IMPL *)wt_session; txn = &session->txn; WT_CSTAT_INCR(session, checkpoint); SESSION_API_CALL(session, checkpoint, config, cfg); /* * Checkpoints require a snapshot to write a transactionally consistent * snapshot of the data. * * We can't use an application's transaction: if it has uncommitted * changes, they will be written in the checkpoint and may appear after * a crash. * * Use a real snapshot transaction: we don't want any chance of the * snapshot being updated during the checkpoint. Eviction is prevented * from evicting anything newer than this because we track the oldest * transaction ID in the system that is not visible to all readers. */ if (F_ISSET(txn, TXN_RUNNING)) WT_ERR_MSG(session, EINVAL, "Checkpoint not permitted in a transaction"); /* * Reset open cursors. * * We do this here explicitly even though it will happen implicitly in * the call to begin_transaction for the checkpoint, in case some * implementation of WT_CURSOR::reset needs the schema lock. */ WT_ERR(__session_reset_cursors(session)); WT_WITH_SCHEMA_LOCK(session, ret = __wt_txn_checkpoint(session, cfg)); err: API_END_NOTFOUND_MAP(session, ret); }
/* * __session_begin_transaction -- * WT_SESSION->begin_transaction method. */ static int __session_begin_transaction(WT_SESSION *wt_session, const char *config) { WT_DECL_RET; WT_SESSION_IMPL *session; session = (WT_SESSION_IMPL *)wt_session; SESSION_API_CALL(session, begin_transaction, config, cfg); WT_CSTAT_INCR(session, txn_begin); if (!F_ISSET(S2C(session), WT_CONN_TRANSACTIONAL)) WT_ERR_MSG(session, EINVAL, "Database not configured for transactions"); if (F_ISSET(&session->txn, TXN_RUNNING)) WT_ERR_MSG(session, EINVAL, "Transaction already running"); WT_ERR(__session_reset_cursors(session)); ret = __wt_txn_begin(session, cfg); err: API_END(session); return (ret); }
/* * __session_open_cursor -- * WT_SESSION->open_cursor method. */ static int __session_open_cursor(WT_SESSION *wt_session, const char *uri, WT_CURSOR *to_dup, const char *config, WT_CURSOR **cursorp) { WT_SESSION_IMPL *session; int ret; session = (WT_SESSION_IMPL *)wt_session; SESSION_API_CALL(session, open_cursor, config, cfg); if (uri != NULL && to_dup != NULL) WT_ERR_MSG(session, EINVAL, "should be passed either a URI or a cursor, but not both"); if (to_dup != NULL) ret = __wt_cursor_dup(session, to_dup, config, cursorp); else if (WT_PREFIX_MATCH(uri, "colgroup:")) ret = __wt_curfile_open(session, uri, cfg, cursorp); else if (WT_PREFIX_MATCH(uri, "config:")) ret = __wt_curconfig_open(session, uri, cfg, cursorp); else if (WT_PREFIX_MATCH(uri, "file:")) ret = __wt_curfile_open(session, uri, cfg, cursorp); else if (WT_PREFIX_MATCH(uri, "index:")) ret = __wt_curindex_open(session, uri, cfg, cursorp); else if (WT_PREFIX_MATCH(uri, "statistics:")) ret = __wt_curstat_open(session, uri, cfg, cursorp); else if (WT_PREFIX_MATCH(uri, "table:")) ret = __wt_curtable_open(session, uri, cfg, cursorp); else { __wt_err(session, EINVAL, "Unknown cursor type '%s'", uri); ret = EINVAL; } err: API_END_NOTFOUND_MAP(session, ret); }
/* * __wt_session_compact -- * WT_SESSION.compact method. */ int __wt_session_compact( WT_SESSION *wt_session, const char *uri, const char *config) { WT_COMPACT_STATE compact; WT_CONFIG_ITEM cval; WT_DATA_SOURCE *dsrc; WT_DECL_RET; WT_SESSION_IMPL *session; u_int i; bool ignore_cache_size_set; ignore_cache_size_set = false; session = (WT_SESSION_IMPL *)wt_session; SESSION_API_CALL(session, compact, config, cfg); /* * The compaction thread should not block when the cache is full: it is * holding locks blocking checkpoints and once the cache is full, it can * spend a long time doing eviction. */ if (!F_ISSET(session, WT_SESSION_IGNORE_CACHE_SIZE)) { ignore_cache_size_set = true; F_SET(session, WT_SESSION_IGNORE_CACHE_SIZE); } /* In-memory ignores compaction operations. */ if (F_ISSET(S2C(session), WT_CONN_IN_MEMORY)) goto err; /* * Non-LSM object compaction requires checkpoints, which are impossible * in transactional contexts. Disallow in all contexts (there's no * reason for LSM to allow this, possible or not), and check now so the * error message isn't confusing. */ WT_ERR(__wt_txn_context_check(session, false)); /* Disallow objects in the WiredTiger name space. */ WT_ERR(__wt_str_name_check(session, uri)); if (!WT_PREFIX_MATCH(uri, "colgroup:") && !WT_PREFIX_MATCH(uri, "file:") && !WT_PREFIX_MATCH(uri, "index:") && !WT_PREFIX_MATCH(uri, "lsm:") && !WT_PREFIX_MATCH(uri, "table:")) { if ((dsrc = __wt_schema_get_source(session, uri)) != NULL) ret = dsrc->compact == NULL ? __wt_object_unsupported(session, uri) : dsrc->compact( dsrc, wt_session, uri, (WT_CONFIG_ARG *)cfg); else ret = __wt_bad_object_type(session, uri); goto err; } /* Setup the session handle's compaction state structure. */ memset(&compact, 0, sizeof(WT_COMPACT_STATE)); session->compact = &compact; /* Compaction can be time-limited. */ WT_ERR(__wt_config_gets(session, cfg, "timeout", &cval)); session->compact->max_time = (uint64_t)cval.val; __wt_epoch(session, &session->compact->begin); /* Find the types of data sources being compacted. */ WT_WITH_SCHEMA_LOCK(session, ret = __wt_schema_worker(session, uri, __compact_handle_append, __compact_uri_analyze, cfg, 0)); WT_ERR(ret); if (session->compact->lsm_count != 0) WT_ERR(__wt_schema_worker( session, uri, NULL, __wt_lsm_compact, cfg, 0)); if (session->compact->file_count != 0) WT_ERR(__compact_worker(session)); err: session->compact = NULL; for (i = 0; i < session->op_handle_next; ++i) { WT_WITH_DHANDLE(session, session->op_handle[i], WT_TRET(__compact_end(session))); WT_WITH_DHANDLE(session, session->op_handle[i], WT_TRET(__wt_session_release_dhandle(session))); } __wt_free(session, session->op_handle); session->op_handle_allocated = session->op_handle_next = 0; /* * Release common session resources (for example, checkpoint may acquire * significant reconciliation structures/memory). */ WT_TRET(__wt_session_release_resources(session)); if (ignore_cache_size_set) F_CLR(session, WT_SESSION_IGNORE_CACHE_SIZE); if (ret != 0) WT_STAT_CONN_INCR(session, session_table_compact_fail); else WT_STAT_CONN_INCR(session, session_table_compact_success); API_END_RET_NOTFOUND_MAP(session, ret); }
/* * __session_close -- * WT_SESSION->close method. */ static int __session_close(WT_SESSION *wt_session, const char *config) { WT_CONNECTION_IMPL *conn; WT_CURSOR *cursor; WT_DECL_RET; WT_SESSION_IMPL *session; int tret; conn = (WT_CONNECTION_IMPL *)wt_session->connection; session = (WT_SESSION_IMPL *)wt_session; SESSION_API_CALL(session, close, config, cfg); WT_UNUSED(cfg); /* Rollback any active transaction. */ if (F_ISSET(&session->txn, TXN_RUNNING)) WT_TRET(__session_rollback_transaction(wt_session, NULL)); /* Close all open cursors. */ while ((cursor = TAILQ_FIRST(&session->cursors)) != NULL) WT_TRET(cursor->close(cursor)); WT_ASSERT(session, session->ncursors == 0); /* * Acquire the schema lock: we may be closing btree handles. * * Note that in some special cases, the schema may already be locked * (e.g., if this session is an LSM tree worker and the tree is being * dropped). */ WT_WITH_SCHEMA_LOCK_OPT(session, tret = __session_close_cache(session)); WT_TRET(tret); /* Discard metadata tracking. */ __wt_meta_track_discard(session); /* Discard scratch buffers. */ __wt_scr_discard(session); /* Free transaction information. */ __wt_txn_destroy(session); /* Confirm we're not holding any hazard references. */ __wt_hazard_close(session); /* Free the reconciliation information. */ __wt_rec_destroy(session, &session->reconcile); /* Free the eviction exclusive-lock information. */ __wt_free(session, session->excl); /* Destroy the thread's mutex. */ if (session->cond != NULL) (void)__wt_cond_destroy(session, session->cond); /* The API lock protects opening and closing of sessions. */ __wt_spin_lock(session, &conn->api_lock); /* * Sessions are re-used, clear the structure: this code sets the active * field to 0, which will exclude the hazard array from review by the * eviction thread. Note: there's no serialization support around the * review of the hazard array, which means threads checking for hazard * references first check the active field (which may be 0) and then use * the hazard pointer (which cannot be NULL). For this reason, clear * the session structure carefully. * * We don't need to publish here, because regardless of the active field * being non-zero, the hazard reference is always valid. */ WT_SESSION_CLEAR(session); session = conn->default_session; /* * Decrement the count of active sessions if that's possible: a session * being closed may or may not be at the end of the array, step toward * the beginning of the array until we reach an active session. */ while (conn->sessions[conn->session_cnt - 1].active == 0) if (--conn->session_cnt == 0) break; __wt_spin_unlock(session, &conn->api_lock); err: API_END_NOTFOUND_MAP(session, ret); }
/* * __session_truncate -- * WT_SESSION->truncate method. */ static int __session_truncate(WT_SESSION *wt_session, const char *uri, WT_CURSOR *start, WT_CURSOR *stop, const char *config) { WT_SESSION_IMPL *session; int ret; session = (WT_SESSION_IMPL *)wt_session; SESSION_API_CALL(session, truncate, config, cfg); /* * If the URI is specified, we don't need a start/stop, if start/stop * is specified, we don't need a URI. * * If no URI is specified, and both cursors are specified, start/stop * must reference the same object. * * Any specified cursor must have been initialized. */ if ((uri == NULL && start == NULL && stop == NULL) || (uri != NULL && (start != NULL || stop != NULL))) WT_ERR_MSG(session, EINVAL, "the truncate method should be passed either a URI or " "start/stop cursors, but not both"); if (start != NULL && stop != NULL && strcmp(start->uri, stop->uri) != 0) WT_ERR_MSG(session, EINVAL, "truncate method cursors must reference the same object"); if ((start != NULL && !F_ISSET(start, WT_CURSTD_KEY_SET)) || (stop != NULL && !F_ISSET(stop, WT_CURSTD_KEY_SET))) WT_ERR_MSG(session, EINVAL, "the truncate method cursors must have their keys set"); if (uri == NULL) { /* * From a starting/stopping cursor to the begin/end of the * object is easy, walk the object. */ if (start == NULL) for (;;) { WT_ERR(stop->remove(stop)); if ((ret = stop->prev(stop)) != 0) { if (ret == WT_NOTFOUND) ret = 0; break; } } else for (;;) { WT_ERR(start->remove(start)); if (stop != NULL && start->equals(start, stop)) break; if ((ret = start->next(start)) != 0) { if (ret == WT_NOTFOUND) ret = 0; break; } } } else ret = __wt_schema_truncate(session, uri, cfg); err: API_END_NOTFOUND_MAP(session, ret); }
/* * __session_close -- * WT_SESSION->close method. */ static int __session_close(WT_SESSION *wt_session, const char *config) { WT_BTREE_SESSION *btree_session; WT_CONNECTION_IMPL *conn; WT_CURSOR *cursor; WT_SESSION_IMPL *session, **tp; int ret; conn = (WT_CONNECTION_IMPL *)wt_session->connection; session = (WT_SESSION_IMPL *)wt_session; SESSION_API_CALL(session, close, config, cfg); WT_UNUSED(cfg); while ((cursor = TAILQ_FIRST(&session->cursors)) != NULL) WT_TRET(cursor->close(cursor)); while ((btree_session = TAILQ_FIRST(&session->btrees)) != NULL) WT_TRET(__wt_session_remove_btree(session, btree_session, 0)); WT_TRET(__wt_schema_close_tables(session)); __wt_spin_lock(session, &conn->spinlock); /* Discard scratch buffers. */ __wt_scr_discard(session); /* Confirm we're not holding any hazard references. */ __wt_hazard_empty(session); /* Free the reconciliation information. */ __wt_rec_destroy(session); /* Free the eviction exclusive-lock information. */ __wt_free(session, session->excl); /* Destroy the thread's mutex. */ if (session->cond != NULL) (void)__wt_cond_destroy(session, session->cond); /* * Replace the session reference we're closing with the last entry in * the table, then clear the last entry. As far as the walk of the * server threads is concerned, it's OK if the session appears twice, * or if it doesn't appear at all, so these lines can race all they * want. */ for (tp = conn->sessions; *tp != session; ++tp) ; --conn->session_cnt; *tp = conn->sessions[conn->session_cnt]; conn->sessions[conn->session_cnt] = NULL; /* * Publish, making the session array entry available for re-use. There * must be a barrier here to ensure the cleanup above completes before * the entry is re-used. */ WT_PUBLISH(session->iface.connection, NULL); session = &conn->default_session; __wt_spin_unlock(session, &conn->spinlock); err: API_END_NOTFOUND_MAP(session, ret); }