/* * Copyright (C) 2014 ScyllaDB */ /* * This file is part of Scylla. * * Scylla is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Scylla is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Scylla. If not, see . */ #include "utils/UUID_gen.hh" #include "cql3/column_identifier.hh" #include "schema.hh" #include "schema_builder.hh" #include "md5_hasher.hh" #include #include #include "db/marshal/type_parser.hh" #include "version.hh" #include "schema_registry.hh" #include #include #include #include "view_info.hh" constexpr int32_t schema::NAME_LENGTH; sstring to_sstring(column_kind k) { switch (k) { case column_kind::partition_key: return "PARTITION_KEY"; case column_kind::clustering_key: return "CLUSTERING_COLUMN"; case column_kind::static_column: return "STATIC"; case column_kind::regular_column: return "REGULAR"; } throw std::invalid_argument("unknown column kind"); } sstring to_sstring(index_type t) { switch (t) { case index_type::keys: return "KEYS"; case index_type::custom: return "CUSTOM"; case index_type::composites: return "COMPOSITES"; case index_type::none: return "null"; } throw std::invalid_argument("unknown index type"); } bool is_compatible(column_kind k1, column_kind k2) { return k1 == k2; } column_mapping_entry::column_mapping_entry(bytes name, sstring type_name) : _name(std::move(name)) , _type(db::marshal::type_parser::parse(type_name)) { } column_mapping_entry::column_mapping_entry(const column_mapping_entry& o) : _name(o._name) , _type(db::marshal::type_parser::parse(o._type->name())) { } column_mapping_entry& column_mapping_entry::operator=(const column_mapping_entry& o) { if (this != &o) { auto tmp = o; this->~column_mapping_entry(); new (this) column_mapping_entry(std::move(tmp)); } return *this; } template std::vector get_column_types(const Sequence& column_definitions) { std::vector result; for (auto&& col : column_definitions) { result.push_back(col.type); } return result; } std::ostream& operator<<(std::ostream& out, const column_mapping& cm) { column_id n_static = cm.n_static(); column_id n_regular = cm.columns().size() - n_static; auto pr_entry = [] (column_id i, const column_mapping_entry& e) { // Without schema we don't know if name is UTF8. If we had schema we could use // s->regular_column_name_type()->to_string(e.name()). return sprint("{id=%s, name=0x%s, type=%s}", i, e.name(), e.type()->name()); }; return out << "{static=[" << ::join(", ", boost::irange(0, n_static) | boost::adaptors::transformed([&] (column_id i) { return pr_entry(i, cm.static_column_at(i)); })) << "], regular=[" << ::join(", ", boost::irange(0, n_regular) | boost::adaptors::transformed([&] (column_id i) { return pr_entry(i, cm.regular_column_at(i)); })) << "]}"; } ::shared_ptr schema::make_column_specification(const column_definition& def) { auto id = ::make_shared(def.name(), column_name_type(def)); return ::make_shared(_raw._ks_name, _raw._cf_name, std::move(id), def.type); } void schema::rebuild() { _partition_key_type = make_lw_shared>(get_column_types(partition_key_columns())); _clustering_key_type = make_lw_shared(get_column_types(clustering_key_columns())); _columns_by_name.clear(); _regular_columns_by_name.clear(); for (const column_definition& def : all_columns_in_select_order()) { _columns_by_name[def.name()] = &def; } static_assert(row_column_ids_are_ordered_by_name::value, "row columns don't need to be ordered by name"); if (!std::is_sorted(regular_columns().begin(), regular_columns().end(), column_definition::name_comparator())) { throw std::runtime_error("Regular columns should be sorted by name"); } if (!std::is_sorted(static_columns().begin(), static_columns().end(), column_definition::name_comparator())) { throw std::runtime_error("Static columns should be sorted by name"); } for (const column_definition& def : regular_columns()) { _regular_columns_by_name[def.name()] = &def; } { std::vector cm_columns; for (const column_definition& def : boost::range::join(static_columns(), regular_columns())) { cm_columns.emplace_back(column_mapping_entry{def.name(), def.type}); } _column_mapping = column_mapping(std::move(cm_columns), static_columns_count()); } thrift()._compound = is_compound(); thrift()._is_dynamic = clustering_key_size() > 0; if (default_validator()->is_counter()) { for (auto&& cdef : boost::range::join(static_columns(), regular_columns())) { if (!cdef.type->is_counter()) { throw exceptions::configuration_exception(sprint("Cannot add a non counter column (%s) in a counter column family", cdef.name_as_text())); } } } else { for (auto&& cdef : all_columns()) { if (cdef.second->type->is_counter()) { throw exceptions::configuration_exception(sprint("Cannot add a counter column (%s) in a non counter column family", cdef.second->name_as_text())); } } } } const column_mapping& schema::get_column_mapping() const { return _column_mapping; } schema::raw_schema::raw_schema(utils::UUID id) : _id(id) { } schema::schema(const raw_schema& raw, stdx::optional raw_view_info) : _raw(raw) , _offsets([this] { if (_raw._columns.size() > std::numeric_limits::max()) { throw std::runtime_error(sprint("Column count limit (%d) overflowed: %d", std::numeric_limits::max(), _raw._columns.size())); } auto& cols = _raw._columns; std::array count = { 0, 0, 0, 0 }; auto i = cols.begin(); auto e = cols.end(); for (auto k : { column_kind::partition_key, column_kind::clustering_key, column_kind::static_column, column_kind::regular_column }) { auto j = std::stable_partition(i, e, [k](const auto& c) { return c.kind == k; }); count[column_count_type(k)] = std::distance(i, j); i = j; } return std::array { count[0], count[0] + count[1], count[0] + count[1] + count[2], }; }()) , _regular_columns_by_name(serialized_compare(_raw._regular_column_name_type)) , _is_counter(_raw._default_validator->is_counter()) { struct name_compare { data_type type; name_compare(data_type type) : type(type) {} bool operator()(const column_definition& cd1, const column_definition& cd2) const { return type->less(cd1.name(), cd2.name()); } }; std::sort( _raw._columns.begin() + column_offset(column_kind::static_column), _raw._columns.begin() + column_offset(column_kind::regular_column), name_compare(utf8_type)); std::sort( _raw._columns.begin() + column_offset(column_kind::regular_column), _raw._columns.end(), name_compare(regular_column_name_type())); std::sort(_raw._columns.begin(), _raw._columns.begin() + column_offset(column_kind::clustering_key), [] (auto x, auto y) { return x.id < y.id; }); std::sort(_raw._columns.begin() + column_offset(column_kind::clustering_key), _raw._columns.begin() + column_offset(column_kind::static_column), [] (auto x, auto y) { return x.id < y.id; }); column_id id = 0; for (auto& def : _raw._columns) { def.column_specification = make_column_specification(def); assert(!def.id || def.id == id - column_offset(def.kind)); def.id = id - column_offset(def.kind); auto dropped_at_it = _raw._dropped_columns.find(def.name_as_text()); if (dropped_at_it != _raw._dropped_columns.end()) { def._dropped_at = std::max(def._dropped_at, dropped_at_it->second); } def._thrift_bits = column_definition::thrift_bits(); { // is_on_all_components // TODO : In origin, this predicate is "componentIndex == null", which is true in // a number of cases, some of which I've most likely missed... switch (def.kind) { case column_kind::partition_key: // In origin, ci == null is true for a PK column where CFMetaData "keyValidator" is non-composite. // Which is true of #pk == 1 def._thrift_bits.is_on_all_components = partition_key_size() == 1; break; case column_kind::regular_column: if (_raw._is_dense) { // regular values in dense tables are alone, so they have no index def._thrift_bits.is_on_all_components = true; break; } default: // Or any other column where "comparator" is not compound def._thrift_bits.is_on_all_components = !thrift().has_compound_comparator(); break; } } ++id; } rebuild(); if (raw_view_info) { _view_info = std::make_unique<::view_info>(*this, *raw_view_info); } } schema::schema(std::experimental::optional id, sstring ks_name, sstring cf_name, std::vector partition_key, std::vector clustering_key, std::vector regular_columns, std::vector static_columns, data_type regular_column_name_type, sstring comment) : schema([&] { raw_schema raw(id ? *id : utils::UUID_gen::get_time_UUID()); raw._comment = std::move(comment); raw._ks_name = std::move(ks_name); raw._cf_name = std::move(cf_name); raw._regular_column_name_type = regular_column_name_type; auto build_columns = [&raw](std::vector& columns, column_kind kind) { for (auto& sc : columns) { if (sc.type->is_multi_cell()) { raw._collections.emplace(sc.name, sc.type); } raw._columns.emplace_back(std::move(sc.name), std::move(sc.type), kind); } }; build_columns(partition_key, column_kind::partition_key); build_columns(clustering_key, column_kind::clustering_key); build_columns(static_columns, column_kind::static_column); build_columns(regular_columns, column_kind::regular_column); return raw; }(), stdx::nullopt) {} schema::schema(const schema& o) : _raw(o._raw) , _offsets(o._offsets) , _regular_columns_by_name(serialized_compare(_raw._regular_column_name_type)) , _is_counter(o._is_counter) { rebuild(); if (o.is_view()) { _view_info = std::make_unique<::view_info>(*this, o.view_info()->raw()); } } schema::~schema() { if (_registry_entry) { _registry_entry->detach_schema(); } } schema_registry_entry* schema::registry_entry() const noexcept { return _registry_entry; } sstring schema::thrift_key_validator() const { if (partition_key_size() == 1) { return partition_key_columns().begin()->type->name(); } else { sstring type_params = ::join(", ", partition_key_columns() | boost::adaptors::transformed(std::mem_fn(&column_definition::type)) | boost::adaptors::transformed(std::mem_fn(&abstract_type::name))); return "org.apache.cassandra.db.marshal.CompositeType(" + type_params + ")"; } } bool schema::has_multi_cell_collections() const { return boost::algorithm::any_of(all_columns_in_select_order(), [] (const column_definition& cdef) { return cdef.type->is_collection() && cdef.type->is_multi_cell(); }); } bool operator==(const schema& x, const schema& y) { return x._raw._id == y._raw._id && x._raw._ks_name == y._raw._ks_name && x._raw._cf_name == y._raw._cf_name && x._raw._columns == y._raw._columns && x._raw._comment == y._raw._comment && x._raw._default_time_to_live == y._raw._default_time_to_live && x._raw._regular_column_name_type->equals(y._raw._regular_column_name_type) && x._raw._bloom_filter_fp_chance == y._raw._bloom_filter_fp_chance && x._raw._compressor_params == y._raw._compressor_params && x._raw._is_dense == y._raw._is_dense && x._raw._is_compound == y._raw._is_compound && x._raw._type == y._raw._type && x._raw._gc_grace_seconds == y._raw._gc_grace_seconds && x._raw._dc_local_read_repair_chance == y._raw._dc_local_read_repair_chance && x._raw._read_repair_chance == y._raw._read_repair_chance && x._raw._min_compaction_threshold == y._raw._min_compaction_threshold && x._raw._max_compaction_threshold == y._raw._max_compaction_threshold && x._raw._min_index_interval == y._raw._min_index_interval && x._raw._max_index_interval == y._raw._max_index_interval && x._raw._memtable_flush_period == y._raw._memtable_flush_period && x._raw._speculative_retry == y._raw._speculative_retry && x._raw._compaction_strategy == y._raw._compaction_strategy && x._raw._compaction_strategy_options == y._raw._compaction_strategy_options && x._raw._caching_options == y._raw._caching_options && x._raw._dropped_columns == y._raw._dropped_columns && x._raw._collections == y._raw._collections && indirect_equal_to>()(x._view_info, y._view_info) && x._raw._indices_by_name == y._raw._indices_by_name; #if 0 && Objects.equal(triggers, other.triggers) #endif } index_info::index_info(::index_type idx_type, std::experimental::optional idx_name, std::experimental::optional idx_options) : index_type(idx_type), index_name(idx_name), index_options(idx_options) {} index_metadata::index_metadata(const sstring& name, const index_options_map& options, index_metadata_kind kind) : _id{utils::UUID_gen::get_name_UUID(name)} , _name{name} , _kind{kind} , _options{options} {} bool index_metadata::operator==(const index_metadata& other) const { return _id == other._id && _name == other._name && _kind == other._kind && _options == other._options; } bool index_metadata::equals_noname(const index_metadata& other) const { return _kind == other._kind && _options == other._options; } const utils::UUID& index_metadata::id() const { return _id; } const sstring& index_metadata::name() const { return _name; } const index_metadata_kind index_metadata::kind() const { return _kind; } const index_options_map& index_metadata::options() const { return _options; } sstring index_metadata::get_default_index_name(const sstring& cf_name, std::experimental::optional root) { if (root) { return cf_name + "_" + root.value() + "_idx"; } return cf_name + "_idx"; } column_definition::column_definition(bytes name, data_type type, column_kind kind, column_id component_index, index_info idx, api::timestamp_type dropped_at) : _name(std::move(name)) , _dropped_at(dropped_at) , _is_atomic(type->is_atomic()) , _is_counter(type->is_counter()) , type(std::move(type)) , id(component_index) , kind(kind) , idx_info(std::move(idx)) {} std::ostream& operator<<(std::ostream& os, const column_definition& cd) { os << "ColumnDefinition{"; os << "name=" << cd.name_as_text(); os << ", type=" << cd.type->name(); os << ", kind=" << to_sstring(cd.kind); os << ", componentIndex=" << (cd.has_component_index() ? std::to_string(cd.component_index()) : "null"); os << ", indexName=" << (cd.idx_info.index_name ? *cd.idx_info.index_name : "null"); os << ", indexType=" << to_sstring(cd.idx_info.index_type); os << ", droppedAt=" << cd._dropped_at; os << "}"; return os; } const column_definition* schema::get_column_definition(const bytes& name) const { auto i = _columns_by_name.find(name); if (i == _columns_by_name.end()) { return nullptr; } return i->second; } const column_definition& schema::column_at(column_kind kind, column_id id) const { return _raw._columns.at(column_offset(kind) + id); } std::ostream& operator<<(std::ostream& os, const schema& s) { os << "org.apache.cassandra.config.CFMetaData@" << &s << "["; os << "cfId=" << s._raw._id; os << ",ksName=" << s._raw._ks_name; os << ",cfName=" << s._raw._cf_name; os << ",cfType=" << cf_type_to_sstring(s._raw._type); os << ",comparator=" << cell_comparator::to_sstring(s); os << ",comment=" << s._raw._comment; os << ",readRepairChance=" << s._raw._read_repair_chance; os << ",dcLocalReadRepairChance=" << s._raw._dc_local_read_repair_chance; os << ",gcGraceSeconds=" << s._raw._gc_grace_seconds; os << ",defaultValidator=" << s._raw._default_validator->name(); os << ",keyValidator=" << s.thrift_key_validator(); os << ",minCompactionThreshold=" << s._raw._min_compaction_threshold; os << ",maxCompactionThreshold=" << s._raw._max_compaction_threshold; os << ",columnMetadata=["; int n = 0; for (auto& cdef : s._raw._columns) { if (n++ != 0) { os << ", "; } os << cdef; } os << "]"; os << ",compactionStrategyClass=class org.apache.cassandra.db.compaction." << sstables::compaction_strategy::name(s._raw._compaction_strategy); os << ",compactionStrategyOptions={"; n = 0; for (auto& p : s._raw._compaction_strategy_options) { if (n++ != 0) { os << ", "; } os << p.first << "=" << p.second; } os << "}"; os << ",compressionParameters={"; n = 0; for (auto& p : s._raw._compressor_params.get_options() ) { if (n++ != 0) { os << ", "; } os << p.first << "=" << p.second; } os << "}"; os << ",bloomFilterFpChance=" << s._raw._bloom_filter_fp_chance; os << ",memtableFlushPeriod=" << s._raw._memtable_flush_period; os << ",caching=" << s._raw._caching_options.to_sstring(); os << ",defaultTimeToLive=" << s._raw._default_time_to_live.count(); os << ",minIndexInterval=" << s._raw._min_index_interval; os << ",maxIndexInterval=" << s._raw._max_index_interval; os << ",speculativeRetry=" << s._raw._speculative_retry.to_sstring(); os << ",droppedColumns={}"; os << ",triggers=[]"; os << ",isDense=" << std::boolalpha << s._raw._is_dense; os << ",version=" << s.version(); os << ",droppedColumns={"; n = 0; for (auto& dc : s._raw._dropped_columns) { if (n++ != 0) { os << ", "; } os << dc.first << " : " << dc.second; } os << "}"; os << ",collections={"; n = 0; for (auto& c : s._raw._collections) { if (n++ != 0) { os << ", "; } os << c.first << " : " << c.second->name(); } os << "}"; os << ",indices={"; n = 0; for (auto& c : s._raw._indices_by_name) { if (n++ != 0) { os << ", "; } os << c.first << " : " << c.second.id(); } os << "}"; if (s.is_view()) { os << ", viewInfo=" << *s.view_info(); } os << "]"; return os; } const sstring& column_definition::name_as_text() const { return column_specification->name->text(); } const bytes& column_definition::name() const { return _name; } bool column_definition::is_on_all_components() const { return _thrift_bits.is_on_all_components; } bool operator==(const column_definition& x, const column_definition& y) { return x._name == y._name && x.type->equals(y.type) && x.id == y.id && x.kind == y.kind && x._dropped_at == y._dropped_at; } // Based on org.apache.cassandra.config.CFMetaData#generateLegacyCfId utils::UUID generate_legacy_id(const sstring& ks_name, const sstring& cf_name) { return utils::UUID_gen::get_name_UUID(ks_name + cf_name); } bool thrift_schema::has_compound_comparator() const { return _compound; } bool thrift_schema::is_dynamic() const { return _is_dynamic; } schema_builder::schema_builder(const sstring& ks_name, const sstring& cf_name, std::experimental::optional id, data_type rct) : _raw(id ? *id : utils::UUID_gen::get_time_UUID()) { _raw._ks_name = ks_name; _raw._cf_name = cf_name; _raw._regular_column_name_type = rct; } schema_builder::schema_builder(const schema_ptr s) : schema_builder(s->_raw) { if (s->is_view()) { _view_info = s->view_info()->raw(); } } schema_builder::schema_builder(const schema::raw_schema& raw) : _raw(raw) { static_assert(schema::row_column_ids_are_ordered_by_name::value, "row columns don't need to be ordered by name"); // Schema builder may add or remove columns and their ids need to be // recomputed in build(). for (auto& def : _raw._columns | boost::adaptors::filtered([] (auto& def) { return !def.is_primary_key(); })) { def.id = 0; } } column_definition& schema_builder::find_column(const cql3::column_identifier& c) { auto i = std::find_if(_raw._columns.begin(), _raw._columns.end(), [c](auto& p) { return p.name() == c.name(); }); if (i != _raw._columns.end()) { return *i; } throw std::invalid_argument(sprint("No such column %s", c.name())); } void schema_builder::add_default_index_names(database& db) { auto s = db.find_schema(ks_name(), cf_name()); if (s) { for (auto& sc : _raw._columns) { if (sc.idx_info.index_type == index_type::none) { continue; } auto* c = s->get_column_definition(sc.name()); if (c == nullptr || !c->idx_info.index_name) { continue; } if (sc.idx_info.index_name && sc.idx_info.index_name != c->idx_info.index_name) { throw exceptions::configuration_exception( sprint( "Can't modify index name: was '%s' changed to '%s'", *c->idx_info.index_name, *sc.idx_info.index_name)); } sc.idx_info.index_name = c->idx_info.index_name; } } auto existing_names = db.existing_index_names(ks_name()); for (auto& sc : _raw._columns) { if (sc.idx_info.index_type != index_type::none && sc.idx_info.index_name) { sstring base_name = cf_name() + "_" + *sc.idx_info.index_name + "_idx"; auto i = std::remove_if(base_name.begin(), base_name.end(), [](char c) { return ::isspace(c); }); base_name.erase(i, base_name.end()); auto index_name = base_name; int n = 0; while (existing_names.count(index_name)) { index_name = base_name + "_" + to_sstring(++n); } sc.idx_info.index_name = index_name; } } } schema_builder& schema_builder::with_column(const column_definition& c) { return with_column(bytes(c.name()), data_type(c.type), index_info(c.idx_info), column_kind(c.kind), c.position()); } schema_builder& schema_builder::with_column(bytes name, data_type type, column_kind kind) { return with_column(name, type, index_info(), kind); } schema_builder& schema_builder::with_column(bytes name, data_type type, index_info info, column_kind kind) { // component_index will be determined by schema cosntructor return with_column(name, type, info, kind, 0); } schema_builder& schema_builder::with_column(bytes name, data_type type, index_info info, column_kind kind, column_id component_index) { _raw._columns.emplace_back(name, type, kind, component_index, info); if (type->is_multi_cell()) { with_collection(name, type); } else if (type->is_counter()) { set_default_validator(counter_type); } return *this; } schema_builder& schema_builder::without_column(bytes name) { auto it = boost::range::find_if(_raw._columns, [&] (auto& column) { return column.name() == name; }); assert(it != _raw._columns.end()); auto now = api::new_timestamp(); auto ret = _raw._dropped_columns.emplace(it->name_as_text(), now); if (!ret.second) { ret.first->second = std::max(ret.first->second, now); } _raw._columns.erase(it); return *this; } schema_builder& schema_builder::without_column(sstring name, api::timestamp_type timestamp) { auto ret = _raw._dropped_columns.emplace(name, timestamp); if (!ret.second) { ret.first->second = std::max(ret.first->second, timestamp); } return *this; } schema_builder& schema_builder::with_column_rename(bytes from, bytes to) { auto it = std::find_if(_raw._columns.begin(), _raw._columns.end(), [&] (auto& col) { return col.name() == from; }); assert(it != _raw._columns.end()); auto& def = *it; column_definition new_def(to, def.type, def.kind, def.component_index(), def.idx_info); _raw._columns.erase(it); return with_column(new_def); } schema_builder& schema_builder::with_altered_column_type(bytes name, data_type new_type) { auto it = boost::find_if(_raw._columns, [&name] (auto& c) { return c.name() == name; }); assert(it != _raw._columns.end()); it->type = new_type; if (new_type->is_multi_cell()) { auto c_it = _raw._collections.find(name); assert(c_it != _raw._collections.end()); c_it->second = new_type; } return *this; } schema_builder& schema_builder::with_collection(bytes name, data_type type) { _raw._collections.emplace(name, type); return *this; } schema_builder& schema_builder::with(compact_storage cs) { _compact_storage = cs; return *this; } schema_builder& schema_builder::with_version(table_schema_version v) { _version = v; return *this; } void schema_builder::prepare_dense_schema(schema::raw_schema& raw) { if (raw._is_dense) { auto regular_cols = std::count_if(raw._columns.begin(), raw._columns.end(), [](auto&& col) { return col.kind == column_kind::regular_column; }); // In Origin, dense CFs always have at least one regular column if (regular_cols == 0) { raw._columns.emplace_back(bytes(""), raw._regular_column_name_type, column_kind::regular_column, 0, index_info()); } else if (regular_cols > 1) { throw exceptions::configuration_exception(sprint("Expecting exactly one regular column. Found %d", regular_cols)); } } } schema_builder& schema_builder::with_view_info(utils::UUID base_id, sstring base_name, bool include_all_columns, sstring where_clause) { _view_info = raw_view_info(std::move(base_id), std::move(base_name), include_all_columns, std::move(where_clause)); return *this; } schema_builder& schema_builder::with_index(const index_metadata& im) { _raw._indices_by_name.emplace(im.name(), im); return *this; } schema_builder& schema_builder::without_index(const sstring& name) { const auto& it = _raw._indices_by_name.find(name); if (it != _raw._indices_by_name.end()) { _raw._indices_by_name.erase(name); } return *this; } schema_ptr schema_builder::build() { schema::raw_schema new_raw = _raw; // Copy so that build() remains idempotent. if (_version) { new_raw._version = *_version; } else { new_raw._version = utils::UUID_gen::get_time_UUID(); } if (_compact_storage) { // Dense means that no part of the comparator stores a CQL column name. This means // COMPACT STORAGE with at least one columnAliases (otherwise it's a thrift "static" CF). auto clustering_key_size = std::count_if(new_raw._columns.begin(), new_raw._columns.end(), [](auto&& col) { return col.kind == column_kind::clustering_key; }); new_raw._is_dense = (*_compact_storage == compact_storage::yes) && (clustering_key_size > 0); if (clustering_key_size == 0) { if (*_compact_storage == compact_storage::yes) { new_raw._is_compound = false; } else { new_raw._is_compound = true; } } else { if ((*_compact_storage == compact_storage::yes) && clustering_key_size == 1) { new_raw._is_compound = false; } else { new_raw._is_compound = true; } } } prepare_dense_schema(new_raw); return make_lw_shared(schema(new_raw, _view_info)); } schema_ptr schema_builder::build(compact_storage cp) { return with(cp).build(); } // Useful functions to manipulate the schema's comparator field namespace cell_comparator { static constexpr auto _composite_str = "org.apache.cassandra.db.marshal.CompositeType"; static constexpr auto _collection_str = "org.apache.cassandra.db.marshal.ColumnToCollectionType"; static sstring compound_name(const schema& s) { sstring compound(_composite_str); compound += "("; if (s.clustering_key_size()) { for (auto &t : s.clustering_key_columns()) { compound += t.type->name() + ","; } } if (!s.is_dense()) { compound += s.regular_column_name_type()->name() + ","; } if (!s.collections().empty()) { compound += _collection_str; compound += "("; for (auto& c : s.collections()) { auto ct = static_pointer_cast(c.second); compound += sprint("%s:%s,", to_hex(c.first), ct->name()); } compound.back() = ')'; compound += ","; } // last one will be a ',', just replace it. compound.back() = ')'; return compound; } sstring to_sstring(const schema& s) { if (s.is_compound()) { return compound_name(s); } else if (s.clustering_key_size() == 1) { assert(s.is_dense()); return s.clustering_key_columns().front().type->name(); } else { return s.regular_column_name_type()->name(); } } bool check_compound(sstring comparator) { static sstring compound(_composite_str); return comparator.compare(0, compound.size(), compound) == 0; } void read_collections(schema_builder& builder, sstring comparator) { // The format of collection entries in the comparator is: // org.apache.cassandra.db.marshal.ColumnToCollectionType(:, ...) auto find_closing_parenthesis = [] (sstring_view str, size_t start) { auto pos = start; auto nest_level = 0; do { pos = str.find_first_of("()", pos); if (pos == sstring::npos) { throw marshal_exception(); } if (str[pos] == ')') { nest_level--; } else if (str[pos] == '(') { nest_level++; } pos++; } while (nest_level > 0); return pos - 1; }; auto collection_str_length = strlen(_collection_str); auto pos = comparator.find(_collection_str); if (pos == sstring::npos) { return; } pos += collection_str_length + 1; while (pos < comparator.size()) { size_t end = comparator.find('(', pos); if (end == sstring::npos) { throw marshal_exception(); } end = find_closing_parenthesis(comparator, end) + 1; auto colon = comparator.find(':', pos); if (colon == sstring::npos || colon > end) { throw marshal_exception(); } auto name = from_hex(sstring_view(comparator.c_str() + pos, colon - pos)); colon++; auto type_str = sstring_view(comparator.c_str() + colon, end - colon); auto type = db::marshal::type_parser::parse(type_str); builder.with_collection(name, type); if (end < comparator.size() && comparator[end] == ',') { pos = end + 1; } else if (end < comparator.size() && comparator[end] == ')') { pos = sstring::npos; } else { throw marshal_exception(); } } } } schema::const_iterator schema::regular_begin() const { return regular_columns().begin(); } schema::const_iterator schema::regular_end() const { return regular_columns().end(); } schema::const_iterator schema::regular_lower_bound(const bytes& name) const { // TODO: use regular_columns and a version of std::lower_bound() with heterogeneous comparator auto i = _regular_columns_by_name.lower_bound(name); if (i == _regular_columns_by_name.end()) { return regular_end(); } else { return regular_begin() + i->second->id; } } schema::const_iterator schema::regular_upper_bound(const bytes& name) const { // TODO: use regular_columns and a version of std::upper_bound() with heterogeneous comparator auto i = _regular_columns_by_name.upper_bound(name); if (i == _regular_columns_by_name.end()) { return regular_end(); } else { return regular_begin() + i->second->id; } } data_type schema::column_name_type(const column_definition& def) const { return def.kind == column_kind::regular_column ? _raw._regular_column_name_type : utf8_type; } const column_definition& schema::regular_column_at(column_id id) const { if (id > regular_columns_count()) { throw std::out_of_range("column_id"); } return _raw._columns.at(column_offset(column_kind::regular_column) + id); } const column_definition& schema::static_column_at(column_id id) const { if (id > static_columns_count()) { throw std::out_of_range("column_id"); } return _raw._columns.at(column_offset(column_kind::static_column) + id); } bool schema::is_last_partition_key(const column_definition& def) const { return &_raw._columns.at(partition_key_size() - 1) == &def; } bool schema::has_static_columns() const { return !static_columns().empty(); } column_count_type schema::partition_key_size() const { return column_offset(column_kind::clustering_key); } column_count_type schema::clustering_key_size() const { return column_offset(column_kind::static_column) - column_offset(column_kind::clustering_key); } column_count_type schema::static_columns_count() const { return column_offset(column_kind::regular_column) - column_offset(column_kind::static_column); } column_count_type schema::regular_columns_count() const { return _raw._columns.size() - column_offset(column_kind::regular_column); } schema::const_iterator_range_type schema::partition_key_columns() const { return boost::make_iterator_range(_raw._columns.begin() + column_offset(column_kind::partition_key) , _raw._columns.begin() + column_offset(column_kind::clustering_key)); } schema::const_iterator_range_type schema::clustering_key_columns() const { return boost::make_iterator_range(_raw._columns.begin() + column_offset(column_kind::clustering_key) , _raw._columns.begin() + column_offset(column_kind::static_column)); } schema::const_iterator_range_type schema::static_columns() const { return boost::make_iterator_range(_raw._columns.begin() + column_offset(column_kind::static_column) , _raw._columns.begin() + column_offset(column_kind::regular_column)); } schema::const_iterator_range_type schema::regular_columns() const { return boost::make_iterator_range(_raw._columns.begin() + column_offset(column_kind::regular_column) , _raw._columns.end()); } const schema::columns_type& schema::all_columns_in_select_order() const { return _raw._columns; } uint32_t schema::position(const column_definition& column) const { if (column.is_primary_key()) { return column.id; } return clustering_key_size(); } stdx::optional schema::find_index_noname(const index_metadata& target) const { const auto& it = boost::find_if(_raw._indices_by_name, [&] (auto&& e) { return e.second.equals_noname(target); }); if (it != _raw._indices_by_name.end()) { return it->second; } return {}; } std::vector schema::indices() const { return boost::copy_range>(_raw._indices_by_name | boost::adaptors::map_values); } bool schema::has_index(const sstring& index_name) const { return _raw._indices_by_name.count(index_name) > 0; } std::vector schema::index_names() const { return boost::copy_range>(_raw._indices_by_name | boost::adaptors::map_keys); } bool schema::is_synced() const { return _registry_entry && _registry_entry->is_synced(); } bool schema::equal_columns(const schema& other) const { return boost::equal(all_columns_in_select_order(), other.all_columns_in_select_order()); } raw_view_info::raw_view_info(utils::UUID base_id, sstring base_name, bool include_all_columns, sstring where_clause) : _base_id(std::move(base_id)) , _base_name(std::move(base_name)) , _include_all_columns(include_all_columns) , _where_clause(where_clause) { } bool operator==(const raw_view_info& x, const raw_view_info& y) { return x._base_id == y._base_id && x._base_name == y._base_name && x._include_all_columns == y._include_all_columns && x._where_clause == y._where_clause; } std::ostream& operator<<(std::ostream& os, const raw_view_info& view) { os << "ViewInfo{"; os << "baseTableId=" << view._base_id; os << ", baseTableName=" << view._base_name; os << ", includeAllColumns=" << view._include_all_columns; os << ", whereClause=" << view._where_clause; os << "}"; return os; } std::ostream& operator<<(std::ostream& os, const view_ptr& view) { return view ? os << *view : os << "null"; }