diff --git a/CMakeLists.txt b/CMakeLists.txt index 95b116a224216533503f5652b12a6453c9f5f59b..9829bbdf0393fd398ef1584d8a8c7194624c2946 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -300,6 +300,7 @@ add_dependencies(build_bundled_libs misc) include(BuildSophia) sophia_build() +set (sophia_lib "${PROJECT_BINARY_DIR}/third_party/sophia/db/libsophia.a") option(ENABLE_RPM "Enable install of a RPM specific files" OFF) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index be7eb5ba1dcd1ededc9a9fc97614d68a407876d2..83c0aef69da56967490b50c19a29e58d00382f3e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -152,7 +152,11 @@ set(TARANTOOL_CXX_FLAGS ${CMAKE_CXX_FLAGS} PARENT_SCOPE) add_executable(tarantool tarantool.cc) add_dependencies(tarantool build_bundled_libs) -target_link_libraries(tarantool box ${common_libraries} -rdynamic) +target_link_libraries(tarantool + box + ${sophia_lib} + ${common_libraries} + -rdynamic) if (module_link_flags) set_target_properties(tarantool PROPERTIES diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index beea2c89dd5bd085e846421e1e26ccb9ef4f4fe1..600f848a2bc544640eee935a9a8ea2325f653719 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -4,6 +4,8 @@ endif() file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src/box/lua) +include_directories(${SOPHIA_INCLUDE_DIR}) + set(lua_sources) lua_source(lua_sources lua/schema.lua) lua_source(lua_sources lua/box.lua) @@ -27,6 +29,8 @@ add_library(box bitset_index.cc engine.cc engine_memtx.cc + engine_sophia.cc + sophia_index.cc space.cc alter.cc schema.cc @@ -42,4 +46,3 @@ add_library(box lua/slab.cc lua/index.cc lua/space.cc) - diff --git a/src/box/alter.cc b/src/box/alter.cc index f19e311d4c450c31e43845953da6d069074758b3..aec77035a1e5d7d81ecda2b3d535f7646ddc24fb 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -596,11 +596,7 @@ DropIndex::commit(struct alter_space *alter) Index *pk = index_find(alter->old_space, 0); if (pk == NULL) return; - struct iterator *it = pk->position(); - pk->initIterator(it, ITER_ALL, NULL, 0); - struct tuple *tuple; - while ((tuple = it->next(it))) - tuple_ref(tuple, -1); + alter->old_space->engine->factory->dropIndex(pk); } /** Change non-essential (no data change) properties of an index. */ diff --git a/src/box/box.cc b/src/box/box.cc index 4952ee201d197be84b27c543544d76352f8741a4..ae93421c29fe0eb0e76484e2101c0c6904fea6e8 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -46,6 +46,7 @@ #include "schema.h" #include "engine.h" #include "engine_memtx.h" +#include "engine_sophia.h" #include "space.h" #include "port.h" #include "request.h" @@ -315,6 +316,10 @@ engine_init() { MemtxFactory *memtx = new MemtxFactory(); engine_register(memtx); + + SophiaFactory *sophia = new SophiaFactory(); + sophia->init(); + engine_register(sophia); } void diff --git a/src/box/engine.cc b/src/box/engine.cc index 578e5ca00164c559171399dba09d1d136537629d..8efcc17516af982092311fe2c3456bc6b2b20d90 100644 --- a/src/box/engine.cc +++ b/src/box/engine.cc @@ -46,6 +46,12 @@ void EngineFactory::init() void EngineFactory::shutdown() {} +void EngineFactory::txnFinish(struct txn*) +{} + +void EngineFactory::recoveryEvent(enum engine_recovery_event) +{} + Engine::Engine(EngineFactory *f) :factory(f) { diff --git a/src/box/engine.h b/src/box/engine.h index fcb2462fd79e1181f1f9d06b60a5277e892fdb1e..21c586c41994540fc13e3123b4a1f28d9280b368 100644 --- a/src/box/engine.h +++ b/src/box/engine.h @@ -58,6 +58,15 @@ enum engine_recovery_state { READY_ALL_KEYS }; +/** + * Engine specific recovery events that represents + * global recovery stage change. + */ +enum engine_recovery_event { + END_RECOVERY_SNAPSHOT, + END_RECOVERY +}; + typedef void (*engine_recover_f)(struct space*); typedef struct tuple * @@ -83,7 +92,7 @@ class EngineFactory: public Object { virtual ~EngineFactory() {} /** Called once at startup. */ virtual void init(); - /** Called at server shutdown */ + /** Called at server shutdown. */ virtual void shutdown(); /** Create a new engine instance for a space. */ virtual Engine *open() = 0; @@ -92,6 +101,19 @@ class EngineFactory: public Object { * space. */ virtual Index *createIndex(struct key_def*) = 0; + /* + * Delete all tuples in the index on drop. + */ + virtual void dropIndex(Index*) = 0; + /** + * Check a key definition for violation of + * various limits. + */ + virtual void keydefCheck(struct key_def *key_def) = 0; + /* Clean transaction engine resources. */ + virtual void txnFinish(struct txn *txn); + /* Inform engine about a recovery stage change. */ + virtual void recoveryEvent(enum engine_recovery_event); public: /** Name of the engine. */ const char *name; @@ -100,7 +122,7 @@ class EngineFactory: public Object { struct rlist link; }; -/** Engine handle - an operator of a space */ +/** Engine handle - an operator of a space */ struct Engine: public Object { public: diff --git a/src/box/engine_memtx.cc b/src/box/engine_memtx.cc index f99d06b0a20ef0c86d202e1fa3af53e3cbee2a10..cb8fab1c7d87b54c46117546c8029133787ae275 100644 --- a/src/box/engine_memtx.cc +++ b/src/box/engine_memtx.cc @@ -46,7 +46,7 @@ struct Memtx: public Engine { virtual ~Memtx() { /* do nothing */ - /* engine->close(this); */ + /* factory->close(this); */ } }; @@ -62,6 +62,7 @@ struct Memtx: public Engine { * 2) when all XLOGs are loaded: * recover = space_build_all_keys */ + static inline void memtx_recovery_prepare(struct engine_recovery *r) { @@ -76,6 +77,19 @@ MemtxFactory::MemtxFactory() memtx_recovery_prepare(&recovery); } +void +MemtxFactory::recoveryEvent(enum engine_recovery_event event) +{ + switch (event) { + case END_RECOVERY_SNAPSHOT: + recovery.recover = space_build_primary_key; + break; + case END_RECOVERY: + recovery.recover = space_build_all_keys; + break; + } +} + Engine *MemtxFactory::open() { return new Memtx(this); @@ -96,3 +110,50 @@ MemtxFactory::createIndex(struct key_def *key_def) return NULL; } } + +void +MemtxFactory::dropIndex(Index *index) +{ + struct iterator *it = index->position(); + index->initIterator(it, ITER_ALL, NULL, 0); + struct tuple *tuple; + while ((tuple = it->next(it))) + tuple_ref(tuple, -1); +} + +void +MemtxFactory::keydefCheck(struct key_def *key_def) +{ + switch (key_def->type) { + case HASH: + if (! key_def->is_unique) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) key_def->iid, + (unsigned) key_def->space_id, + "HASH index must be unique"); + } + break; + case TREE: + /* TREE index has no limitations. */ + break; + case BITSET: + if (key_def->part_count != 1) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) key_def->iid, + (unsigned) key_def->space_id, + "BITSET index key can not be multipart"); + } + if (key_def->is_unique) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) key_def->iid, + (unsigned) key_def->space_id, + "BITSET can not be unique"); + } + break; + default: + tnt_raise(ClientError, ER_INDEX_TYPE, + (unsigned) key_def->iid, + (unsigned) key_def->space_id); + break; + } +} diff --git a/src/box/engine_memtx.h b/src/box/engine_memtx.h index 14b8871fc2e361ce34882119af795e9cac80daad..e3ea0c0fa41094536414a4f445c453eeac4e2dfe 100644 --- a/src/box/engine_memtx.h +++ b/src/box/engine_memtx.h @@ -33,6 +33,9 @@ struct MemtxFactory: public EngineFactory { MemtxFactory(); virtual Engine *open(); virtual Index *createIndex(struct key_def *key_def); + virtual void dropIndex(Index *index); + virtual void keydefCheck(struct key_def *key_def); + virtual void recoveryEvent(enum engine_recovery_event event); }; #endif /* TARANTOOL_BOX_ENGINE_MEMTX_H_INCLUDED */ diff --git a/src/box/engine_sophia.cc b/src/box/engine_sophia.cc new file mode 100644 index 0000000000000000000000000000000000000000..3a06c76c0582600a10716d9c40cd508cd6cfa791 --- /dev/null +++ b/src/box/engine_sophia.cc @@ -0,0 +1,180 @@ +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "txn.h" +#include "tuple.h" +#include "engine.h" +#include "engine_sophia.h" +#include "index.h" +#include "sophia_index.h" +#include "space.h" +#include "exception.h" +#include "salad/rlist.h" +#include <sophia.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <errno.h> + +struct Sophia: public Engine { + Sophia(EngineFactory*); +}; + +Sophia::Sophia(EngineFactory *e) + :Engine(e) +{ } + +static struct tuple * +sophia_replace_noop(struct space*, + struct tuple*, struct tuple*, + enum dup_replace_mode) +{ + return NULL; +} + +static void +sophia_end_build_primary_key(struct space *space) +{ + engine_recovery *r = &space->engine->recovery; + /* enable replace */ + r->state = READY_ALL_KEYS; + r->replace = space_replace_primary_key; + r->recover = space_noop; +} + +static void +sophia_begin_build_primary_key(struct space *space) +{ + engine_recovery *r = &space->engine->recovery; + r->replace = sophia_replace_noop; + r->recover = sophia_end_build_primary_key; +} + +static inline void +sophia_recovery_prepare(struct engine_recovery *r) +{ + r->state = READY_NO_KEYS; + r->recover = sophia_begin_build_primary_key; + r->replace = space_replace_no_keys; +} + +SophiaFactory::SophiaFactory() + :EngineFactory("sophia") +{ + sophia_recovery_prepare(&recovery); +} + +void +SophiaFactory::init() +{ + int rc = mkdir("sophia", 0755); + + if (rc == -1 && errno != EEXIST) { + say_error("failed to create directory: 'sophia', %d, %s", + errno, strerror(errno)); + } +} + +Engine* +SophiaFactory::open() +{ + return new Sophia(this); +} + +void +SophiaFactory::recoveryEvent(enum engine_recovery_event event) +{ + switch (event) { + case END_RECOVERY_SNAPSHOT: + recovery.replace = sophia_replace_noop; + break; + case END_RECOVERY: + recovery.state = READY_NO_KEYS; + recovery.replace = space_replace_primary_key; + recovery.recover = space_noop; + break; + } +} + +Index* +SophiaFactory::createIndex(struct key_def *key_def) +{ + switch (key_def->type) { + case TREE: return new SophiaIndex(key_def); + default: + assert(false); + return NULL; + } +} + +void +SophiaFactory::dropIndex(Index *index) +{ + (void)index; +} + +void +SophiaFactory::keydefCheck(struct key_def *key_def) +{ + switch (key_def->type) { + case TREE: + if (! key_def->is_unique) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) key_def->iid, + (unsigned) key_def->space_id, + "Sophia TREE index must be unique"); + } + if (key_def->iid != 0) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) key_def->iid, + (unsigned) key_def->space_id, + "Sophia TREE secondary indexes are not supported"); + } + if (key_def->part_count != 1) { + tnt_raise(ClientError, ER_MODIFY_INDEX, + (unsigned) key_def->iid, + (unsigned) key_def->space_id, + "Sophia TREE index key can not be multipart"); + } + break; + default: + tnt_raise(ClientError, ER_INDEX_TYPE, + (unsigned) key_def->iid, + (unsigned) key_def->space_id); + break; + } +} + +void +SophiaFactory::txnFinish(struct txn *txn) +{ + if (txn->new_tuple) + tuple_ref(txn->new_tuple, -1); +} diff --git a/src/box/engine_sophia.h b/src/box/engine_sophia.h new file mode 100644 index 0000000000000000000000000000000000000000..c4320ada4517af084c519315d18786347262d9e0 --- /dev/null +++ b/src/box/engine_sophia.h @@ -0,0 +1,43 @@ +#ifndef TARANTOOL_BOX_ENGINE_SOPHIA_H_INCLUDED +#define TARANTOOL_BOX_ENGINE_SOPHIA_H_INCLUDED +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +struct SophiaFactory: public EngineFactory { + SophiaFactory(); + virtual void init(); + virtual Engine *open(); + virtual Index *createIndex(struct key_def *key_def); + virtual void dropIndex(Index *index); + virtual void keydefCheck(struct key_def *key_def); + virtual void txnFinish(struct txn *txn); + virtual void recoveryEvent(enum engine_recovery_event event); +}; + +#endif /* TARANTOOL_BOX_ENGINE_SOPHIA_H_INCLUDED */ diff --git a/src/box/index.cc b/src/box/index.cc index 4860452c3ea492598dd5419c8e77ba9a3d040cfa..13f33bc962b97ffa001b0de7ce8b2f0bf648a039 100644 --- a/src/box/index.cc +++ b/src/box/index.cc @@ -27,6 +27,11 @@ * SUCH DAMAGE. */ #include "index.h" +#include "hash_index.h" +#include "tree_index.h" +#include "bitset_index.h" +#include "sophia_index.h" +#include "tuple.h" #include "say.h" #include "exception.h" diff --git a/src/box/index.h b/src/box/index.h index 862caa578178aa0248626f1ca0c3c3fef520d8c2..6adb06236138fd1ba60e85e684329b81bfb26f32 100644 --- a/src/box/index.h +++ b/src/box/index.h @@ -83,8 +83,15 @@ iterator_type_is_reverse(enum iterator_type type) struct iterator { struct tuple *(*next)(struct iterator *); void (*free)(struct iterator *); + void (*close)(struct iterator *); }; +static inline void +iterator_close(struct iterator *it) { + if (it->close) + it->close(it); +} + /** * Check that the key has correct part count and correct part size * for use in an index iterator. @@ -142,6 +149,11 @@ class Index: public Object { */ Index(struct key_def *key_def); + /* + * Pre-allocated iterator to speed up the main case of + * box_process(). Should not be used elsewhere. + */ + struct iterator *m_position; public: virtual ~Index(); @@ -180,12 +192,6 @@ class Index: public Object { m_position = allocIterator(); return m_position; } -private: - /* - * Pre-allocated iterator to speed up the main case of - * box_process(). Should not be used elsewhere. - */ - struct iterator *m_position; }; /** diff --git a/src/box/key_def.cc b/src/box/key_def.cc index 57a396eb3f3b9548b3877ca09cb24097dd96cda0..78cc0c0eed2bd8eacfc807403c3e6cc35d6cb7f3 100644 --- a/src/box/key_def.cc +++ b/src/box/key_def.cc @@ -27,6 +27,8 @@ * SUCH DAMAGE. */ #include "key_def.h" +#include "space.h" +#include "schema.h" #include <stdlib.h> #include <stdio.h> #include "exception.h" @@ -184,38 +186,11 @@ key_def_check(struct key_def *key_def) } } } - switch (key_def->type) { - case HASH: - if (! key_def->is_unique) { - tnt_raise(ClientError, ER_MODIFY_INDEX, - (unsigned) key_def->iid, - (unsigned) key_def->space_id, - "HASH index must be unique"); - } - break; - case TREE: - /* TREE index has no limitations. */ - break; - case BITSET: - if (key_def->part_count != 1) { - tnt_raise(ClientError, ER_MODIFY_INDEX, - (unsigned) key_def->iid, - (unsigned) key_def->space_id, - "BITSET index key can not be multipart"); - } - if (key_def->is_unique) { - tnt_raise(ClientError, ER_MODIFY_INDEX, - (unsigned) key_def->iid, - (unsigned) key_def->space_id, - "BITSET can not be unique"); - } - break; - default: - tnt_raise(ClientError, ER_INDEX_TYPE, - (unsigned) key_def->iid, - (unsigned) key_def->space_id); - break; - } + struct space *space = + space_cache_find(key_def->space_id); + + /* validate key_def->type */ + space->engine->factory->keydefCheck(key_def); } void diff --git a/src/box/key_def.h b/src/box/key_def.h index 1b45972719b9d6ebb170a428394885aab81ce482..de837c8d66e69373cdf5ffe63a21fbd3a81e0997 100644 --- a/src/box/key_def.h +++ b/src/box/key_def.h @@ -194,9 +194,7 @@ key_list_del_key(struct rlist *key_list, uint32_t id); /** * Check a key definition for violation of various limits. * - * @param id space id * @param key_def key_def - * @param type_str type name (to produce a nice error) */ void key_def_check(struct key_def *key_def); diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index a2e50ed5ee838f860cd4d81bc1297e924a659b75..00346e1cfbd606121a979316549ac17ca50bac26 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -8,6 +8,7 @@ ffi.cdef[[ struct iterator { struct tuple *(*next)(struct iterator *); void (*free)(struct iterator *); + void (*close)(struct iterator *); }; struct iterator * boxffi_index_iterator(uint32_t space_id, uint32_t index_id, int type, diff --git a/src/box/request.cc b/src/box/request.cc index 21bee5b6e782059e11aad5b2b97e2b6fd539afc1..b17eb91e68faa344693a3e4002832a8ac8646482 100644 --- a/src/box/request.cc +++ b/src/box/request.cc @@ -199,6 +199,8 @@ execute_select(struct request *request, struct txn *txn, struct port *port) struct iterator *it = index->position(); key_validate(index->key_def, type, key, part_count); index->initIterator(it, type, key, part_count); + auto iterator_guard = + make_scoped_guard([=] { iterator_close(it); }); struct tuple *tuple; while ((tuple = it->next(it)) != NULL) { diff --git a/src/box/schema.cc b/src/box/schema.cc index fb38919620f258ee44e4126726f4760bc8adb879..e93d744b4a28d764fbbe9916eccc54db378b7a63 100644 --- a/src/box/schema.cc +++ b/src/box/schema.cc @@ -303,7 +303,7 @@ static inline void space_end_recover_snapshot_cb(EngineFactory *f, void *udate) { (void)udate; - f->recovery.recover = space_build_primary_key; + f->recoveryEvent(END_RECOVERY_SNAPSHOT); } void @@ -328,7 +328,7 @@ static inline void space_end_recover_cb(EngineFactory *f, void *udate) { (void)udate; - f->recovery.recover = space_build_all_keys; + f->recoveryEvent(END_RECOVERY); } void diff --git a/src/box/sophia_index.cc b/src/box/sophia_index.cc new file mode 100644 index 0000000000000000000000000000000000000000..642e9439d105dcffb4702ed20155598e13f2cbab --- /dev/null +++ b/src/box/sophia_index.cc @@ -0,0 +1,355 @@ +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "sophia_index.h" +#include "say.h" +#include "tuple.h" +#include "pickle.h" +#include "scoped_guard.h" +#include "exception.h" +#include "errinj.h" + +#include "schema.h" +#include "space.h" + +#include <sophia.h> +#include <stdio.h> + +static inline int +sophia_index_compare(char *a, size_t asz __attribute__((unused)), + char *b, size_t bsz __attribute__((unused)), + void *arg) +{ + struct key_def *key_def = (struct key_def*)arg; + + int rc = tuple_compare_field(a, b, key_def->parts[0].type); + return (rc == 0) ? 0 : + ((rc > 0) ? 1 : -1); +} + +static struct tuple * +sophia_gettuple(void *db, const char *key, size_t keysize) +{ + size_t valuesize = 0; + char *value = NULL; + int rc = sp_get(db, key, keysize, (void**)&value, &valuesize); + if (rc == -1) + tnt_raise(ClientError, ER_SOPHIA, sp_error(db)); + if (rc == 0) + return NULL; + auto scoped_guard = make_scoped_guard([=] { free(value); }); + struct tuple *ret = + tuple_new(tuple_format_ber, value, value + valuesize); + tuple_ref(ret, 1); + return ret; +} + +/* {{{ SophiaIndex */ + +SophiaIndex::SophiaIndex(struct key_def *key_def_arg __attribute__((unused))) + : Index(key_def_arg) +{ + env = sp_env(); + if (env == NULL) + tnt_raise(ClientError, ER_MEMORY_ISSUE, sizeof(void*), + "SophiaIndex", "env"); + + auto env_freer = + make_scoped_guard([=] { sp_destroy(env); }); + + int rc = sp_ctl(env, SPCMP, sophia_index_compare, key_def); + if (rc == -1) + tnt_raise(ClientError, ER_SOPHIA, sp_error(env)); + + char path[PATH_MAX]; + snprintf(path, sizeof(path), "sophia/%04d", key_def->space_id); + rc = sp_ctl(env, SPDIR, SPO_RDWR|SPO_CREAT, path); + if (rc == -1) + tnt_raise(ClientError, ER_SOPHIA, sp_error(env)); + + say_info("start sophia space '%s' recover", path); + + db = sp_open(env); + if (db == NULL) + tnt_raise(ClientError, ER_SOPHIA, sp_error(env)); + + say_info("recover complete"); + + env_freer.is_active = false; +} + +SophiaIndex::~SophiaIndex() +{ + if (m_position != NULL) { + m_position->free(m_position); + m_position = NULL; + } + + if (db) { + int rc = sp_destroy(db); + if (rc == -1) + say_info("sophia space %d close error: %s", key_def->space_id, + sp_error(env)); + } + if (env) { + sp_destroy(env); + } +} + +void +SophiaIndex::endBuild() +{ +} + +size_t +SophiaIndex::size() const +{ + return 0; +} + +size_t +SophiaIndex::memsize() const +{ + return 0; +} + +struct tuple * +SophiaIndex::findByKey(const char *key, uint32_t part_count) const +{ + assert(part_count == 1); + assert(key_def->is_unique && part_count == key_def->part_count); + const char *keyptr = key; + mp_next(&keyptr); + size_t keysize = keyptr - key; + struct tuple *ret = sophia_gettuple(db, key, keysize); + return ret; +} + +static inline uint32_t +sophia_check_dup(struct key_def *key_def, + struct tuple *old_tuple, + struct tuple *dup_tuple, enum dup_replace_mode mode) +{ + if (dup_tuple == NULL) { + if (mode == DUP_REPLACE) { + /* + * dup_replace_mode is DUP_REPLACE, and + * a tuple with the same key is not found. + */ + return ER_TUPLE_NOT_FOUND; + } + } else { /* dup_tuple != NULL */ + + int equal = old_tuple != NULL && + tuple_compare(dup_tuple, old_tuple, key_def) == 0; + + if (!equal && (old_tuple != NULL || mode == DUP_INSERT)) { + /* + * There is a duplicate of new_tuple, + * and it's not old_tuple: we can't + * possibly delete more than one tuple + * at once. + */ + return ER_TUPLE_FOUND; + } + } + return 0; +} + +struct tuple * +SophiaIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple, + enum dup_replace_mode mode) +{ + if (new_tuple) { + assert(new_tuple->refs == 0); + + const char *key = tuple_field(new_tuple, key_def->parts[0].fieldno); + const char *keyptr = key; + mp_next(&keyptr); + size_t keysize = keyptr - key; + + struct tuple *dup_tuple = sophia_gettuple(db, key, keysize); + + uint32_t errcode = + sophia_check_dup(key_def, old_tuple, dup_tuple, mode); + if (errcode) { + if (dup_tuple) + tuple_ref(dup_tuple, -1); + tnt_raise(ClientError, errcode, index_id(this)); + } + + int rc = sp_set(db, key, keysize, new_tuple->data, new_tuple->bsize); + if (rc == -1) { + if (dup_tuple) + tuple_ref(dup_tuple, -1); + tnt_raise(ClientError, ER_SOPHIA, sp_error(db)); + } + + if (dup_tuple) + return dup_tuple; + } + + if (old_tuple) { + /* delete */ + const char *key = tuple_field(old_tuple, key_def->parts[0].fieldno); + const char *keyptr = key; + mp_next(&keyptr); + size_t keysize = keyptr - key; + int rc = sp_delete(db, key, keysize); + if (rc == -1) + tnt_raise(ClientError, ER_SOPHIA, sp_error(db)); + } + + return old_tuple; +} + +struct sophia_iterator { + struct iterator base; + const char *key; + int keysize; + uint32_t part_count; + void *db; + void *cursor; +}; + +void +sophia_iterator_free(struct iterator *ptr) +{ + assert(ptr->free == sophia_iterator_free); + struct sophia_iterator *it = (struct sophia_iterator *) ptr; + if (it->cursor) + sp_destroy(it->cursor); + free(ptr); +} + +void +sophia_iterator_close(struct iterator *ptr) +{ + assert(ptr->free == sophia_iterator_free); + struct sophia_iterator *it = (struct sophia_iterator *) ptr; + if (it->cursor) { + sp_destroy(it->cursor); + it->cursor = NULL; + } +} + +struct tuple * +sophia_iterator_next(struct iterator *ptr) +{ + assert(ptr->next == sophia_iterator_next); + struct sophia_iterator *it = (struct sophia_iterator *) ptr; + assert(it->cursor != NULL); + int rc = sp_fetch(it->cursor); + if (rc == 0) + return NULL; + size_t valuesize = sp_valuesize(it->cursor); + const char *value = sp_value(it->cursor); + struct tuple *ret = + tuple_new(tuple_format_ber, value, value + valuesize); + tuple_ref(ret, 1); + return ret; +} + +struct tuple * +sophia_iterator_last(struct iterator *ptr __attribute__((unused))) +{ + return NULL; +} + +struct tuple * +sophia_iterator_eq(struct iterator *ptr) +{ + ptr->next = sophia_iterator_last; + struct sophia_iterator *it = (struct sophia_iterator *) ptr; + assert(it->cursor == NULL); + return sophia_gettuple(it->db, it->key, it->keysize); +} + +struct iterator * +SophiaIndex::allocIterator() const +{ + struct sophia_iterator *it = + (struct sophia_iterator *) calloc(1, sizeof(*it)); + if (it == NULL) { + tnt_raise(ClientError, ER_MEMORY_ISSUE, + sizeof(struct sophia_iterator), "SophiaIndex", + "iterator"); + } + it->base.next = sophia_iterator_next; + it->base.close = sophia_iterator_close; + it->base.free = sophia_iterator_free; + it->cursor = NULL; + return (struct iterator *) it; +} + +void +SophiaIndex::initIterator(struct iterator *ptr, enum iterator_type type, + const char *key, uint32_t part_count) const +{ + assert(part_count <= 1); + + struct sophia_iterator *it = (struct sophia_iterator *) ptr; + assert(it->cursor == NULL); + + size_t keysize; + if (part_count > 0) { + const char *keyptr = key; + mp_next(&keyptr); + keysize = keyptr - key; + } else { + keysize = 0; + key = NULL; + } + it->key = key; + it->keysize = keysize; + it->part_count = part_count; + it->db = db; + + sporder compare; + switch (type) { + case ITER_EQ: it->base.next = sophia_iterator_eq; + return; + case ITER_ALL: + case ITER_GE: compare = SPGTE; + break; + case ITER_GT: compare = SPGT; + break; + case ITER_LE: compare = SPLTE; + break; + case ITER_LT: compare = SPLT; + break; + default: + tnt_raise(ClientError, ER_UNSUPPORTED, + "SophiaIndex", "requested iterator type"); + } + it->cursor = sp_cursor(db, compare, key, keysize); + if (it->cursor == NULL) + tnt_raise(ClientError, ER_SOPHIA, sp_error(db)); +} + +/* }}} */ diff --git a/src/box/sophia_index.h b/src/box/sophia_index.h new file mode 100644 index 0000000000000000000000000000000000000000..547f5ef455ebe548fa0a0f2c3e8343ebdca321cb --- /dev/null +++ b/src/box/sophia_index.h @@ -0,0 +1,58 @@ +#ifndef TARANTOOL_BOX_SOPHIA_INDEX_H_INCLUDED +#define TARANTOOL_BOX_SOPHIA_INDEX_H_INCLUDED +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "index.h" + +class SophiaIndex: public Index { +public: + SophiaIndex(struct key_def *key_def); + ~SophiaIndex(); + + virtual size_t size() const; + virtual void endBuild(); + + virtual struct tuple *findByKey(const char *key, uint32_t part_count) const; + virtual struct tuple *replace(struct tuple *old_tuple, + struct tuple *new_tuple, + enum dup_replace_mode mode); + + virtual struct iterator *allocIterator() const; + virtual void initIterator(struct iterator *iterator, + enum iterator_type type, + const char *key, uint32_t part_count) const; + virtual size_t memsize() const; + +protected: + void *env; + void *db; +}; + +#endif /* TARANTOOL_BOX_SOPHIA_INDEX_H_INCLUDED */ diff --git a/src/box/space.h b/src/box/space.h index 2ea00cfa4c5bfa5ca2b09f4b199abfd8951eeff5..7999a4b06205fec7fd00651e7d4aec1e35e93984 100644 --- a/src/box/space.h +++ b/src/box/space.h @@ -189,10 +189,14 @@ space_replace(struct space *space, struct tuple *old_tuple, struct tuple * space_replace_no_keys(struct space*, struct tuple*, struct tuple*, enum dup_replace_mode); +struct tuple * +space_replace_primary_key(struct space*, struct tuple*, struct tuple*, + enum dup_replace_mode); void space_begin_build_primary_key(struct space *space); void space_build_primary_key(struct space *space); void space_build_all_keys(struct space *space); +void space_noop(struct space *space); uint32_t space_size(struct space *space); diff --git a/src/box/tuple.cc b/src/box/tuple.cc index c509a545a0ec1aee392128070af68699d2fb8ff4..bcb1eb0ff3171c80514a5924e222da861b1e592a 100644 --- a/src/box/tuple.cc +++ b/src/box/tuple.cc @@ -428,7 +428,7 @@ tuple_new(struct tuple_format *format, const char *data, const char *end) inline __attribute__((always_inline)) int mp_compare_uint(const char **data_a, const char **data_b); -static inline int +int tuple_compare_field(const char *field_a, const char *field_b, enum field_type type) { diff --git a/src/box/tuple.h b/src/box/tuple.h index 0f94a4c0a1053cc956cf2e9e66b887dbe23a8bab..e766bd668f82ef52283e9f997e16efd0499c28f0 100644 --- a/src/box/tuple.h +++ b/src/box/tuple.h @@ -391,6 +391,19 @@ tuple_update(struct tuple_format *new_format, const struct tuple *old_tuple, const char *expr, const char *expr_end); +/** + * @brief Compare two tuple fields using using field type definition + * @param field_a field + * @param field_b field + * @param field_type field type definition + * @retval 0 if field_a == field_b + * @retval <0 if field_a < field_b + * @retval >0 if field_a > field_b + */ +int +tuple_compare_field(const char *field_a, const char *field_b, + enum field_type type); + /** * @brief Compare two tuples using field by field using key definition * @param tuple_a tuple diff --git a/src/box/txn.cc b/src/box/txn.cc index 622a6550606f618524c35ea1e7d951f44e6b52c7..62831885d7e9593511f99738afe0375ff8da6887 100644 --- a/src/box/txn.cc +++ b/src/box/txn.cc @@ -130,6 +130,8 @@ txn_finish(struct txn *txn) { if (txn->old_tuple) tuple_ref(txn->old_tuple, -1); + if (txn->space) + txn->space->engine->factory->txnFinish(txn); TRASH(txn); } diff --git a/src/box/txn.h b/src/box/txn.h index e2e22bbb4d29eaafaadeb53133883029db723c05..c67d6ad687dde949a47532613aa9b8df02567fa4 100644 --- a/src/box/txn.h +++ b/src/box/txn.h @@ -56,4 +56,5 @@ void txn_replace(struct txn *txn, struct space *space, struct tuple *old_tuple, struct tuple *new_tuple, enum dup_replace_mode mode); void txn_add_redo(struct txn *txn, struct request *request); + #endif /* TARANTOOL_BOX_TXN_H_INCLUDED */ diff --git a/src/errcode.h b/src/errcode.h index 41476d99f71be24b1cf0fadc37ed805c2d3d19be..586bb944a02de0872bd07424a31ab22e37fd0809 100644 --- a/src/errcode.h +++ b/src/errcode.h @@ -107,8 +107,9 @@ enum { TNT_ERRMSG_MAX = 512 }; /* 55 */_(ER_SPACE_ACCESS_DENIED, 2, "%s access denied for user '%s' to space '%s'") \ /* 56 */_(ER_USER_MAX, 2, "A limit on the total number of users has been reached: %u") \ /* 57 */_(ER_NO_SUCH_ENGINE, 2, "Space engine '%s' does not exist") \ - /* 57 */_(ER_RELOAD_CFG, 2, "Can't set option '%s' dynamically") \ - /* 57 */_(ER_CFG, 2, "Incorrect option value: %s") \ + /* 58 */_(ER_RELOAD_CFG, 2, "Can't set option '%s' dynamically") \ + /* 59 */_(ER_CFG, 2, "Incorrect option value: %s") \ + /* 60 */_(ER_SOPHIA, 2, "%s") \ /* diff --git a/test/big/lua.result b/test/big/lua.result index c84bab07982137e1eeb78874d1692c702301ebaf..3b334151f611623824dbc6301d077b6ce7bcfe5e 100644 --- a/test/big/lua.result +++ b/test/big/lua.result @@ -472,7 +472,7 @@ t = {} ... index:pairs('sid_t', { iterator = 'wrong_iterator_type' }) --- -- error: '[string "-- schema.lua (internal file)..."]:324: Wrong iterator type: wrong_iterator_type' +- error: '[string "-- schema.lua (internal file)..."]:325: Wrong iterator type: wrong_iterator_type' ... index = nil --- diff --git a/test/box/misc.result b/test/box/misc.result index c67e1939055ab2f237077e577bb2672908c11636..a6c0f62361a4bd710eb261e607e3f9d3ee525da8 100644 --- a/test/box/misc.result +++ b/test/box/misc.result @@ -196,6 +196,7 @@ t; - 'box.error.ER_NO_SUCH_INDEX : 35' - 'box.error.ER_TUPLE_FOUND : 3' - 'box.error.ER_CREATE_SPACE : 9' + - 'box.error.ER_PROC_RET : 21' - 'box.error.ER_TUPLE_FORMAT_LIMIT : 16' - 'box.error.ER_FIELD_TYPE : 23' - 'box.error.ER_CFG : 59' @@ -247,7 +248,7 @@ t; - 'box.error.ER_NO_SUCH_PROC : 33' - 'box.error.ER_SPACE_EXISTS : 10' - 'box.error.ER_PROC_LUA : 32' - - 'box.error.ER_PROC_RET : 21' + - 'box.error.ER_SOPHIA : 60' - 'box.error.ER_NO_SUCH_TRIGGER : 34' - 'box.error.ER_TUPLE_IS_TOO_LONG : 27' - 'box.error.ER_SPLICE : 25' diff --git a/test/box/sophia.result b/test/box/sophia.result new file mode 100644 index 0000000000000000000000000000000000000000..7cae8cef6e6fc90c5528fb2ef51db96264e78d82 --- /dev/null +++ b/test/box/sophia.result @@ -0,0 +1,109 @@ +space = box.schema.create_space('tweedledum', { id = 123, engine = 'sophia' }) +--- +... +space:create_index('primary', { type = 'tree', parts = {0, 'num'} }) +--- +... +for v=1, 10 do space:insert({v}) end +--- +... +t = space.index[0]:select({}, {iterator = box.index.ALL}) +--- +... +t +--- +- - [1] + - [2] + - [3] + - [4] + - [5] + - [6] + - [7] + - [8] + - [9] + - [10] +... +t = space.index[0]:select({}, {iterator = box.index.GE}) +--- +... +t +--- +- - [1] + - [2] + - [3] + - [4] + - [5] + - [6] + - [7] + - [8] + - [9] + - [10] +... +t = space.index[0]:select(4, {iterator = box.index.GE}) +--- +... +t +--- +- - [4] + - [5] + - [6] + - [7] + - [8] + - [9] + - [10] +... +t = space.index[0]:select({}, {iterator = box.index.LE}) +--- +... +t +--- +- - [10] + - [9] + - [8] + - [7] + - [6] + - [5] + - [4] + - [3] + - [2] + - [1] +... +t = space.index[0]:select(7, {iterator = box.index.LE}) +--- +... +t +--- +- - [7] + - [6] + - [5] + - [4] + - [3] + - [2] + - [1] +... +t = {} +--- +... +for v=1, 10 do table.insert(t, space:get({v})) end +--- +... +t +--- +- - [1] + - [2] + - [3] + - [4] + - [5] + - [6] + - [7] + - [8] + - [9] + - [10] +... +space:drop() +--- +... +os.execute("rm -rf sophia") +--- +- 0 +... diff --git a/test/box/sophia.test.lua b/test/box/sophia.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..759fe750abd7e9d79163701fadeb2c02606a263f --- /dev/null +++ b/test/box/sophia.test.lua @@ -0,0 +1,27 @@ + +space = box.schema.create_space('tweedledum', { id = 123, engine = 'sophia' }) +space:create_index('primary', { type = 'tree', parts = {0, 'num'} }) + +for v=1, 10 do space:insert({v}) end + +t = space.index[0]:select({}, {iterator = box.index.ALL}) +t + +t = space.index[0]:select({}, {iterator = box.index.GE}) +t + +t = space.index[0]:select(4, {iterator = box.index.GE}) +t + +t = space.index[0]:select({}, {iterator = box.index.LE}) +t + +t = space.index[0]:select(7, {iterator = box.index.LE}) +t + +t = {} +for v=1, 10 do table.insert(t, space:get({v})) end +t + +space:drop() +os.execute("rm -rf sophia") diff --git a/test/module/suite.ini b/test/module/suite.ini index 0dede8fba9a77d53803c4ed063efcad5a29fc4d9..fba1bc22dc6b612d5b9dc675c36c3639258c8edb 100644 --- a/test/module/suite.ini +++ b/test/module/suite.ini @@ -1,3 +1,4 @@ [default] script = box.lua +disabled = sophia.test.lua description = tarantool/box, optional lua modules