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

Back to archive index

susumu.yata null+****@clear*****
Wed Oct 1 10:49:48 JST 2014


susumu.yata	2014-10-01 10:49:48 +0900 (Wed, 01 Oct 2014)

  New Revision: fb5870374e77e67cede72feb91d0dcd5d4efb96a
  https://github.com/groonga/grnxx/commit/fb5870374e77e67cede72feb91d0dcd5d4efb96a

  Message:
    Implement key attribute for Column<Text>. (#70)

  Modified files:
    lib/grnxx/column.cpp
    lib/grnxx/column_impl.hpp

  Modified: lib/grnxx/column.cpp (+150 -0)
===================================================================
--- lib/grnxx/column.cpp    2014-09-30 17:46:29 +0900 (ca1c5e6)
+++ lib/grnxx/column.cpp    2014-10-01 10:49:48 +0900 (2070201)
@@ -6,6 +6,7 @@
 #include "grnxx/index.hpp"
 #include "grnxx/table.hpp"
 
+#include <set>
 #include <unordered_set>
 
 namespace grnxx {
@@ -564,6 +565,10 @@ bool ColumnImpl<Text>::set(Error *error, Int row_id, const Datum &datum) {
   Text old_value = get(row_id);
   Text new_value = datum.force_text();
   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) {
@@ -630,7 +635,111 @@ unique_ptr<ColumnImpl<Text>> ColumnImpl<Text>::create(
 
 ColumnImpl<Text>::~ColumnImpl() {}
 
+bool ColumnImpl<Text>::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::set<Text> 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(get(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<Text>::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<Text>::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 >= headers_.size()) {
+    if (!headers_.resize(error, row_id + 1, 0)) {
+      return false;
+    }
+  }
+  Text value = key.force_text();
+  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;
+    }
+  }
+  Int offset = bodies_.size();
+  UInt header;
+  if (value.size() < 0xFFFF) {
+    if (!bodies_.resize(error, offset + value.size())) {
+      return false;
+    }
+    std::memcpy(&bodies_[offset], value.data(), value.size());
+    header = (offset << 16) | value.size();
+  } else {
+    // The size of a long text is stored in front of the body.
+    if ((offset % sizeof(Int)) != 0) {
+      offset += sizeof(Int) - (offset % sizeof(Int));
+    }
+    if (!bodies_.resize(error, offset + sizeof(Int) + value.size())) {
+      return false;
+    }
+    *reinterpret_cast<Int *>(&bodies_[offset]) = value.size();
+    std::memcpy(&bodies_[offset + sizeof(Int)], value.data(), value.size());
+    header = (offset << 16) | 0xFFFF;
+  }
+  headers_[row_id] = header;
+  return true;
+}
+
 bool ColumnImpl<Text>::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 >= headers_.size()) {
     if (!headers_.resize(error, row_id + 1)) {
       return false;
@@ -656,6 +765,47 @@ void ColumnImpl<Text>::unset(Int row_id) {
   headers_[row_id] = 0;
 }
 
+Int ColumnImpl<Text>::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.
+  Text value = datum.force_text();
+  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 (get(records.get_row_id(i)) == value) {
+          return records.get_row_id(i);
+        }
+      }
+      records.clear();
+    }
+  }
+  return NULL_ROW_ID;
+}
+
 ColumnImpl<Text>::ColumnImpl() : Column(), headers_(), bodies_() {}
 
 // -- ColumnImpl<Vector<Int>> --

  Modified: lib/grnxx/column_impl.hpp (+5 -0)
===================================================================
--- lib/grnxx/column_impl.hpp    2014-09-30 17:46:29 +0900 (9a1ef95)
+++ lib/grnxx/column_impl.hpp    2014-10-01 10:49:48 +0900 (735fa41)
@@ -122,8 +122,13 @@ class ColumnImpl<Text> : 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".
   //
-------------- next part --------------
HTML����������������������������...
Télécharger 



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