static int kvs_session_verify(WT_DATA_SOURCE *wtds, WT_SESSION *session, const char *uri, WT_CONFIG_ARG *config) { DATA_SOURCE *ds; DB *db; WT_EXTENSION_API *wt_api; int ret = 0; const char *name; (void)config; /* Unused parameters */ ds = (DATA_SOURCE *)wtds; wt_api = ds->wt_api; /* Get the object name */ if ((ret = uri2name(wt_api, session, uri, &name)) != 0) return (ret); if ((ret = single_thread(wtds, session, &ds->rwlock)) != 0) return (ret); if ((ret = db_create(&db, ds->dbenv, 0)) != 0) ESET(wt_api, session, WT_ERROR, "db_create: %s", db_strerror(ret)); else if ((ret = db->verify(db, name, NULL, NULL, 0)) != 0) ESET(wt_api, session, WT_ERROR, "Db.verify: %s: %s", uri, db_strerror(ret)); /* db handle is dead */ ETRET(unlock(wt_api, session, &ds->rwlock)); return (ret); }
/* * wiredtiger_extension_terminate -- * Shutdown the KVS connector code. */ int wiredtiger_extension_terminate(WT_CONNECTION *connection) { DATA_SOURCE *p; int ret, tret; (void)connection; /* Unused parameters */ ret = writelock(NULL, &global_lock); /* Start a flush on any open objects. */ for (p = data_source_head; p != NULL; p = p->next) if ((tret = kvs_commit(p->kvs)) != 0) ESET(NULL, WT_ERROR, "kvs_commit: %s: %s", p->uri, kvs_strerror(tret)); /* Complain if any of the objects are in use. */ for (p = data_source_head; p != NULL; p = p->next) if (p->open_cursors != 0) ESET(NULL, WT_ERROR, "%s: has open cursors during close", p->uri); /* Close and discard the remaining objects. */ while ((p = data_source_head) != NULL) { if ((tret = kvs_close(p->kvs)) != 0) ESET(NULL, WT_ERROR, "kvs_close: %s: %s", p->uri, kvs_strerror(tret)); data_source_head = p->next; free(p->uri); ETRET(lock_destroy(NULL, &p->lock)); free(p); } ETRET(unlock(NULL, &global_lock)); ETRET(lock_destroy(NULL, &global_lock)); wt_ext = NULL; return (ret); }
/* * drop_data_source -- * Drop a data source from our list, closing any underlying KVS handle. */ static int drop_data_source(WT_SESSION *session, const char *uri) { DATA_SOURCE *p, **ref; int ret; if ((ret = writelock(session, &global_lock)) != 0) return (ret); /* Search our list of objects for a match. */ for (ref = &data_source_head; (p = *ref) != NULL; p = p->next) if (strcmp(p->uri, uri) == 0) break; /* * If we don't find the URI in our object list, we're done. * If the object is in use (it has open cursors), we can't drop it. * Otherwise, drop it. */ if (p == NULL || p->open_cursors != 0) { if (p != NULL) ret = EBUSY; } else { if ((ret = kvs_close(p->kvs)) != 0) ESET(session, WT_ERROR, "kvs_close: %s: %s", uri, kvs_strerror(ret)); *ref = p->next; free(p->uri); ETRET(lock_destroy(session, &p->lock)); free(p); } ETRET(unlock(session, &global_lock)); return (ret); }
/* * kvs_recno_alloc -- * Allocate a new record number. */ static INLINE int kvs_recno_alloc(WT_CURSOR *wt_cursor) { struct kvs_record *r; CURSOR *cursor; WT_SESSION *session; int ret; cursor = (CURSOR *)wt_cursor; session = cursor->session; r = &cursor->record; /* Lock the data-source. */ if ((ret = writelock(session, &cursor->data_source->lock)) != 0) return (ret); /* * If not yet tracking the maximum record number, read the last record * from the object and figure it out. * * XXX * There is still a race here. If another thread of control performs a * WT_CURSOR::insert of an explicit record number (that is, the other * cursor isn't configured for "append"), we could race. If we figure * out the last record in the data-source is 37, then the other thread * explicitly writes record 37, things will go badly. I don't want to * lock the data-source on every update, so I'm leaving this until we * write the transactional code, because that's going to change all of * the locking in here. */ if (cursor->data_source->append_recno == 0) { if ((ret = kvs_last(cursor->data_source->kvs, &cursor->record, (unsigned long)0, (unsigned long)0)) != 0) goto err; if ((ret = wt_ext->struct_unpack(wt_ext, session, r->key, sizeof(r->key), "r", &cursor->data_source->append_recno)) != 0) goto err; } wt_cursor->recno = ++cursor->data_source->append_recno; err: ETRET(unlock(session, &cursor->data_source->lock)); return (ret); }
/* * kvs_session_truncate -- * WT_SESSION::truncate method. */ static int kvs_session_truncate(WT_DATA_SOURCE *dsrc, WT_SESSION *session, const char *uri, WT_CONFIG_ARG *config) { DATA_SOURCE *p; int ret, tret; (void)dsrc; /* Unused parameters */ /* * Truncate should work even if the object is not yet opened: if we * don't find it, open it. We loop because we could theoretically * race with other threads creating/deleting the object. */ for (;;) { if ((ret = writelock(session, &global_lock)) != 0) return (ret); /* Search our list of objects for a match. */ for (p = data_source_head; p != NULL; p = p->next) if (strcmp(p->uri, uri) == 0) break; /* If we don't find the object, open it. */ if (p == NULL) { if ((ret = unlock(session, &global_lock)) != 0) return (ret); if ((ret = open_data_source(session, uri, config)) != 0) return (ret); continue; } if (p->open_cursors == 0) { if ((tret = kvs_truncate(p->kvs)) != 0) ESET(NULL, WT_ERROR, "kvs_truncate: %s: %s", p->uri, kvs_strerror(tret)); } else ret = EBUSY; ETRET(unlock(session, &global_lock)); return (ret); } /* NOTREACHED */ }
static int kvs_terminate(WT_DATA_SOURCE *wtds, WT_SESSION *session) { DB_ENV *dbenv; DATA_SOURCE *ds; WT_EXTENSION_API *wt_api; int ret = 0; ds = (DATA_SOURCE *)wtds; wt_api = ds->wt_api; dbenv = ds->dbenv; if (dbenv != NULL && (ret = dbenv->close(dbenv, 0)) != 0) ESET(wt_api, session, WT_ERROR, "DbEnv.close: %s", db_strerror(ret)); ETRET(lock_destroy(wt_api, session, &ds->rwlock)); return (ret); }
static int kvs_session_truncate(WT_DATA_SOURCE *wtds, WT_SESSION *session, const char *uri, WT_CONFIG_ARG *config) { DATA_SOURCE *ds; DB *db; WT_EXTENSION_API *wt_api; int tret, ret = 0; const char *name; (void)config; /* Unused parameters */ ds = (DATA_SOURCE *)wtds; wt_api = ds->wt_api; /* Get the object name */ if ((ret = uri2name(wt_api, session, uri, &name)) != 0) return (ret); if ((ret = single_thread(wtds, session, &ds->rwlock)) != 0) return (ret); if ((ret = db_create(&db, ds->dbenv, 0)) != 0) ESET(wt_api, session, WT_ERROR, "db_create: %s", db_strerror(ret)); else { if ((ret = db->open(db, NULL, name, NULL, DB_UNKNOWN, DB_TRUNCATE, 0)) != 0) ESET(wt_api, session, WT_ERROR, "Db.open: %s", db_strerror(ret)); if ((tret = db->close(db, 0)) != 0) ESET(wt_api, session, WT_ERROR, "Db.close: %s", db_strerror(tret)); } ETRET(unlock(wt_api, session, &ds->rwlock)); return (ret); }
static int kvs_session_open_cursor(WT_DATA_SOURCE *wtds, WT_SESSION *session, const char *uri, WT_CONFIG_ARG *config, WT_CURSOR **new_cursor) { CURSOR_SOURCE *cursor; DATA_SOURCE *ds; DB *db; WT_CONFIG_ITEM v; WT_EXTENSION_API *wt_api; int locked, ret; const char *name; ds = (DATA_SOURCE *)wtds; wt_api = ds->wt_api; locked = 0; /* Get the object name */ if ((ret = uri2name(wt_api, session, uri, &name)) != 0) return (ret); /* Allocate the cursor */ if ((cursor = calloc(1, sizeof(CURSOR_SOURCE))) == NULL) return (os_errno()); cursor->ds = (DATA_SOURCE *)wtds; cursor->wt_api = wt_api; /* Parse configuration */ if ((ret = wt_api->config_get( wt_api, session, config, "append", &v)) != 0) { ESET(wt_api, session, ret, "append configuration: %s", wt_api->strerror(wt_api, session, ret)); goto err; } cursor->config_append = v.val != 0; if ((ret = wt_api->config_get( wt_api, session, config, "overwrite", &v)) != 0) { ESET(wt_api, session, ret, "overwrite configuration: %s", wt_api->strerror(wt_api, session, ret)); goto err; } cursor->config_overwrite = v.val != 0; if ((ret = wt_api->config_get( wt_api, session, config, "key_format", &v)) != 0) { ESET(wt_api, session, ret, "key_format configuration: %s", wt_api->strerror(wt_api, session, ret)); goto err; } cursor->config_recno = v.len == 1 && v.str[0] == 'r'; if ((ret = wt_api->config_get( wt_api, session, config, "value_format", &v)) != 0) { ESET(wt_api, session, ret, "value_format configuration: %s", wt_api->strerror(wt_api, session, ret)); goto err; } cursor->config_bitfield = v.len == 2 && isdigit((u_char)v.str[0]) && v.str[1] == 't'; if ((ret = writelock(wt_api, session, &ds->rwlock)) != 0) goto err; locked = 1; /* Open the Berkeley DB cursor */ if ((ret = db_create(&cursor->db, ds->dbenv, 0)) != 0) { ESET(wt_api, session, WT_ERROR, "db_create: %s", db_strerror(ret)); goto err; } db = cursor->db; if ((ret = db->open(db, NULL, name, NULL, cursor->config_recno ? DB_RECNO : DB_BTREE, DB_CREATE, 0)) != 0) { ESET(wt_api, session, WT_ERROR, "Db.open: %s", db_strerror(ret)); goto err; } if ((ret = db->cursor(db, NULL, &cursor->dbc, 0)) != 0) { ESET(wt_api, session, WT_ERROR, "Db.cursor: %s", db_strerror(ret)); goto err; } /* Initialize the methods */ cursor->wtcursor.next = kvs_cursor_next; cursor->wtcursor.prev = kvs_cursor_prev; cursor->wtcursor.reset = kvs_cursor_reset; cursor->wtcursor.search = kvs_cursor_search; cursor->wtcursor.search_near = kvs_cursor_search_near; cursor->wtcursor.insert = kvs_cursor_insert; cursor->wtcursor.update = kvs_cursor_update; cursor->wtcursor.remove = kvs_cursor_remove; cursor->wtcursor.close = kvs_cursor_close; *new_cursor = (WT_CURSOR *)cursor; ++ds->open_cursors; if (0) { err: free(cursor); } if (locked) ETRET(unlock(wt_api, session, &ds->rwlock)); return (ret); }
/* * open_data_source -- * Open a new data source and insert it into the list. */ static int open_data_source(WT_SESSION *session, const char *uri, WT_CONFIG_ARG *config) { struct kvs_config kvs_config; DATA_SOURCE *data_source, *p; int ds_lockinit, flags, locked, ret; char **devices, *emsg; devices = NULL; emsg = NULL; ds_lockinit = locked = ret = 0; /* * The first time we open a cursor on an object, allocate an underlying * data source object. */ if ((data_source = (DATA_SOURCE *)calloc(1, sizeof(DATA_SOURCE))) == NULL) return (os_errno()); if ((data_source->uri = strdup(uri)) == NULL) goto err; if ((ret = lock_init(session, &data_source->lock)) != 0) goto err; ds_lockinit = 1; /* Read the configuration. */ if ((ret = kvs_config_read( session, config, &devices, &kvs_config, &flags)) != 0) goto err; /* We require a list of devices underlying the URI. */ if (devices[0] == NULL) { ESET( session, EINVAL, "WT_SESSION.create: no devices specified"); goto err; } /* * kvs_open isn't re-entrant: lock things down while we make sure we * don't have more than a single handle at a time. */ if ((ret = writelock(session, &global_lock)) != 0) goto err; locked = 1; /* * Check for a match: if we find one, we raced, but we return success, * someone else did the work. */ for (p = data_source_head; p != NULL; p = p->next) if (strcmp(p->uri, uri) == 0) goto err; /* Open the KVS handle. */ if ((data_source->kvs = kvs_open(devices, &kvs_config, flags)) == NULL) { emsg = kvs_create_path_string(devices); ESET(session, WT_ERROR, "WT_SESSION.create: kvs_open: %s: %s", emsg == NULL ? devices[0] : emsg, kvs_strerror(os_errno())); goto err; } /* Insert the new entry at the head of the list. */ data_source->next = data_source_head; data_source_head = data_source; data_source = NULL; err: if (locked) ETRET(unlock(session, &global_lock)); if (data_source != NULL) { if (data_source->uri != NULL) free(data_source->uri); if (ds_lockinit) ETRET(lock_destroy(session, &data_source->lock)); free(data_source); } free(devices); free(emsg); return (ret); }