[Groonga-commit] groonga/grnxx at cd812c3 [master] Support primary key.

Back to archive index

susumu.yata null+****@clear*****
Mon Mar 17 18:24:46 JST 2014


susumu.yata	2014-03-17 18:24:46 +0900 (Mon, 17 Mar 2014)

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

  Message:
    Support primary key.

  Modified files:
    lib/grnxx/column.cpp
    lib/grnxx/column.hpp
    lib/grnxx/column_impl.cpp
    lib/grnxx/column_impl.hpp
    lib/grnxx/table.cpp
    lib/grnxx/table.hpp
    src/grnxx.cpp
    test/test_grnxx.cpp

  Modified: lib/grnxx/column.cpp (+34 -1)
===================================================================
--- lib/grnxx/column.cpp    2014-03-13 13:39:04 +0900 (086d204)
+++ lib/grnxx/column.cpp    2014-03-17 18:24:46 +0900 (bb062b6)
@@ -12,11 +12,44 @@ Column::Column(Table *table, ColumnID id, const String &name,
     : table_(table),
       id_(id),
       name_(reinterpret_cast<const char *>(name.data()), name.size()),
-      data_type_(data_type) {}
+      data_type_(data_type),
+      is_unique_(false) {}
 
 // カラムを破棄する.
 Column::~Column() {}
 
+// UNIQUE 制約を解除する.
+bool Column::unset_unique() {
+  if (!is_unique_) {
+    return false;
+  }
+  is_unique_ = false;
+  return true;
+}
+
+// FIXME: 指定された値を検索する.
+RowID Column::generic_find(const Datum &datum) const {
+  switch (data_type()) {
+    case BOOLEAN: {
+      auto impl = static_cast<const ColumnImpl<Boolean> *>(this);
+      return impl->find(static_cast<Boolean>(datum));
+    }
+    case INTEGER: {
+      auto impl = static_cast<const ColumnImpl<Int64> *>(this);
+      return impl->find(static_cast<Int64>(datum));
+    }
+    case FLOAT: {
+      auto impl = static_cast<const ColumnImpl<Float> *>(this);
+      return impl->find(static_cast<Float>(datum));
+    }
+    case STRING: {
+      auto impl = static_cast<const ColumnImpl<String> *>(this);
+      return impl->find(static_cast<std::string>(datum));
+    }
+  }
+  return false;
+}
+
 // FIXME: 指定された ID の値を返す.
 Datum Column::generic_get(RowID row_id) const {
   switch (data_type()) {

  Modified: lib/grnxx/column.hpp (+12 -0)
===================================================================
--- lib/grnxx/column.hpp    2014-03-13 13:39:04 +0900 (04111e7)
+++ lib/grnxx/column.hpp    2014-03-17 18:24:46 +0900 (34f0b3d)
@@ -28,6 +28,15 @@ class Column {
   DataType data_type() const {
     return data_type_;
   }
+  // UNIQUE 制約の有無を返す.
+  bool is_unique() const {
+    return is_unique_;
+  }
+
+  // UNIQUE 制約を設定する.
+  virtual bool set_unique() = 0;
+  // UNIQUE 制約を解除する.
+  virtual bool unset_unique();
 
   // 指定された索引との関連付けをおこなう.
   virtual bool register_index(Index *index) = 0;
@@ -37,6 +46,8 @@ class Column {
   // 指定された行 ID が使えるようにサイズを変更する.
   virtual void resize(RowID max_row_id) = 0;
 
+  // FIXME: 指定された値を検索する.
+  virtual RowID generic_find(const Datum &datum) const;
   // FIXME: 指定された ID の値を返す.
   virtual Datum generic_get(RowID row_id) const;
   // FIXME: 指定された ID の値を設定する.
@@ -50,6 +61,7 @@ class Column {
   ColumnID id_;
   std::string name_;
   DataType data_type_;
+  bool is_unique_;
 };
 
 std::ostream &operator<<(std::ostream &stream, const Column &column);

  Modified: lib/grnxx/column_impl.cpp (+223 -2)
===================================================================
--- lib/grnxx/column_impl.cpp    2014-03-13 13:39:04 +0900 (d346e21)
+++ lib/grnxx/column_impl.cpp    2014-03-17 18:24:46 +0900 (909e625)
@@ -3,7 +3,7 @@
 #include "grnxx/index.hpp"
 #include "grnxx/table.hpp"
 
-//#include <iostream>
+#include <iostream>  // For debugging.
 
 namespace grnxx {
 
@@ -18,6 +18,22 @@ ColumnImpl<T>::ColumnImpl(Table *table, ColumnID id, const String &name)
 template <typename T>
 ColumnImpl<T>::~ColumnImpl() {}
 
+// UNIQUE 制約を設定する.
+template <typename T>
+bool ColumnImpl<T>::set_unique() {
+  std::set<T> set;
+  for (RowID row_id = MIN_ROW_ID; row_id <= data_.size(); ++row_id) {
+    auto it = set.find(data_[row_id]);
+    if (it != set.end()) {
+      // 重複があれば失敗する.
+      return false;
+    }
+    set.insert(data_[row_id]);
+  }
+  is_unique_ = true;
+  return true;
+}
+
 // 指定された索引との関連付けをおこなう.
 template <typename T>
 bool ColumnImpl<T>::register_index(Index *index) {
@@ -46,6 +62,27 @@ void ColumnImpl<T>::resize(RowID max_row_id) {
   data_.resize(max_row_id + 1, 0);
 }
 
+// 指定された値を検索する.
+template <typename T>
+RowID ColumnImpl<T>::find(T value) const {
+  if (indexes_.empty()) {
+    // 索引がなければ全体を走査する.
+    for (RowID row_id = MIN_ROW_ID; row_id <= data_.size(); ++row_id) {
+      if (data_[row_id] == value) {
+        return row_id;
+      }
+    }
+  } else {
+    // 索引があれば使う.
+    auto cursor = indexes_[0]->find_equal(value);
+    RowID row_id;
+    if (cursor->get_next(&row_id, 1) != 0) {
+      return row_id;
+    }
+  }
+  return 0;
+}
+
 // 指定された ID の値を更新する.
 template <typename T>
 void ColumnImpl<T>::set(RowID row_id, T value) {
@@ -75,6 +112,62 @@ ColumnImpl<Int64>::ColumnImpl(Table *table, ColumnID id, const String &name,
 // カラムを破棄する.
 ColumnImpl<Int64>::~ColumnImpl() {}
 
+// UNIQUE 制約を設定する.
+bool ColumnImpl<Int64>::set_unique() {
+  switch (internal_data_type_size_) {
+    case 8: {
+      std::set<Int8> set;
+      for (RowID row_id = MIN_ROW_ID; row_id <= data_8_.size(); ++row_id) {
+        auto it = set.find(data_8_[row_id]);
+        if (it != set.end()) {
+          // 重複があれば失敗する.
+          return false;
+        }
+        set.insert(get(row_id));
+      }
+      break;
+    }
+    case 16: {
+      std::set<Int16> set;
+      for (RowID row_id = MIN_ROW_ID; row_id <= data_8_.size(); ++row_id) {
+        auto it = set.find(data_16_[row_id]);
+        if (it != set.end()) {
+          // 重複があれば失敗する.
+          return false;
+        }
+        set.insert(get(row_id));
+      }
+      break;
+    }
+    case 32: {
+      std::set<Int32> set;
+      for (RowID row_id = MIN_ROW_ID; row_id <= data_8_.size(); ++row_id) {
+        auto it = set.find(data_32_[row_id]);
+        if (it != set.end()) {
+          // 重複があれば失敗する.
+          return false;
+        }
+        set.insert(get(row_id));
+      }
+      break;
+    }
+    default: {
+      std::set<Int64> set;
+      for (RowID row_id = MIN_ROW_ID; row_id <= data_8_.size(); ++row_id) {
+        auto it = set.find(data_64_[row_id]);
+        if (it != set.end()) {
+          // 重複があれば失敗する.
+          return false;
+        }
+        set.insert(get(row_id));
+      }
+      break;
+    }
+  }
+  is_unique_ = true;
+  return true;
+}
+
 // 指定された索引との関連付けをおこなう.
 bool ColumnImpl<Int64>::register_index(Index *index) {
   auto it = std::find(indexes_.begin(), indexes_.end(), index);
@@ -113,6 +206,55 @@ void ColumnImpl<Int64>::resize(RowID max_row_id) {
   }
 }
 
+// 指定された値を検索する.
+RowID ColumnImpl<Int64>::find(Int64 value) const {
+  if (indexes_.empty()) {
+    // 索引がなければ全体を走査する.
+    switch (internal_data_type_size_) {
+      case 8: {
+        for (RowID row_id = MIN_ROW_ID; row_id <= data_8_.size(); ++row_id) {
+          if (data_8_[row_id] == value) {
+            return false;
+          }
+        }
+        break;
+      }
+      case 16: {
+        for (RowID row_id = MIN_ROW_ID; row_id <= data_16_.size(); ++row_id) {
+          if (data_16_[row_id] == value) {
+            return false;
+          }
+        }
+        break;
+      }
+      case 32: {
+        for (RowID row_id = MIN_ROW_ID; row_id <= data_32_.size(); ++row_id) {
+          if (data_32_[row_id] == value) {
+            return false;
+          }
+        }
+        break;
+      }
+      default: {
+        for (RowID row_id = MIN_ROW_ID; row_id <= data_64_.size(); ++row_id) {
+          if (data_64_[row_id] == value) {
+            return false;
+          }
+        }
+        break;
+      }
+    }
+  } else {
+    // 索引があれば使う.
+    auto cursor = indexes_[0]->find_equal(value);
+    RowID row_id;
+    if (cursor->get_next(&row_id, 1) != 0) {
+      return row_id;
+    }
+  }
+  return 0;
+}
+
 // 指定された ID の値を更新する.
 void ColumnImpl<Int64>::set(RowID row_id, Int64 value) {
   if (dest_table_) {
@@ -220,14 +362,31 @@ void ColumnImpl<Int64>::expand_and_set(RowID row_id, Int64 value) {
 #else  // GRNXX_ENABLE_VARIABLE_INTEGER_TYPE
 
 // カラムを初期化する.
-ColumnImpl<Int64>::ColumnImpl(Table *table, ColumnID id, const String &name)
+ColumnImpl<Int64>::ColumnImpl(Table *table, ColumnID id, const String &name,
+                              Table *dest_table)
     : Column(table, id, name, INTEGER),
+      dest_table_(dest_table),
       data_(MIN_ROW_ID, 0),
       indexes_() {}
 
 // カラムを破棄する.
 ColumnImpl<Int64>::~ColumnImpl() {}
 
+// UNIQUE 制約を設定する.
+bool ColumnImpl<Int64>::set_unique() {
+  std::set<Int64> set;
+  for (RowID row_id = MIN_ROW_ID; row_id <= data_.size(); ++row_id) {
+    auto it = set.find(data_[row_id]);
+    if (it != set.end()) {
+      // 重複があれば失敗する.
+      return false;
+    }
+    set.insert(data_[row_id]);
+  }
+  is_unique_ = true;
+  return true;
+}
+
 // 指定された索引との関連付けをおこなう.
 bool ColumnImpl<Int64>::register_index(Index *index) {
   auto it = std::find(indexes_.begin(), indexes_.end(), index);
@@ -253,8 +412,34 @@ void ColumnImpl<Int64>::resize(RowID max_row_id) {
   data_.resize(max_row_id + 1, 0);
 }
 
+// 指定された値を検索する.
+RowID ColumnImpl<Int64>::find(Int64 value) const {
+  if (indexes_.empty()) {
+    // 索引がなければ全体を走査する.
+    for (RowID row_id = MIN_ROW_ID; row_id <= data_.size(); ++row_id) {
+      if (data_[row_id] == value) {
+        return row_id;
+      }
+    }
+  } else {
+    // 索引があれば使う.
+    auto cursor = indexes_[0]->find_equal(value);
+    RowID row_id;
+    if (cursor->get_next(&row_id, 1) != 0) {
+      return row_id;
+    }
+  }
+  return 0;
+}
+
 // 指定された ID の値を更新する.
 void ColumnImpl<Int64>::set(RowID row_id, Int64 value) {
+  if (dest_table_) {
+    if ((value < dest_table_->min_row_id()) ||
+        (value > dest_table_->max_row_id())) {
+      throw "invalid reference";
+    }
+  }
   data_[row_id] = value;
   for (auto index : indexes_) {
     index->insert(row_id);
@@ -273,6 +458,22 @@ ColumnImpl<String>::ColumnImpl(Table *table, ColumnID id, const String &name)
 // カラムを破棄する.
 ColumnImpl<String>::~ColumnImpl() {}
 
+// UNIQUE 制約を設定する.
+bool ColumnImpl<String>::set_unique() {
+  std::set<String> set;
+  for (RowID row_id = MIN_ROW_ID; row_id <= headers_.size(); ++row_id) {
+    auto value = get(row_id);
+    auto it = set.find(value);
+    if (it != set.end()) {
+      // 重複があれば失敗する.
+      return false;
+    }
+    set.insert(value);
+  }
+  is_unique_ = true;
+  return true;
+}
+
 // 指定された索引との関連付けをおこなう.
 bool ColumnImpl<String>::register_index(Index *index) {
   auto it = std::find(indexes_.begin(), indexes_.end(), index);
@@ -298,6 +499,26 @@ void ColumnImpl<String>::resize(RowID max_row_id) {
   headers_.resize(max_row_id + 1, 0);
 }
 
+// 指定された値を検索する.
+RowID ColumnImpl<String>::find(const String &value) const {
+  if (indexes_.empty()) {
+    // 索引がなければ全体を走査する.
+    for (RowID row_id = MIN_ROW_ID; row_id <= headers_.size(); ++row_id) {
+      if (get(row_id) == value) {
+        return row_id;
+      }
+    }
+  } else {
+    // 索引があれば使う.
+    auto cursor = indexes_[0]->find_equal(value);
+    RowID row_id;
+    if (cursor->get_next(&row_id, 1) != 0) {
+      return row_id;
+    }
+  }
+  return 0;
+}
+
 // 指定された ID の値を更新する.
 void ColumnImpl<String>::set(RowID row_id, const String &value) {
   if (value.empty()) {

  Modified: lib/grnxx/column_impl.hpp (+33 -1)
===================================================================
--- lib/grnxx/column_impl.hpp    2014-03-13 13:39:04 +0900 (9100225)
+++ lib/grnxx/column_impl.hpp    2014-03-17 18:24:46 +0900 (2114a5f)
@@ -20,6 +20,9 @@ class ColumnImpl : public Column {
   ColumnImpl(const ColumnImpl &) = delete;
   ColumnImpl &operator=(const ColumnImpl &) = delete;
 
+  // UNIQUE 制約を設定する.
+  bool set_unique();
+
   // 指定された索引との関連付けをおこなう.
   bool register_index(Index *index);
   // 指定された索引との関連を削除する.
@@ -28,6 +31,9 @@ class ColumnImpl : public Column {
   // 指定された行 ID が使えるようにサイズを変更する.
   void resize(RowID max_row_id);
 
+  // 指定された値を検索する.
+  RowID find(T value) const;
+
   // 指定された ID の値を返す.
   T get(RowID row_id) const {
     return data_[row_id];
@@ -56,6 +62,9 @@ class ColumnImpl<Int64> : public Column {
   ColumnImpl(const ColumnImpl &) = delete;
   ColumnImpl &operator=(const ColumnImpl &) = delete;
 
+  // UNIQUE 制約を設定する.
+  bool set_unique();
+
   // 指定された索引との関連付けをおこなう.
   bool register_index(Index *index);
   // 指定された索引との関連を削除する.
@@ -70,6 +79,9 @@ class ColumnImpl<Int64> : public Column {
     return dest_table_;
   }
 
+  // 指定された値を検索する.
+  RowID find(Int64 value) const;
+
   // 指定された ID の値を返す.
   Int64 get(RowID row_id) const {
     switch (internal_data_type_size_) {
@@ -109,7 +121,8 @@ class ColumnImpl<Int64> : public Column {
   using Value = Int64;
 
   // カラムを初期化する.
-  ColumnImpl(Table *table, ColumnID id, const String &name);
+  ColumnImpl(Table *table, ColumnID id, const String &name,
+             Table *dest_table = nullptr);
   // カラムを破棄する.
   ~ColumnImpl();
 
@@ -117,6 +130,9 @@ class ColumnImpl<Int64> : public Column {
   ColumnImpl(const ColumnImpl &) = delete;
   ColumnImpl &operator=(const ColumnImpl &) = delete;
 
+  // UNIQUE 制約を設定する.
+  bool set_unique();
+
   // 指定された索引との関連付けをおこなう.
   bool register_index(Index *index);
   // 指定された索引との関連を削除する.
@@ -125,6 +141,15 @@ class ColumnImpl<Int64> : public Column {
   // 指定された行 ID が使えるようにサイズを変更する.
   void resize(RowID max_row_id);
 
+  // 参照先のテーブルを返す.
+  // なければ nullptr を返す.
+  Table *dest_table() const {
+    return dest_table_;
+  }
+
+  // 指定された値を検索する.
+  RowID find(Int64 value) const;
+
   // 指定された ID の値を返す.
   Int64 get(RowID row_id) const {
     return data_[row_id];
@@ -133,6 +158,7 @@ class ColumnImpl<Int64> : public Column {
   void set(RowID row_id, Int64 value);
 
  private:
+  Table *dest_table_;
   std::vector<Int64> data_;
   std::vector<Index *> indexes_;
 };
@@ -152,6 +178,9 @@ class ColumnImpl<String> : public Column {
   ColumnImpl(const ColumnImpl &) = delete;
   ColumnImpl &operator=(const ColumnImpl &) = delete;
 
+  // UNIQUE 制約を設定する.
+  bool set_unique();
+
   // 指定された索引との関連付けをおこなう.
   bool register_index(Index *index);
   // 指定された索引との関連を削除する.
@@ -160,6 +189,9 @@ class ColumnImpl<String> : public Column {
   // 指定された行 ID が使えるようにサイズを変更する.
   void resize(RowID max_row_id);
 
+  // 指定された値を検索する.
+  RowID find(const String &value) const;
+
   // 指定された ID の値を返す.
   String get(RowID row_id) const {
     Int64 size = headers_[row_id] & 0xFFFF;

  Modified: lib/grnxx/table.cpp (+39 -0)
===================================================================
--- lib/grnxx/table.cpp    2014-03-13 13:39:04 +0900 (8b321ac)
+++ lib/grnxx/table.cpp    2014-03-17 18:24:46 +0900 (d230fb2)
@@ -8,6 +8,8 @@
 
 #include <ostream>
 
+#include <iostream>  // For debugging.
+
 namespace grnxx {
 namespace {
 
@@ -62,6 +64,7 @@ Table::Table(Database *database, TableID id, const String &name)
       id_(id),
       name_(reinterpret_cast<const char *>(name.data()), name.size()),
       max_row_id_(MIN_ROW_ID - 1),
+      primary_key_column_(nullptr),
       columns_(MIN_COLUMN_ID),
       columns_map_(),
       indexes_(MIN_INDEX_ID),
@@ -144,6 +147,37 @@ bool Table::drop_column(const String &column_name) {
   return true;
 }
 
+// 指定されたカラムに主キー属性を与える.
+// 成功すれば true を返し,失敗すれば false を返す.
+bool Table::set_primary_key(const String &column_name) {
+  if (primary_key_column_) {
+    return false;
+  }
+  auto it = columns_map_.find(column_name);
+  if (it == columns_map_.end()) {
+    return false;
+  }
+  auto column = columns_[it->second].get();
+  if (!column->set_unique()) {
+    return false;
+  }
+  primary_key_column_ = column;
+  return true;
+}
+
+// 主キー属性を解除する.
+// 成功すれば true を返し,失敗すれば false を返す.
+bool Table::unset_primary_key() {
+  if (!primary_key_column_) {
+    return false;
+  }
+  if (!primary_key_column_->unset_unique()) {
+    return false;
+  }
+  primary_key_column_ = nullptr;
+  return true;
+}
+
 // 指定された 名前 のカラムを返す.
 // なければ nullptr を返す.
 Column *Table::get_column_by_name(const String &column_name) const {
@@ -290,6 +324,11 @@ Table::Cursor *Table::create_cursor(RowID range_min, RowID range_max) const {
   return new Cursor(range_min, range_max);
 }
 
+// FIXME: 指定された主キーを持つ行を検索する.
+RowID Table::find_row(const Datum &datum) {
+  return primary_key_column_->generic_find(datum);
+}
+
 // 演算器を作成する.
 Calc *Table::create_calc(const String &query) const {
   return CalcHelper::create(this, query);

  Modified: lib/grnxx/table.hpp (+15 -0)
===================================================================
--- lib/grnxx/table.hpp    2014-03-13 13:39:04 +0900 (8e51a8c)
+++ lib/grnxx/table.hpp    2014-03-17 18:24:46 +0900 (acb4a49)
@@ -40,6 +40,13 @@ class Table {
   // 成功すれば true を返し,失敗すれば false を返す.
   bool drop_column(const String &column_name);
 
+  // 指定されたカラムに主キー属性を与える.
+  // 成功すれば true を返し,失敗すれば false を返す.
+  bool set_primary_key(const String &column_name);
+  // 主キー属性を解除する.
+  // 成功すれば true を返し,失敗すれば false を返す.
+  bool unset_primary_key();
+
   // カラム ID の最小値を返す.
   ColumnID min_column_id() const {
     return MIN_COLUMN_ID;
@@ -106,6 +113,10 @@ class Table {
   RowID max_row_id() const {
     return max_row_id_;
   }
+  // 主キーカラムを返す.
+  Column *primary_key_column() const {
+    return primary_key_column_;
+  }
 
   // 行 ID を取得するカーソル.
   class Cursor : public RowIDCursor {
@@ -128,6 +139,9 @@ class Table {
   Cursor *create_cursor(RowID range_min = MIN_ROW_ID,
                         RowID range_max = INT64_MAX) const;
 
+  // FIXME: 指定された主キーを持つ行を検索する.
+  RowID find_row(const Datum &datum);
+
   // 演算器を作成する.
   Calc *create_calc(const String &query) const;
   // 整列器を作成する.
@@ -156,6 +170,7 @@ class Table {
   TableID id_;
   std::string name_;
   RowID max_row_id_;
+  Column *primary_key_column_;
   std::vector<std::unique_ptr<Column>> columns_;
   std::map<String, ColumnID> columns_map_;
   std::vector<std::unique_ptr<Index>> indexes_;

  Modified: src/grnxx.cpp (+57 -0)
===================================================================
--- src/grnxx.cpp    2014-03-13 13:39:04 +0900 (7c37f7e)
+++ src/grnxx.cpp    2014-03-17 18:24:46 +0900 (b6356f6)
@@ -7,6 +7,7 @@
 
 #include "grnxx/calc.hpp"
 #include "grnxx/column.hpp"
+#include "grnxx/column_impl.hpp"
 #include "grnxx/database.hpp"
 #include "grnxx/index.hpp"
 #include "grnxx/library.hpp"
@@ -368,6 +369,19 @@ bool run_load(grnxx::Database *database,
       if (!column) {
         continue;
       }
+      grnxx::Table *dest_table = nullptr;
+      if (params[begin] == '&') {
+        // 参照型.
+        if (column->data_type() != grnxx::INTEGER) {
+          return false;
+        }
+        auto impl = static_cast<grnxx::ColumnImpl<grnxx::Int64> *>(column);
+        dest_table = impl->dest_table();
+        if (!dest_table || !dest_table->primary_key_column()) {
+          return false;
+        }
+        ++begin;
+      }
       if (params[begin] == '"') {
         // 文字列.
         end = params.find_first_of('"', ++begin);
@@ -386,6 +400,16 @@ bool run_load(grnxx::Database *database,
         }
         datum = params.extract(begin, end - begin);
       }
+      if (dest_table) {
+        // 参照の解決.
+        datum = dest_table->find_row(datum);
+      }
+      if (column->is_unique()) {
+        // UNIQUE 制約の確認.
+        if (column->generic_find(datum) != 0) {
+          return false;
+        }
+      }
       column->generic_set(table->max_row_id(), datum);
       begin = params.find_first_not_of(" \t\n", end);
       if (begin == params.npos) {
@@ -397,6 +421,37 @@ bool run_load(grnxx::Database *database,
   return true;
 }
 
+// set_primary_key コマンド.
+bool run_set_primary_key(grnxx::Database *database,
+                         const grnxx::String &params) {
+  auto end = params.find_first_of(" \t\n");
+  if (end == params.npos) {
+    std::cerr << "Error: too few arguments" << std::endl;
+    return false;
+  }
+  grnxx::String table_name = params.prefix(end);
+  auto table = database->get_table_by_name(table_name);
+  if (!table) {
+    std::cerr << "Error: table not found: "
+              << "table_name = " << table_name << std::endl;
+    return false;
+  }
+  auto begin = params.find_first_not_of(" \t\n", end);
+  end = params.find_first_of(" \t\n", begin);
+  if (end == params.npos) {
+    end = params.size();
+  }
+  grnxx::String column_name = params.extract(begin, end - begin);
+  if (!table->set_primary_key(column_name)) {
+    std::cerr << "Error: grnxx::Table::set_primary_key() failed: "
+              << "table_name = " << table_name
+              << ", column_name = " << column_name << std::endl;
+    return false;
+  }
+  std::cout << "OK\n";
+  return true;
+}
+
 struct SelectQuery {
   grnxx::String table_name;
   grnxx::String output_column_names;
@@ -913,6 +968,8 @@ void run_terminal() {
         run_index_list(&database, params);
       } else if (command == "load") {
         run_load(&database, params);
+      } else if (command == "set_primary_key") {
+        run_set_primary_key(&database, params);
       } else if (command == "select") {
         grnxx::Timer timer;
         run_select(&database, params, std::cout);

  Modified: test/test_grnxx.cpp (+73 -0)
===================================================================
--- test/test_grnxx.cpp    2014-03-13 13:39:04 +0900 (6c0512e)
+++ test/test_grnxx.cpp    2014-03-17 18:24:46 +0900 (134b660)
@@ -835,6 +835,78 @@ void test_deep_reference() {
   }
 }
 
+void test_primary_key() {
+  grnxx::Database database;
+
+  grnxx::Table *src_table = database.create_table("Src");
+  assert(src_table);
+  grnxx::Table *dest_table = database.create_table("Dest");
+  assert(dest_table);
+
+  auto reference_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Int64> *>(
+      src_table->create_reference_column("Reference", "Dest"));
+  assert(reference_column);
+
+  auto key_column = dynamic_cast<grnxx::ColumnImpl<grnxx::String> *>(
+      dest_table->create_column("Key", grnxx::STRING));
+  assert(key_column);
+  assert(dest_table->set_primary_key("Key"));
+
+  std::mt19937_64 random;
+
+  std::vector<std::string> key_data;
+  for (grnxx::Int64 i = 0; i < 1000; ++i) {
+    std::string str(8, 'A');
+    for (std::size_t i = 0; i < str.size(); ++i) {
+      str[i] += random() % 26;
+    }
+    key_data.push_back(str);
+    grnxx::RowID row_id = dest_table->insert_row();
+    key_column->set(row_id, key_data[i]);
+  }
+
+  std::vector<grnxx::Int64> reference_data;
+  for (grnxx::Int64 i = 0; i < 1000; ++i) {
+    reference_data.push_back(random() % 1000);
+    grnxx::RowID row_id = src_table->insert_row();
+    reference_column->set(row_id, reference_data[i] + 1);
+  }
+
+  std::vector<grnxx::RowID> all_row_ids(1000);
+  std::unique_ptr<grnxx::RowIDCursor> cursor(src_table->create_cursor());
+  assert(cursor->get_next(&all_row_ids[0], 1000) == 1000);
+
+  // Reference.Key で絞り込む.
+  {
+    std::unique_ptr<grnxx::Calc> calc(
+        src_table->create_calc("Reference.Key > \"N\""));
+    assert(calc);
+    std::vector<grnxx::RowID> row_ids(all_row_ids);
+    grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size());
+    assert(num_row_ids != 0);
+    grnxx::Int64 count = 0;
+    for (grnxx::Int64 i = 0; i < 1000; ++i) {
+      grnxx::RowID row_id = grnxx::MIN_ROW_ID + i;
+      if (key_data[reference_data[i]] > "N") {
+        assert(row_ids[count] == row_id);
+        assert(++count <= num_row_ids);
+      }
+    }
+    assert(count == num_row_ids);
+  }
+
+  for (grnxx::Int64 i = 0; i < 1000; ++i) {
+    assert(dest_table->find_row(key_data[i]) == (i + 1));
+  }
+  for (grnxx::Int64 i = 0; i < 1000; ++i) {
+    std::string str(8, 'a');
+    for (std::size_t i = 0; i < str.size(); ++i) {
+      str[i] += random() % 26;
+    }
+    assert(dest_table->find_row(str) == 0);
+  }
+}
+
 void test_sorter() {
   constexpr grnxx::Int64 DATA_SIZE = 1000;
 
@@ -1466,6 +1538,7 @@ int main() {
   test_calc();
   test_reference();
   test_deep_reference();
+  test_primary_key();
   test_sorter();
   test_sorter_large();
   test_index();
-------------- next part --------------
HTML����������������������������...
Download 



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