susumu.yata
null+****@clear*****
Thu Nov 6 19:51:13 JST 2014
susumu.yata 2014-11-06 19:51:13 +0900 (Thu, 06 Nov 2014) New Revision: b976fc5b085fdd4722585ae480dff6b398e1f741 https://github.com/groonga/grnxx/commit/b976fc5b085fdd4722585ae480dff6b398e1f741 Message: Add a draft version of Column<Int>. Modified files: lib/grnxx/impl/column/base.cpp lib/grnxx/impl/column/base.hpp lib/grnxx/impl/column/scalar.hpp lib/grnxx/impl/column/scalar/Makefile.am lib/grnxx/impl/column/scalar/int.cpp lib/grnxx/impl/column/scalar/int.hpp lib/grnxx/impl/table.cpp Modified: lib/grnxx/impl/column/base.cpp (+7 -7) =================================================================== --- lib/grnxx/impl/column/base.cpp 2014-11-06 19:47:48 +0900 (caf5474) +++ lib/grnxx/impl/column/base.cpp 2014-11-06 19:51:13 +0900 (cda986b) @@ -183,10 +183,10 @@ std::unique_ptr<ColumnBase> ColumnBase::create( column.reset(new impl::Column<Bool>(table, name, options)); break; } -// case INT_DATA: { -// column.reset(new impl::Column<Int>(table, name, options)); -// break; -// } + case INT_DATA: { + column.reset(new impl::Column<Int>(table, name, options)); + break; + } // case FLOAT_DATA: { // column.reset(new impl::Column<Float>(table, name, options)); // break; @@ -249,9 +249,9 @@ void ColumnBase::set_key(Int, const Datum &) { throw "Not supported"; // TODO } -void ColumnBase::clear_references(Int) { - throw "Not supported"; // TODO -} +//void ColumnBase::clear_references(Int) { +// throw "Not supported"; // TODO +//} //bool ColumnBase::initialize_base(Error *error, // Table *table, Modified: lib/grnxx/impl/column/base.hpp (+2 -2) =================================================================== --- lib/grnxx/impl/column/base.hpp 2014-11-06 19:47:48 +0900 (ec8cb58) +++ lib/grnxx/impl/column/base.hpp 2014-11-06 19:51:13 +0900 (1f9a90e) @@ -103,8 +103,8 @@ class ColumnBase : public ColumnInterface { // Unset the value. virtual void unset(Int row_id) = 0; - // Replace references to "row_id" with NULL. - virtual void clear_references(Int row_id); +// // Replace references to "row_id" with NULL. +// virtual void clear_references(Int row_id); protected: Table *table_; Modified: lib/grnxx/impl/column/scalar.hpp (+1 -1) =================================================================== --- lib/grnxx/impl/column/scalar.hpp 2014-11-06 19:47:48 +0900 (1b33481) +++ lib/grnxx/impl/column/scalar.hpp 2014-11-06 19:51:13 +0900 (a2f7635) @@ -4,7 +4,7 @@ #include "grnxx/impl/column/scalar/bool.hpp" //#include "grnxx/impl/column/scalar/float.hpp" //#include "grnxx/impl/column/scalar/geo_point.hpp" -//#include "grnxx/impl/column/scalar/int.hpp" +#include "grnxx/impl/column/scalar/int.hpp" //#include "grnxx/impl/column/scalar/text.hpp" #endif // GRNXX_IMPL_COLUMN_SCALAR_HPP Modified: lib/grnxx/impl/column/scalar/Makefile.am (+2 -2) =================================================================== --- lib/grnxx/impl/column/scalar/Makefile.am 2014-11-06 19:47:48 +0900 (690cb71) +++ lib/grnxx/impl/column/scalar/Makefile.am 2014-11-06 19:51:13 +0900 (491594f) @@ -9,11 +9,11 @@ lib_LTLIBRARIES = libgrnxx_impl_column_scalar.la libgrnxx_impl_column_scalar_la_LDFLAGS = @AM_LTLDFLAGS@ libgrnxx_impl_column_scalar_la_SOURCES = \ - bool.cpp + bool.cpp \ + int.cpp # float.cpp \ # geo_point.cpp \ -# int.cpp \ # text.cpp libgrnxx_impl_column_scalar_includedir = ${includedir}/grnxx/impl/column/scalar Modified: lib/grnxx/impl/column/scalar/int.cpp (+245 -226) =================================================================== --- lib/grnxx/impl/column/scalar/int.cpp 2014-11-06 19:47:48 +0900 (bb80bfa) +++ lib/grnxx/impl/column/scalar/int.cpp 2014-11-06 19:51:13 +0900 (7fd800b) @@ -1,279 +1,298 @@ -#include "grnxx/impl/column/column_int.hpp" +#include "grnxx/impl/column/scalar/int.hpp" -#include "grnxx/cursor.hpp" #include "grnxx/impl/db.hpp" #include "grnxx/impl/table.hpp" -#include "grnxx/index.hpp" +//#include "grnxx/impl/index.hpp" -#include <unordered_set> +//#include <unordered_set> namespace grnxx { namespace impl { -bool Column<Int>::set(Error *error, Int row_id, const Datum &datum) { - if (datum.type() != INT_DATA) { - GRNXX_ERROR_SET(error, INVALID_ARGUMENT, "Wrong data type"); - return false; - } - if (!table_->test_row(error, row_id)) { - return false; - } - if (ref_table_) { - if (!ref_table_->test_row(error, datum.force_int())) { - return false; - } - } - Int old_value = get(row_id); - Int new_value = datum.force_int(); - if (new_value != old_value) { - if (has_key_attribute_ && contains(datum)) { - GRNXX_ERROR_SET(error, ALREADY_EXISTS, "Key duplicate"); - return false; - } - for (Int i = 0; i < num_indexes(); ++i) { - if (!indexes_[i]->insert(error, row_id, datum)) { - for (Int j = 0; j < i; ++i) { - indexes_[j]->remove(nullptr, row_id, datum); - } - return false; - } - } - for (Int i = 0; i < num_indexes(); ++i) { - indexes_[i]->remove(nullptr, row_id, old_value); +Column<Int>::Column(Table *table, + const String &name, + const ColumnOptions &options) + : ColumnBase(table, name, INT_DATA), + values_() { + if (!options.reference_table_name.is_empty()) { + reference_table_ = table->_db()->find_table(options.reference_table_name); + if (!reference_table_) { + throw "Table not found"; // TODO } } - values_.set(row_id, new_value); - return true; } -bool Column<Int>::get(Error *error, Int row_id, Datum *datum) const { - if (!table_->test_row(error, row_id)) { - return false; - } - *datum = values_[row_id]; - return true; -} +Column<Int>::~Column() {} -unique_ptr<Column<Int>> Column<Int>::create( - Error *error, - Table *table, - const StringCRef &name, - const ColumnOptions &options) { - unique_ptr<Column> column(new (nothrow) Column); - if (!column) { - GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed"); - return nullptr; +void Column<Int>::set(Int row_id, const Datum &datum) { + Int new_value = parse_datum(datum); + if (!table_->test_row(row_id)) { + throw "Invalid row ID"; // TODO } - if (!column->initialize_base(error, table, name, INT_DATA, options)) { - return nullptr; + if (is_key_) { + if (new_value.is_na()) { + throw "N/A key"; // TODO + } } - if (!column->values_.resize(error, table->max_row_id() + 1, - TypeTraits<Int>::default_value())) { - return nullptr; + if (new_value.is_na()) { + unset(row_id); + return; } - if (column->ref_table()) { - if (!column->ref_table_->append_referrer_column(error, column.get())) { - return nullptr; + if (reference_table_) { + if (!reference_table_->test_row(new_value)) { + throw "Invalid reference"; // TODO } } - return column; -} - -Column<Int>::~Column() {} - -bool Column<Int>::set_key_attribute(Error *error) { - if (has_key_attribute_) { - GRNXX_ERROR_SET(error, INVALID_OPERATION, - "This column is a key column"); - return false; + Int old_value = get(row_id); + if (old_value == new_value) { + return; } - // TODO: An index should be used if possible. - try { - std::unordered_set<Int> set; - // TODO: Functor-based inline callback may be better in this case, - // because it does not require memory allocation. - auto cursor = table_->create_cursor(nullptr); - if (!cursor) { - return false; - } - Array<Record> records; - for ( ; ; ) { - auto result = cursor->read(nullptr, 1024, &records); - if (!result.is_ok) { - return false; - } else { - break; - } - for (Int i = 0; i < result.count; ++i) { - if (!set.insert(values_[records.get_row_id(i)]).second) { - GRNXX_ERROR_SET(error, INVALID_OPERATION, "Key duplicate"); - return false; - } - } - records.clear(); - } - } catch (...) { - GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed"); - return false; + if (is_key_ && contains(datum)) { + throw "Key already exists"; // TODO + } + if (!old_value.is_na()) { + // TODO: Remove the old value from indexes. +// for (size_t i = 0; i < num_indexes(); ++i) { +// indexes_[i]->remove(row_id, old_value); +// } + } + size_t value_id = row_id.value(); + if (value_id >= values_.size()) { + values_.resize(value_id + 1, Int::na()); } - has_key_attribute_ = true; - return true; + // TODO: Insert the new value into indexes. +// for (size_t i = 0; i < num_indexes(); ++i) try { +// indexes_[i]->insert(row_id, datum)) { +// } catch (...) { +// for (size_t j = 0; j < i; ++i) { +// indexes_[j]->remove(row_id, datum); +// } +// throw; +// } + values_[value_id] = new_value; } -bool Column<Int>::unset_key_attribute(Error *error) { - if (!has_key_attribute_) { - GRNXX_ERROR_SET(error, INVALID_OPERATION, - "This column is not a key column"); - return false; +void Column<Int>::get(Int row_id, Datum *datum) const { + size_t value_id = row_id.value(); + if (value_id >= values_.size()) { + *datum = Int::na(); + } else { + *datum = values_[value_id]; } - has_key_attribute_ = false; - return true; } -bool Column<Int>::set_initial_key(Error *error, - Int row_id, - const Datum &key) { - if (!has_key_attribute_) { - GRNXX_ERROR_SET(error, INVALID_OPERATION, - "This column is not a key column"); - return false; - } - if (has_key_attribute_ && contains(key)) { - GRNXX_ERROR_SET(error, ALREADY_EXISTS, "Key duplicate"); - return false; - } - if (row_id >= values_.size()) { - if (!values_.resize(error, row_id + 1, TypeTraits<Int>::default_value())) { - return false; +bool Column<Int>::contains(const Datum &datum) const { + // TODO: Use an index if exists. + Int value = parse_datum(datum); + if (value.is_na()) { + for (size_t i = 0; i < values_.size(); ++i) { + if (values_[i].is_na() && table_->_test_row(i)) { + return true; + } } - } - Int value = key.force_int(); - for (Int i = 0; i < num_indexes(); ++i) { - if (!indexes_[i]->insert(error, row_id, value)) { - for (Int j = 0; j < i; ++j) { - indexes_[j]->remove(nullptr, row_id, value); + } else { + for (size_t i = 0; i < values_.size(); ++i) { + if (values_[i].value() == value.value()) { + return true; } - return false; } } - values_.set(row_id, value); - return true; + return false; } -bool Column<Int>::set_default_value(Error *error, Int row_id) { - if (has_key_attribute_) { - GRNXX_ERROR_SET(error, INVALID_OPERATION, - "This column is a key column"); - return false; - } - if (row_id >= values_.size()) { - if (!values_.resize(error, row_id + 1, TypeTraits<Int>::default_value())) { - return false; +Int Column<Int>::find_one(const Datum &datum) const { + // TODO: Use an index if exists. + Int value = parse_datum(datum); + if (value.is_na()) { + for (size_t i = 0; i < values_.size(); ++i) { + if (values_[i].is_na() && table_->_test_row(i)) { + return Int(i); + } } - } - Int value = TypeTraits<Int>::default_value(); - for (Int i = 0; i < num_indexes(); ++i) { - if (!indexes_[i]->insert(error, row_id, value)) { - for (Int j = 0; j < i; ++j) { - indexes_[j]->remove(nullptr, row_id, value); + } else { + for (size_t i = 0; i < values_.size(); ++i) { + if (values_[i].value() == value.value()) { + return Int(i); } - return false; } } - values_.set(row_id, value); - return true; + return Int::na(); + +// // TODO: Cursor should not be used because it takes time. +// // Also, cursor operations can fail due to memory allocation. +// Int value = datum.force_int(); +// if (indexes_.size() != 0) { +// return indexes_[0]->find_one(datum); +// } else { +// // TODO: A full scan takes time. +// // An index should be required for a key column. + +// // TODO: Functor-based inline callback may be better in this case, +// // because it does not require memory allocation. + +// // Scan the column to find "value". +// auto cursor = table_->create_cursor(nullptr); +// if (!cursor) { +// return NULL_ROW_ID; +// } +// Array<Record> records; +// for ( ; ; ) { +// auto result = cursor->read(nullptr, 1024, &records); +// if (!result.is_ok || result.count == 0) { +// return NULL_ROW_ID; +// } +// for (Int i = 0; i < result.count; ++i) { +// if (values_[records.get_row_id(i)] == value) { +// return records.get_row_id(i); +// } +// } +// records.clear(); +// } +// } +// return NULL_ROW_ID; } -void Column<Int>::unset(Int row_id) { - for (Int i = 0; i < num_indexes(); ++i) { - indexes_[i]->remove(nullptr, row_id, get(row_id)); +void Column<Int>::set_key_attribute() { + if (is_key_) { + throw "Key column"; // TODO } - values_.set(row_id, TypeTraits<Int>::default_value()); + throw "Not supported yet"; // TODO + +// // TODO: An index should be used if possible. +// try { +// std::unordered_set<Int> set; +// // TODO: Functor-based inline callback may be better in this case, +// // because it does not require memory allocation. +// auto cursor = table_->create_cursor(nullptr); +// if (!cursor) { +// return false; +// } +// Array<Record> records; +// for ( ; ; ) { +// auto result = cursor->read(nullptr, 1024, &records); +// if (!result.is_ok) { +// return false; +// } else { +// break; +// } +// for (Int i = 0; i < result.count; ++i) { +// if (!set.insert(values_[records.get_row_id(i)]).second) { +// GRNXX_ERROR_SET(error, INVALID_OPERATION, "Key duplicate"); +// return false; +// } +// } +// records.clear(); +// } +// } catch (...) { +// GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed"); +// return false; +// } +// has_key_attribute_ = true; +// return true; } -Int Column<Int>::find_one(const Datum &datum) const { - // TODO: Cursor should not be used because it takes time. - // Also, cursor operations can fail due to memory allocation. - Int value = datum.force_int(); - if (indexes_.size() != 0) { - return indexes_[0]->find_one(datum); - } else { - // TODO: A full scan takes time. - // An index should be required for a key column. +void Column<Int>::unset_key_attribute() { + if (!is_key_) { + throw "Not key column"; // TODO + } + is_key_ = true; +} - // TODO: Functor-based inline callback may be better in this case, - // because it does not require memory allocation. +void Column<Int>::set_key(Int row_id, const Datum &key) { + if (!is_key_) { + throw "Not key column"; // TODO + } + if (contains(key)) { + throw "Key already exists"; // TODO + } + size_t value_id = row_id.value(); + if (value_id >= values_.size()) { + values_.resize(value_id + 1, Int::na()); + } + Int value = parse_datum(key); + // TODO: Update indexes if exist. +// for (size_t i = 0; i < num_indexes(); ++i) try { +// indexes_[i]->insert(row_id, value); +// } catch (...) { +// for (size_t j = 0; j < i; ++j) { +// indexes_[j]->remove(row_id, value); +// } +// throw; +// } + values_[value_id] = value; +} - // Scan the column to find "value". - auto cursor = table_->create_cursor(nullptr); - if (!cursor) { - return NULL_ROW_ID; - } - Array<Record> records; - for ( ; ; ) { - auto result = cursor->read(nullptr, 1024, &records); - if (!result.is_ok || result.count == 0) { - return NULL_ROW_ID; - } - for (Int i = 0; i < result.count; ++i) { - if (values_[records.get_row_id(i)] == value) { - return records.get_row_id(i); - } - } - records.clear(); - } +void Column<Int>::unset(Int row_id) { + Int value = get(row_id); + if (!value.is_na()) { + // TODO: Update indexes if exist. +// for (size_t i = 0; i < num_indexes(); ++i) { +// indexes_[i]->remove(row_id, value); +// } + values_[row_id.value()] = Int::na(); } - return NULL_ROW_ID; } -void Column<Int>::clear_references(Int row_id) { - // TODO: Cursor should not be used to avoid errors. - if (indexes_.size() != 0) { - auto cursor = indexes_[0]->find(nullptr, Int(0)); - if (!cursor) { - // Error. - return; - } - Array<Record> records; - for ( ; ; ) { - auto result = cursor->read(nullptr, 1024, &records); - if (!result.is_ok) { - // Error. - return; - } else if (result.count == 0) { - return; - } - for (Int i = 0; i < records.size(); ++i) { - set(nullptr, row_id, NULL_ROW_ID); - } - records.clear(); +//void Column<Int>::clear_references(Int row_id) { +// // TODO: Cursor should not be used to avoid errors. +// if (indexes_.size() != 0) { +// auto cursor = indexes_[0]->find(nullptr, Int(0)); +// if (!cursor) { +// // Error. +// return; +// } +// Array<Record> records; +// for ( ; ; ) { +// auto result = cursor->read(nullptr, 1024, &records); +// if (!result.is_ok) { +// // Error. +// return; +// } else if (result.count == 0) { +// return; +// } +// for (Int i = 0; i < records.size(); ++i) { +// set(nullptr, row_id, NULL_ROW_ID); +// } +// records.clear(); +// } +// } else { +// auto cursor = table_->create_cursor(nullptr); +// if (!cursor) { +// // Error. +// return; +// } +// Array<Record> records; +// for ( ; ; ) { +// auto result = cursor->read(nullptr, 1024, &records); +// if (!result.is_ok) { +// // Error. +// return; +// } else if (result.count == 0) { +// return; +// } +// for (Int i = 0; i < records.size(); ++i) { +// if (values_[records.get_row_id(i)] == row_id) { +// values_[records.get_row_id(i)] = NULL_ROW_ID; +// } +// } +// records.clear(); +// } +// } +//} + +Int Column<Int>::parse_datum(const Datum &datum) { + switch (datum.type()) { + case NA_DATA: { + return Int::na(); } - } else { - auto cursor = table_->create_cursor(nullptr); - if (!cursor) { - // Error. - return; + case INT_DATA: { + return datum.as_int(); } - Array<Record> records; - for ( ; ; ) { - auto result = cursor->read(nullptr, 1024, &records); - if (!result.is_ok) { - // Error. - return; - } else if (result.count == 0) { - return; - } - for (Int i = 0; i < records.size(); ++i) { - if (values_[records.get_row_id(i)] == row_id) { - values_[records.get_row_id(i)] = NULL_ROW_ID; - } - } - records.clear(); + default: { + throw "Wrong data type"; // TODO } } } -Column<Int>::Column() : ColumnBase(), values_() {} - } // namespace impl } // namespace grnxx Modified: lib/grnxx/impl/column/scalar/int.hpp (+28 -32) =================================================================== --- lib/grnxx/impl/column/scalar/int.hpp 2014-11-06 19:47:48 +0900 (5046b64) +++ lib/grnxx/impl/column/scalar/int.hpp 2014-11-06 19:51:13 +0900 (d8cff6e) @@ -1,62 +1,58 @@ #ifndef GRNXX_IMPL_COLUMN_SCALAR_INT_HPP #define GRNXX_IMPL_COLUMN_SCALAR_INT_HPP -#include "grnxx/impl/column/column.hpp" +#include "grnxx/impl/column/base.hpp" namespace grnxx { namespace impl { +template <typename T> class Column; + template <> class Column<Int> : public ColumnBase { public: - // -- Public API -- + // -- Public API (grnxx/column.hpp) -- - bool set(Error *error, Int row_id, const Datum &datum); - bool get(Error *error, Int row_id, Datum *datum) const; + Column(Table *table, const String &name, const ColumnOptions &options); + ~Column(); - // -- Internal API -- + void set(Int row_id, const Datum &datum); + void get(Int row_id, Datum *datum) const; - // Create a new column. - // - // Returns a pointer to the column on success. - // On failure, returns nullptr and stores error information into "*error" if - // "error" != nullptr. - static unique_ptr<Column> create(Error *error, - Table *table, - const StringCRef &name, - const ColumnOptions &options); + bool contains(const Datum &datum) const; + Int find_one(const Datum &datum) const; - ~Column(); + // -- Internal API (grnxx/impl/column/base.hpp) -- - bool set_key_attribute(Error *error); - bool unset_key_attribute(Error *error); + void set_key_attribute(); + void unset_key_attribute(); - bool set_initial_key(Error *error, Int row_id, const Datum &key); - bool set_default_value(Error *error, Int row_id); + void set_key(Int row_id, const Datum &key); void unset(Int row_id); - - Int find_one(const Datum &datum) const; - void clear_references(Int row_id); - // Return a value identified by "row_id". + // -- Internal API -- + + // Return a value. // - // Assumes that "row_id" is valid. Otherwise, the result is undefined. + // If "row_id" is valid, returns the stored value. + // If "row_id" is invalid, returns N/A. Int get(Int row_id) const { - return values_[row_id]; - } - - // Read values. - void read(ArrayCRef<Record> records, ArrayRef<Int> values) const { - for (Int i = 0; i < records.size(); ++i) { - values.set(i, get(records.get_row_id(i))); + size_t value_id = row_id.value(); + if (value_id >= values_.size()) { + return Int::na(); } + return values_[value_id]; } + // Read values. + // + // On failure, throws an exception. + void read(ArrayCRef<Record> records, ArrayRef<Bool> values) const; private: Array<Int> values_; - Column(); + static Int parse_datum(const Datum &datum); }; } // namespace impl Modified: lib/grnxx/impl/table.cpp (+8 -4) =================================================================== --- lib/grnxx/impl/table.cpp 2014-11-06 19:47:48 +0900 (5a26a42) +++ lib/grnxx/impl/table.cpp 2014-11-06 19:51:13 +0900 (8c1f585) @@ -235,6 +235,9 @@ ColumnBase *Table::create_column(const String &name, columns_.reserve(columns_.size() + 1); std::unique_ptr<ColumnBase> new_column = ColumnBase::create(this, name, data_type, options); + if (new_column->_reference_table()) { + new_column->_reference_table()->append_referrer_column(new_column.get()); + } columns_.push_back(std::move(new_column)); return columns_.back().get(); } @@ -398,10 +401,11 @@ void Table::remove_row(Int row_id) { columns_[i]->unset(row_id); } invalidate_row(row_id); - // Clear referrers. - for (size_t i = 0; i < referrer_columns_.size(); ++i) { - referrer_columns_[i]->clear_references(row_id); - } + + // TODO: Clear referrers. +// for (size_t i = 0; i < referrer_columns_.size(); ++i) { +// referrer_columns_[i]->clear_references(row_id); +// } } Int Table::find_row(const Datum &key) const { -------------- next part -------------- HTML����������������������������...ダウンロード