/* * Copyright (C) 2015 Cloudius Systems, Ltd. */ #include #include "core/do_with.hh" #include "cql_test_env.hh" #include "cql3/query_processor.hh" #include "cql3/query_options.hh" #include "core/distributed.hh" #include "core/shared_ptr.hh" #include "utils/UUID_gen.hh" #include "service/migration_manager.hh" #include "message/messaging_service.hh" #include "service/storage_service.hh" #include "db/config.hh" #include "db/batchlog_manager.hh" #include "schema_builder.hh" #include "init.hh" class in_memory_cql_env : public cql_test_env { public: static auto constexpr ks_name = "ks"; private: ::shared_ptr> _db; ::shared_ptr> _qp; private: struct core_local_state { service::client_state client_state; core_local_state() : client_state(service::client_state::for_external_calls()) { client_state.set_keyspace(ks_name); } future<> stop() { return make_ready_future<>(); } }; distributed _core_local; private: auto make_query_state() { return ::make_shared(_core_local.local().client_state); } public: in_memory_cql_env( ::shared_ptr> db, ::shared_ptr> qp) : _db(db) , _qp(qp) { } virtual future<::shared_ptr> execute_cql(const sstring& text) override { auto qs = make_query_state(); return _qp->local().process(text, *qs, cql3::query_options::DEFAULT).finally([qs] {}); } virtual future<::shared_ptr> execute_cql( const sstring& text, std::unique_ptr qo) override { auto qs = make_query_state(); auto& lqo = *qo; return _qp->local().process(text, *qs, lqo).finally([qs, qo = std::move(qo)] {}); } virtual future prepare(sstring query) override { return _qp->invoke_on_all([query, this] (auto& local_qp) { auto qs = this->make_query_state(); return local_qp.prepare(query, *qs).finally([qs] {}).discard_result(); }).then([query, this] { return _qp->local().compute_id(query, ks_name); }); } virtual future<::shared_ptr> execute_prepared( bytes id, std::vector values) override { auto prepared = _qp->local().get_prepared(id); assert(bool(prepared)); auto stmt = prepared->statement; assert(stmt->get_bound_terms() == values.size()); int32_t protocol_version = 3; auto options = ::make_shared(db::consistency_level::ONE, std::experimental::nullopt, std::move(values), false, cql3::query_options::specific_options::DEFAULT, protocol_version, serialization_format::use_32_bit()); options->prepare(prepared->bound_names); auto qs = make_query_state(); return _qp->local().process_statement(stmt, *qs, *options) .finally([options, qs] {}); } virtual future<> create_table(std::function schema_maker) override { auto id = utils::UUID_gen::get_time_UUID(); return _db->invoke_on_all([schema_maker, id, this] (database& db) { schema_builder builder(make_lw_shared(schema_maker(ks_name))); builder.set_uuid(id); auto cf_schema = builder.build(schema_builder::compact_storage::no); auto& ks = db.find_keyspace(ks_name); auto cfg = ks.make_column_family_config(*cf_schema); db.add_column_family(std::move(cf_schema), std::move(cfg)); }); } virtual future<> require_keyspace_exists(const sstring& ks_name) override { auto& db = _db->local(); assert(db.has_keyspace(ks_name)); return make_ready_future<>(); } virtual future<> require_table_exists(const sstring& ks_name, const sstring& table_name) override { auto& db = _db->local(); assert(db.has_schema(ks_name, table_name)); return make_ready_future<>(); } virtual future<> require_column_has_value(const sstring& table_name, std::vector pk, std::vector ck, const sstring& column_name, boost::any expected) override { auto& db = _db->local(); auto& cf = db.find_column_family(ks_name, table_name); auto schema = cf.schema(); auto pkey = partition_key::from_deeply_exploded(*schema, pk); auto dk = dht::global_partitioner().decorate_key(*schema, pkey); auto shard = db.shard_of(dk._token); return _db->invoke_on(shard, [pkey = std::move(pkey), ck = std::move(ck), ks_name = std::move(ks_name), column_name = std::move(column_name), expected = std::move(expected), table_name = std::move(table_name)] (database& db) mutable { auto& cf = db.find_column_family(ks_name, table_name); auto schema = cf.schema(); return cf.find_partition_slow(pkey).then([schema, ck, column_name, expected] (column_family::const_mutation_partition_ptr p) { assert(p != nullptr); auto row = p->find_row(clustering_key::from_deeply_exploded(*schema, ck)); assert(row != nullptr); auto col_def = schema->get_column_definition(utf8_type->decompose(column_name)); assert(col_def != nullptr); const atomic_cell_or_collection* cell = row->find_cell(col_def->id); if (!cell) { assert(((void)"column not set", 0)); } bytes actual; if (!col_def->type->is_multi_cell()) { auto c = cell->as_atomic_cell(); assert(c.is_live()); actual = { c.value().begin(), c.value().end() }; } else { auto c = cell->as_collection_mutation(); auto type = dynamic_pointer_cast(col_def->type); actual = type->to_value(type->deserialize_mutation_form(c), serialization_format::internal()); } assert(col_def->type->equal(actual, col_def->type->decompose(expected))); }); }); } virtual database& local_db() override { return _db->local(); } cql3::query_processor& local_qp() override { return _qp->local(); } future<> start() { return _core_local.start().then([this] () { auto query = sprint("create keyspace %s with replication = { 'class' : 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor' : 1 };", sstring{ks_name}); return execute_cql(query).discard_result().then([] { return make_ready_future<>(); }); }); } virtual future<> stop() override { return _core_local.stop().then([this] { return db::get_batchlog_manager().stop().then([this] { return _qp->stop().then([this] { return service::get_migration_manager().stop().then([this] { return service::get_storage_proxy().stop().then([this] { return _db->stop().then([this] { return locator::i_endpoint_snitch::stop_snitch(); }); }); }); }); }); }); } }; future<> init_once(shared_ptr> db) { static bool done = false; if (!done) { done = true; // FIXME: we leak db, since we're initializing the global storage_service with it. new shared_ptr>(db); return init_storage_service(*db).then([] { return init_ms_fd_gossiper("127.0.0.1", db::config::seed_provider_type()); }); } else { return make_ready_future(); } } future<::shared_ptr> make_env_for_test() { return locator::i_endpoint_snitch::create_snitch("SimpleSnitch").then([] { auto db = ::make_shared>(); return init_once(db).then([db] { return seastar::async([db] { auto cfg = make_lw_shared(); cfg->data_file_directories() = {}; db->start(std::move(*cfg)).get(); distributed& proxy = service::get_storage_proxy(); distributed& mm = service::get_migration_manager(); distributed& bm = db::get_batchlog_manager(); auto qp = ::make_shared>(); proxy.start(std::ref(*db)).get(); mm.start().get(); qp->start(std::ref(proxy), std::ref(*db)).get(); db::system_keyspace::init_local_cache().get(); auto& ss = service::get_local_storage_service(); static bool storage_service_started = false; if (!storage_service_started) { storage_service_started = true; ss.init_server().get(); } bm.start(std::ref(*qp)); auto env = ::make_shared(db, qp); env->start().get(); return dynamic_pointer_cast(env); }); }); }); } future<> do_with_cql_env(std::function(cql_test_env&)> func) { return make_env_for_test().then([func = std::move(func)] (auto e) mutable { return do_with(std::move(func), [e] (auto& f) { return f(*e); }).finally([e] { return e->stop().finally([e] {}); }); }); }