susumu.yata
null+****@clear*****
Tue Sep 30 17:10:32 JST 2014
susumu.yata 2014-09-30 17:10:32 +0900 (Tue, 30 Sep 2014) New Revision: 416d613e888bc60b867716beff95d51c93892087 https://github.com/groonga/grnxx/commit/416d613e888bc60b867716beff95d51c93892087 Message: Implement key attribute for Column<Int>. (#70) Modified files: include/grnxx/column.hpp lib/grnxx/column.cpp lib/grnxx/column_impl.hpp lib/grnxx/table.cpp Modified: include/grnxx/column.hpp (+17 -0) =================================================================== --- include/grnxx/column.hpp 2014-09-26 16:24:53 +0900 (cf2a86e) +++ include/grnxx/column.hpp 2014-09-30 17:10:32 +0900 (723450a) @@ -114,6 +114,18 @@ class Column { // "error" != nullptr. virtual bool get(Error *error, Int row_id, Datum *datum) const; + // Check if "datum" exists in the column or not. + // + // If exists, returns true. + // Otherwise, returns false. + virtual bool contains(const Datum &datum) const; + + // Find "datum" in the column. + // + // If found, returns the row ID of the matched value. + // Otherwise, returns NULL_ROW_ID. + virtual Int find_one(const Datum &datum) const; + protected: Table *table_; Name name_; @@ -158,6 +170,11 @@ class Column { // Return whether the column is removable or not. bool is_removable(); + // Set the key attribute. + virtual bool set_key_attribute(Error *error); + // Unset the key attribute. + virtual bool unset_key_attribute(Error *error); + // Set the initial key. // // On success, returns true. Modified: lib/grnxx/column.cpp (+150 -0) =================================================================== --- lib/grnxx/column.cpp 2014-09-26 16:24:53 +0900 (a567256) +++ lib/grnxx/column.cpp 2014-09-30 17:10:32 +0900 (627b826) @@ -6,6 +6,8 @@ #include "grnxx/index.hpp" #include "grnxx/table.hpp" +#include <unordered_set> + namespace grnxx { Column::~Column() {} @@ -114,6 +116,15 @@ bool Column::get(Error *error, Int, Datum *) const { return false; } +bool Column::contains(const Datum &datum) const { + return find_one(datum) != NULL_ROW_ID; +} + +Int Column::find_one(const Datum &) const { + // TODO: This function should be pure virtual. + return NULL_ROW_ID; +} + unique_ptr<Column> Column::create(Error *error, Table *table, const StringCRef &name, @@ -202,6 +213,16 @@ bool Column::set_initial_key(Error *error, Int, const Datum &) { return false; } +bool Column::set_key_attribute(Error *error) { + GRNXX_ERROR_SET(error, INVALID_OPERATION, "This type does not support Key"); + return false; +} + +bool Column::unset_key_attribute(Error *error) { + GRNXX_ERROR_SET(error, INVALID_OPERATION, "This type does not support Key"); + return false; +} + Index *Column::find_index_with_id(Error *error, const StringCRef &name, Int *index_id) const { @@ -333,6 +354,10 @@ bool ColumnImpl<Int>::set(Error *error, Int row_id, const Datum &datum) { 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) { @@ -379,7 +404,91 @@ unique_ptr<ColumnImpl<Int>> ColumnImpl<Int>::create( ColumnImpl<Int>::~ColumnImpl() {} +bool ColumnImpl<Int>::set_key_attribute(Error *error) { + if (has_key_attribute_) { + GRNXX_ERROR_SET(error, INVALID_OPERATION, + "This column is a key column"); + return false; + } + // 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; +} + +bool ColumnImpl<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; + } + has_key_attribute_ = false; + return true; +} + +bool ColumnImpl<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; + } + } + 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); + } + return false; + } + } + values_.set(row_id, value); + return true; +} + bool ColumnImpl<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; @@ -405,6 +514,47 @@ void ColumnImpl<Int>::unset(Int row_id) { values_.set(row_id, TypeTraits<Int>::default_value()); } +Int ColumnImpl<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) { + auto cursor = indexes_[0]->find(nullptr, value); + Array<Record> records; + auto result = cursor->read(nullptr, 1, &records); + if (!result.is_ok || (result.count == 0)) { + return NULL_ROW_ID; + } + return true; + } 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; +} + ColumnImpl<Int>::ColumnImpl() : Column(), values_() {} // -- ColumnImpl<Text> -- Modified: lib/grnxx/column_impl.hpp (+5 -0) =================================================================== --- lib/grnxx/column_impl.hpp 2014-09-26 16:24:53 +0900 (dbf4903) +++ lib/grnxx/column_impl.hpp 2014-09-30 17:10:32 +0900 (9a1ef95) @@ -72,8 +72,13 @@ class ColumnImpl<Int> : public Column { ~ColumnImpl(); + bool set_key_attribute(Error *error); + bool unset_key_attribute(Error *error); + + bool set_initial_key(Error *error, Int row_id, const Datum &key); bool set_default_value(Error *error, Int row_id); void unset(Int row_id); + Int find_one(const Datum &datum) const; // Return a value identified by "row_id". // Modified: lib/grnxx/table.cpp (+20 -23) =================================================================== --- lib/grnxx/table.cpp 2014-09-26 16:24:53 +0900 (5380ae8) +++ lib/grnxx/table.cpp 2014-09-30 17:10:32 +0900 (b46b3a4) @@ -243,10 +243,9 @@ bool Table::remove_column(Error *error, const StringCRef &name) { static_cast<int>(name.size()), name.data()); return false; } - // TODO: Clear key_column_ if the specified column is the key column. -// if (columns_[column_id].get() == key_column_) { -// key_column_ = nullptr; -// } + if (columns_[column_id].get() == key_column_) { + key_column_ = nullptr; + } columns_.erase(column_id); return true; } @@ -309,22 +308,20 @@ Column *Table::find_column(Error *error, const StringCRef &name) const { return nullptr; } -bool Table::set_key_column(Error *error, const StringCRef &) { +bool Table::set_key_column(Error *error, const StringCRef &name) { if (key_column_) { GRNXX_ERROR_SET(error, ALREADY_EXISTS, "Key column already exists"); return false; } - - // TODO: Check if the column exists or not. - - // TODO: Check if the data type is supported or not. - - // TODO: Check if there are duplicate values or not. - // Use an index if exists. - - // TODO: Set the key attribute to the column. - - return false; + Column *column = find_column(error, name); + if (!column) { + return false; + } + if (!column->set_key_attribute(error)) { + return false; + } + key_column_ = column; + return true; } bool Table::unset_key_column(Error *error) { @@ -332,10 +329,11 @@ bool Table::unset_key_column(Error *error) { GRNXX_ERROR_SET(error, NOT_FOUND, "Key column not found"); return false; } - - // TODO: Unset the key attribute of the key column. - - return false; + if (!key_column_->unset_key_attribute(error)) { + return false; + } + key_column_ = nullptr; + return true; } bool Table::insert_row(Error *error, @@ -448,13 +446,12 @@ bool Table::test_row(Error *error, Int row_id) const { return true; } -Int Table::find_row(Error *error, const Datum &) const { +Int Table::find_row(Error *error, const Datum &key) const { if (!key_column_) { GRNXX_ERROR_SET(error, NO_KEY_COLUMN, "No key column"); return NULL_ROW_ID; } - // TODO: Key column is not supported yet. - return NULL_ROW_ID; + return key_column_->find_one(key); } unique_ptr<Cursor> Table::create_cursor( -------------- next part -------------- HTML����������������������������... Download