[Groonga-commit] groonga/grnxx at 416d613 [master] Implement key attribute for Column<Int>. (#70)

Back to archive index

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 



More information about the Groonga-commit mailing list
Back to archive index