[Groonga-commit] groonga/grnxx at 1c0ca60 [master] Add Filter implementations.

Back to archive index

susumu.yata null+****@clear*****
Mon Jul 14 14:07:30 JST 2014


susumu.yata	2014-07-14 14:07:30 +0900 (Mon, 14 Jul 2014)

  New Revision: 1c0ca60942362c0aeedc457b8c84592f54b7e3ab
  https://github.com/groonga/grnxx/commit/1c0ca60942362c0aeedc457b8c84592f54b7e3ab

  Message:
    Add Filter implementations.

  Modified files:
    include/grnxx/datum.hpp
    include/grnxx/expression.hpp
    include/grnxx/record.hpp
    include/grnxx/types.hpp
    lib/grnxx/column.cpp
    lib/grnxx/column_impl.hpp
    lib/grnxx/expression.cpp
    test/test_grnxx.cpp

  Modified: include/grnxx/datum.hpp (+20 -0)
===================================================================
--- include/grnxx/datum.hpp    2014-07-14 01:48:25 +0900 (8defb80)
+++ include/grnxx/datum.hpp    2014-07-14 14:07:30 +0900 (05c5b42)
@@ -54,6 +54,26 @@ class Datum {
     return text_;
   }
 
+  // Force the specified interpretation.
+  void force(Bool *value) const {
+    *value = bool_;
+  }
+  void force(Int *value) const {
+    *value = int_;
+  }
+  void force(Float *value) const {
+    *value = float_;
+  }
+  void force(Time *value) const {
+    *value = time_;
+  }
+  void force(GeoPoint *value) const {
+    *value = geo_point_;
+  }
+  void force(Text *value) const {
+    *value = text_;
+  }
+
  private:
   DataType type_;
   union {

  Modified: include/grnxx/expression.hpp (+4 -3)
===================================================================
--- include/grnxx/expression.hpp    2014-07-14 01:48:25 +0900 (ed71605)
+++ include/grnxx/expression.hpp    2014-07-14 14:07:30 +0900 (0ae4ffd)
@@ -24,12 +24,13 @@ class Expression {
   // On failure, returns nullptr and stores error information into "*error" if
   // "error" != nullptr.
   static unique_ptr<Expression> create(Error *error,
+                                       const Table *table,
                                        unique_ptr<ExpressionNode> &&root);
 
   ~Expression();
 
   // Return the associated table.
-  Table *table() const {
+  const Table *table() const {
     return table_;
   }
   // Return the result data type.
@@ -46,10 +47,10 @@ class Expression {
   bool filter(Error *error, RecordSet *record_set);
 
  private:
-  Table *table_;
+  const Table *table_;
   unique_ptr<ExpressionNode> root_;
 
-  Expression(Table *table, unique_ptr<ExpressionNode> &&root);
+  Expression(const Table *table, unique_ptr<ExpressionNode> &&root);
 };
 
 class ExpressionBuilder {

  Modified: include/grnxx/record.hpp (+23 -1)
===================================================================
--- include/grnxx/record.hpp    2014-07-14 01:48:25 +0900 (66d90d5)
+++ include/grnxx/record.hpp    2014-07-14 14:07:30 +0900 (920e7fe)
@@ -35,7 +35,7 @@ class RecordSet {
       records_.push_back(record);
       return true;
     } catch (...) {
-      // TODO: Error report.
+      // TODO: Error report should be written in .cpp file.
       return false;
     }
   }
@@ -47,6 +47,28 @@ class RecordSet {
     return records_[i];
   }
 
+  // Set a record.
+  //
+  // If "i" is invalid, the result is undefined.
+  void set(Int i, Record record) {
+    records_[i] = record;
+  }
+
+  // Resize the set.
+  //
+  // Returns true on success.
+  // On failure, returns false and stores error information into "*error" if
+  // "error" != nullptr.
+  bool resize(Error *error, Int size) {
+    try {
+      records_.resize(size);
+      return true;
+    } catch (...) {
+      // TODO: Error report should be written in .cpp file.
+      return false;
+    }
+  }
+
   // Clear all the records.
   void clear() {
     records_.clear();

  Modified: include/grnxx/types.hpp (+27 -0)
===================================================================
--- include/grnxx/types.hpp    2014-07-14 01:48:25 +0900 (30997fd)
+++ include/grnxx/types.hpp    2014-07-14 14:07:30 +0900 (3babc6b)
@@ -294,6 +294,33 @@ class RowRef {
   Int row_id_;  // Row ID number.
 };
 
+// Type information.
+template <typename T> struct TypeTraits;
+template <> struct TypeTraits <Bool> {
+  static DataType data_type() {
+    return BOOL_DATA;
+  }
+  static Bool default_value() {
+    return false;
+  }
+};
+template <> struct TypeTraits <Int> {
+  static DataType data_type() {
+    return INT_DATA;
+  }
+  static Int default_value() {
+    return 0;
+  }
+};
+template <> struct TypeTraits <Float> {
+  static DataType data_type() {
+    return FLOAT_DATA;
+  }
+  static Float default_value() {
+    return 0.0;
+  }
+};
+
 // Zero is reserved for representing a null reference.
 constexpr Int NULL_ROW_ID = 0;
 constexpr Int MIN_ROW_ID  = 1;

  Modified: lib/grnxx/column.cpp (+37 -90)
===================================================================
--- lib/grnxx/column.cpp    2014-07-14 01:48:25 +0900 (a32f687)
+++ lib/grnxx/column.cpp    2014-07-14 14:07:30 +0900 (75cc2b3)
@@ -74,22 +74,22 @@ unique_ptr<Column> Column::create(Error *error,
                                   const ColumnOptions &options) {
   switch (data_type) {
     case BOOL_DATA: {
-      return BoolColumn::create(error, table, name, options);
+      return ColumnImpl<Bool>::create(error, table, name, options);
     }
     case INT_DATA: {
-      return IntColumn::create(error, table, name, options);
+      return ColumnImpl<Int>::create(error, table, name, options);
     }
 //    case FLOAT_DATA: {
-//      return FloatColumn::create(error, table, name, options);
+//      return ColumnImpl<Float>::create(error, table, name, options);
 //    }
 //    case TIME_DATA: {
-//      return TimeColumn::create(error, table, name, options);
+//      return ColumnImpl<Time>::create(error, table, name, options);
 //    }
 //    case GEO_POINT_DATA: {
-//      return GeoPointColumn::create(error, table, name, options);
+//      return ColumnImpl<GeoPoint>::create(error, table, name, options);
 //    }
 //    case TEXT_DATA: {
-//      return TextColumn::create(error, table, name, options);
+//      return ColumnImpl<Text>::create(error, table, name, options);
 //    }
     default: {
       // TODO: Other data types are not supported yet.
@@ -143,21 +143,26 @@ bool Column::set_default_value(Error *error, Int row_id) {
 void Column::unset(Int row_id) {
 }
 
-// -- BoolColumn --
+// -- ColumnImpl --
 
-bool BoolColumn::set(Error *error, Int row_id, const Datum &datum) {
-  if (datum.type() != BOOL_DATA) {
+template <typename T>
+bool ColumnImpl<T>::set(Error *error, Int row_id, const Datum &datum) {
+  if (datum.type() != TypeTraits<T>::data_type()) {
     GRNXX_ERROR_SET(error, INVALID_ARGUMENT, "Wrong data type");
     return false;
   }
   if (!table_->test_row(error, row_id)) {
     return false;
   }
-  values_[row_id] = datum.force_bool();
+  // Note that a Bool object does not have its own address.
+  T value;
+  datum.force(&value);
+  values_[row_id] = value;
   return true;
 }
 
-bool BoolColumn::get(Error *error, Int row_id, Datum *datum) const {
+template <typename T>
+bool ColumnImpl<T>::get(Error *error, Int row_id, Datum *datum) const {
   if (!table_->test_row(error, row_id)) {
     return false;
   }
@@ -165,20 +170,23 @@ bool BoolColumn::get(Error *error, Int row_id, Datum *datum) const {
   return true;
 }
 
-unique_ptr<BoolColumn> BoolColumn::create(Error *error,
-                                          Table *table,
-                                          String name,
-                                          const ColumnOptions &options) {
-  unique_ptr<BoolColumn> column(new (nothrow) BoolColumn);
+template <typename T>
+unique_ptr<ColumnImpl<T>> ColumnImpl<T>::create(Error *error,
+                                                Table *table,
+                                                String name,
+                                                const ColumnOptions &options) {
+  unique_ptr<ColumnImpl> column(new (nothrow) ColumnImpl);
   if (!column) {
     GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
     return nullptr;
   }
-  if (!column->initialize_base(error, table, name, BOOL_DATA, options)) {
+  if (!column->initialize_base(error, table, name,
+                               TypeTraits<T>::data_type(), options)) {
     return nullptr;
   }
   try {
-    column->values_.resize(table->max_row_id() + 1, false);
+    column->values_.resize(table->max_row_id() + 1,
+                           TypeTraits<T>::default_value());
   } catch (...) {
     GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
     return nullptr;
@@ -186,91 +194,30 @@ unique_ptr<BoolColumn> BoolColumn::create(Error *error,
   return column;
 }
 
-BoolColumn::~BoolColumn() {}
+template <typename T>
+ColumnImpl<T>::~ColumnImpl() {}
 
-bool BoolColumn::set_default_value(Error *error, Int row_id) {
+template <typename T>
+bool ColumnImpl<T>::set_default_value(Error *error, Int row_id) {
   if (row_id >= values_.size()) {
     try {
-      values_.resize(row_id + 1, false);
+      values_.resize(row_id + 1, TypeTraits<T>::default_value());
       return true;
     } catch (...) {
       GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
       return false;
     }
   }
-  values_[row_id] = false;
+  values_[row_id] = TypeTraits<T>::default_value();
   return true;
 }
 
-void BoolColumn::unset(Int row_id) {
-  values_[row_id] = false;
+template <typename T>
+void ColumnImpl<T>::unset(Int row_id) {
+  values_[row_id] = TypeTraits<T>::default_value();
 }
 
-BoolColumn::BoolColumn() : Column(), values_() {}
-
-// -- IntColumn --
-
-bool IntColumn::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;
-  }
-  values_[row_id] = datum.force_int();
-  return true;
-}
-
-bool IntColumn::get(Error *error, Int row_id, Datum *datum) const {
-  if (!table_->test_row(error, row_id)) {
-    return false;
-  }
-  *datum = values_[row_id];
-  return true;
-}
-
-unique_ptr<IntColumn> IntColumn::create(Error *error,
-                                        Table *table,
-                                        String name,
-                                        const ColumnOptions &options) {
-  unique_ptr<IntColumn> column(new (nothrow) IntColumn);
-  if (!column) {
-    GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
-    return nullptr;
-  }
-  if (!column->initialize_base(error, table, name, INT_DATA, options)) {
-    return nullptr;
-  }
-  try {
-    column->values_.resize(table->max_row_id() + 1, 0);
-  } catch (...) {
-    GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
-    return nullptr;
-  }
-  return column;
-}
-
-IntColumn::~IntColumn() {}
-
-bool IntColumn::set_default_value(Error *error, Int row_id) {
-  if (row_id >= values_.size()) {
-    try {
-      values_.resize(row_id + 1, 0);
-      return true;
-    } catch (...) {
-      GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
-      return false;
-    }
-  }
-  values_[row_id] = 0;
-  return true;
-}
-
-void IntColumn::unset(Int row_id) {
-  values_[row_id] = 0;
-}
-
-IntColumn::IntColumn() : Column(), values_() {}
+template <typename T>
+ColumnImpl<T>::ColumnImpl() : Column(), values_() {}
 
 }  // namespace grnxx

  Modified: lib/grnxx/column_impl.hpp (+7 -43)
===================================================================
--- lib/grnxx/column_impl.hpp    2014-07-14 01:48:25 +0900 (102dcba)
+++ lib/grnxx/column_impl.hpp    2014-07-14 14:07:30 +0900 (87c75b6)
@@ -7,7 +7,8 @@
 
 namespace grnxx {
 
-class BoolColumn : public Column {
+template <typename T>
+class ColumnImpl : public Column {
  public:
   // -- Public API --
 
@@ -21,12 +22,12 @@ class BoolColumn : public 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<BoolColumn> create(Error *error,
+  static unique_ptr<ColumnImpl> create(Error *error,
                                        Table *table,
                                        String name,
                                        const ColumnOptions &options);
 
-  ~BoolColumn();
+  ~ColumnImpl();
 
   bool set_default_value(Error *error, Int row_id);
   void unset(Int row_id);
@@ -34,51 +35,14 @@ class BoolColumn : public Column {
   // Return a value identified by "row_id".
   //
   // Assumes that "row_id" is valid. Otherwise, the result is undefined.
-  Bool get(Int row_id) const {
+  T get(Int row_id) const {
     return values_[row_id];
   }
 
  protected:
-  std::vector<Bool> values_;
+  std::vector<T> values_;
 
-  BoolColumn();
-};
-
-class IntColumn : public Column {
- public:
-  // -- Public API --
-
-  bool set(Error *error, Int row_id, const Datum &datum);
-  bool get(Error *error, Int row_id, Datum *datum) const;
-
-  // -- Internal API --
-
-  // 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<IntColumn> create(Error *error,
-                                      Table *table,
-                                      String name,
-                                      const ColumnOptions &options);
-
-  ~IntColumn();
-
-  bool set_default_value(Error *error, Int row_id);
-  void unset(Int row_id);
-
-  // Return a value identified by "row_id".
-  //
-  // Assumes that "row_id" is valid. Otherwise, the result is undefined.
-  Int get(Int row_id) const {
-    return values_[row_id];
-  }
-
- protected:
-  std::vector<Int> values_;
-
-  IntColumn();
+  ColumnImpl();
 };
 
 }  // namespace grnxx

  Modified: lib/grnxx/expression.cpp (+213 -217)
===================================================================
--- lib/grnxx/expression.cpp    2014-07-14 01:48:25 +0900 (a00a5fc)
+++ lib/grnxx/expression.cpp    2014-07-14 14:07:30 +0900 (dd654ea)
@@ -6,23 +6,28 @@
 #include "grnxx/record.hpp"
 #include "grnxx/table.hpp"
 
-namespace grnxx {
+#include <iostream>  // For debugging.
 
-enum ExpressionNodeType {
-  EXPRESSION_DATUM_NODE,
-  EXPRESSION_ROW_ID_NODE,
-  EXPRESSION_SCORE_NODE,
-  EXPRESSION_COLUMN_NODE,
-  EXPRESSION_OPERATOR_NODE
+namespace grnxx {
+namespace {
+
+enum NodeType {
+  DATUM_NODE,
+  ROW_ID_NODE,
+  SCORE_NODE,
+  COLUMN_NODE,
+  OPERATOR_NODE
 };
 
+}  // namespace
+
 class ExpressionNode {
  public:
   ExpressionNode() {}
   virtual ~ExpressionNode() {}
 
   // Return the node type.
-  virtual ExpressionNodeType node_type() const = 0;
+  virtual NodeType node_type() const = 0;
   // Return the result data type.
   virtual DataType data_type() const = 0;
 
@@ -34,11 +39,7 @@ class ExpressionNode {
   // Returns true on success.
   // On failure, returns false and stores error information into "*error" if
   // "error" != nullptr.
-  virtual bool filter(Error *error, RecordSet *record_set) {
-    // TODO: Define "This type is not supported" error.
-    GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet");
-    return false;
-  }
+  virtual bool filter(Error *error, RecordSet *record_set) = 0;
 
   // Evaluate the expression subtree.
   //
@@ -47,257 +48,240 @@ class ExpressionNode {
   // Returns true on success.
   // On failure, returns false and stores error information into "*error" if
   // "error" != nullptr.
-  virtual bool evaluate(Error *error, const RecordSet &record_set) {
-    // TODO: This should be a pure virtual function.
-    GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet");
-    return false;
-  }
+  virtual bool evaluate(Error *error, const RecordSet &record_set) = 0;
 };
 
-// -- ExpressionDatumNode --
-
-class ExpressionDatumNode : public ExpressionNode {
- public:
-  ExpressionDatumNode() : ExpressionNode() {}
-  virtual ~ExpressionDatumNode() {}
-
-  ExpressionNodeType node_type() const {
-    return EXPRESSION_DATUM_NODE;
-  }
-};
+namespace {
 
-class ExpressionBoolDatumNode : public ExpressionDatumNode {
+template <typename T>
+class Node : public ExpressionNode {
  public:
-  ExpressionBoolDatumNode(Bool datum)
-      : ExpressionDatumNode(),
-        datum_(datum) {}
-  ~ExpressionBoolDatumNode() {}
+  Node() : ExpressionNode(), values_() {}
+  virtual ~Node() {}
 
   DataType data_type() const {
-    return BOOL_DATA;
+    return TypeTraits<T>::data_type();
   }
 
-  bool filter(Error *error, RecordSet *record_set) {
-    if (!datum_) {
-      record_set->clear();
-    }
-    return true;
-  }
+  virtual bool filter(Error *error, RecordSet *record_set);
 
-  bool evaluate(Error *error, const RecordSet &record_set) {
-    return true;
-  }
+  virtual bool evaluate(Error *error, const RecordSet &record_set);
 
-  Bool get(Int i) const {
-    return datum_;
+  T get(Int i) const {
+    return values_[i];
   }
 
- private:
-  Bool datum_;
+ protected:
+  std::vector<T> values_;
 };
 
-class ExpressionIntDatumNode : public ExpressionDatumNode {
- public:
-  ExpressionIntDatumNode(Int datum)
-      : ExpressionDatumNode(),
-        datum_(datum) {}
-  ~ExpressionIntDatumNode() {}
-
-  DataType data_type() const {
-    return INT_DATA;
-  }
+template <typename T>
+bool Node<T>::filter(Error *error, RecordSet *record_set) {
+  // TODO: Define "This type is not supported" error.
+  GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet");
+  return false;
+}
 
-  bool evaluate(Error *error, const RecordSet &record_set) {
-    return true;
+template <>
+bool Node<Bool>::filter(Error *error, RecordSet *record_set) {
+  if (!evaluate(error, *record_set)) {
+    return false;
   }
-
-  Int get(Int i) const {
-    return datum_;
+  Int dest = 0;
+  for (Int i = 0; i < record_set->size(); ++i) {
+    if (values_[i]) {
+      record_set->set(dest, record_set->get(i));
+      ++dest;
+    }
   }
+  return record_set->resize(error, dest);
+}
 
- private:
-  Int datum_;
-};
+template <typename T>
+bool Node<T>::evaluate(Error *error, const RecordSet &record_set) {
+  // TODO: This should be a pure virtual function.
+  GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet");
+  return false;
+}
 
-// -- ExpressionRowIDNode --
+// -- DatumNode --
 
-class ExpressionRowIDNode : public ExpressionNode {
+template <typename T>
+class DatumNode : public Node<T> {
  public:
-  ExpressionRowIDNode()
-      : ExpressionNode(),
-        record_set_(nullptr) {}
-  ~ExpressionRowIDNode() {}
-
-  ExpressionNodeType node_type() const {
-    return EXPRESSION_ROW_ID_NODE;
-  }
-  DataType data_type() const {
-    return INT_DATA;
-  }
+  explicit DatumNode(T datum) : Node<T>(), datum_(datum) {}
+  virtual ~DatumNode() {}
 
-  bool evaluate(Error *error, const RecordSet &record_set) {
-    record_set_ = &record_set;
-    return true;
+  NodeType node_type() const {
+    return DATUM_NODE;
   }
 
-  Int get(Int i) const {
-    return record_set_->get(i).row_id;
-  }
+  bool evaluate(Error *error, const RecordSet &record_set);
 
  private:
-  const RecordSet *record_set_;
+  T datum_;
 };
 
-// -- ExpressionScoreNode --
+template <typename T>
+bool DatumNode<T>::evaluate(Error *error, const RecordSet &record_set) {
+  try {
+    this->values_.resize(record_set.size(), datum_);
+    return true;
+  } catch (...) {
+    GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
+    return false;
+  }
+}
+
+// -- RowIDNode --
 
-class ExpressionScoreNode : public ExpressionNode {
+class RowIDNode : public Node<Int> {
  public:
-  ExpressionScoreNode() : ExpressionNode() {}
-  ~ExpressionScoreNode() {}
+  RowIDNode() : Node<Int>() {}
+  ~RowIDNode() {}
 
-  ExpressionNodeType node_type() const {
-    return EXPRESSION_SCORE_NODE;
-  }
-  DataType data_type() const {
-    return FLOAT_DATA;
+  NodeType node_type() const {
+    return ROW_ID_NODE;
   }
 
   bool evaluate(Error *error, const RecordSet &record_set) {
-    record_set_ = &record_set;
+    try {
+      this->values_.resize(record_set.size());
+    } catch (...) {
+      GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
+      return false;
+    }
+    for (Int i = 0; i < record_set.size(); ++i) {
+      this->values_[i] = record_set.get(i).row_id;
+    }
     return true;
   }
-
-  Float get(Int i) const {
-    return record_set_->get(i).score;
-  }
-
- private:
-  const RecordSet *record_set_;
 };
 
-// -- ExpressionColumnNode --
+// -- ScoreNode --
 
-class ExpressionColumnNode : public ExpressionNode {
+class ScoreNode : public Node<Float> {
  public:
-  ExpressionColumnNode() : ExpressionNode() {}
-  virtual ~ExpressionColumnNode() {}
+  ScoreNode() : Node<Float>() {}
+  ~ScoreNode() {}
 
-  ExpressionNodeType node_type() const {
-    return EXPRESSION_COLUMN_NODE;
-  }
-};
-
-class ExpressionBoolColumnNode : public ExpressionColumnNode {
- public:
-  ExpressionBoolColumnNode(const BoolColumn *column)
-      : ExpressionColumnNode(),
-        column_(column),
-        record_set_(nullptr) {}
-  ~ExpressionBoolColumnNode() {}
-
-  DataType data_type() const {
-    return column_->data_type();
+  NodeType node_type() const {
+    return SCORE_NODE;
   }
 
-//  bool filter(Error *error, RecordSet *record_set) {
-//    return false;
-//  }
-
   bool evaluate(Error *error, const RecordSet &record_set) {
-    record_set_ = &record_set;
+    try {
+      this->values_.resize(record_set.size());
+    } catch (...) {
+      GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
+      return false;
+    }
+    for (Int i = 0; i < record_set.size(); ++i) {
+      this->values_[i] = record_set.get(i).score;
+    }
     return true;
   }
-
-  Bool get(Int i) const {
-    return column_->get(record_set_->get(i).row_id);
-  }
-
- private:
-  const BoolColumn *column_;
-  const RecordSet *record_set_;
 };
 
-class ExpressionIntColumnNode : public ExpressionColumnNode {
+// -- ColumnNode --
+
+template <typename T>
+class ColumnNode : public Node<T> {
  public:
-  ExpressionIntColumnNode(const IntColumn *column)
-      : ExpressionColumnNode(),
-        column_(column),
-        record_set_(nullptr) {}
-  ~ExpressionIntColumnNode() {}
+  explicit ColumnNode(const Column *column)
+      : Node<T>(),
+        column_(static_cast<const ColumnImpl<T> *>(column)) {}
+  virtual ~ColumnNode() {}
 
-  DataType data_type() const {
-    return column_->data_type();
+  NodeType node_type() const {
+    return COLUMN_NODE;
   }
 
   bool evaluate(Error *error, const RecordSet &record_set) {
-    record_set_ = &record_set;
+    try {
+      this->values_.resize(record_set.size());
+    } catch (...) {
+      GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
+      return false;
+    }
+    for (Int i = 0; i < record_set.size(); ++i) {
+      this->values_[i] = column_->get(record_set.get(i).row_id);
+    }
     return true;
   }
 
-  Int get(Int i) const {
-    return column_->get(record_set_->get(i).row_id);
-  }
-
  private:
-  const IntColumn *column_;
-  const RecordSet *record_set_;
+  const ColumnImpl<T> *column_;
 };
 
-// -- ExpressionOperatorNode --
-
-// TODO: 演算子の実装はどうしても長くなるので,別ファイルに移行すべきかもしれません.
+// -- OperatorNode --
 
-// TODO: ExpressionOperatorNode<T> (T: 評価結果のデータ型)に data_type と
-//       バッファを持たせてインタフェースを統一しないと,再帰的な組み合わせが発生します.
-
-class ExpressionOperatorNode : public ExpressionNode {
- public:
-  ExpressionOperatorNode() : ExpressionNode() {}
-  virtual ~ExpressionOperatorNode() {}
-
-  ExpressionNodeType node_type() const {
-    return EXPRESSION_OPERATOR_NODE;
-  }
-
-  virtual OperatorType operator_type() const = 0;
+template <typename T, typename U>
+struct Equal {
+  using Arg1 = T;
+  using Arg2 = U;
+  using ResultType = Bool;
+  Bool operator()(Arg1 lhs, Arg2 rhs) const {
+    return lhs == rhs;
+  };
 };
 
-template <typename T, typename U>
-class ExpressionEqualOperatorNode : public ExpressionOperatorNode {
+template <typename Op>
+class BinaryNode : public Node<Bool> {
  public:
-  ExpressionEqualOperatorNode() : ExpressionOperatorNode() {}
-  virtual ~ExpressionEqualOperatorNode() {}
+  using Arg1 = typename Op::Arg1;
+  using Arg2 = typename Op::Arg2;
 
-  DataType data_type() const {
-    return BOOL_DATA;
-  }
-  OperatorType operator_type() const {
-    return EQUAL_OPERATOR;
-  }
+  BinaryNode(Op op,
+             unique_ptr<ExpressionNode> &&lhs,
+             unique_ptr<ExpressionNode> &&rhs)
+      : Node<typename Op::ResultType>(),
+        operator_(op),
+        lhs_(static_cast<Node<Arg1> *>(lhs.release())),
+        rhs_(static_cast<Node<Arg2> *>(rhs.release())) {}
+  virtual ~BinaryNode() {}
 
-  bool evaluate(Error *error, const RecordSet &record_set) {
-    // TODO: Not supported yet.
-    GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet");
-    return false;
+  NodeType node_type() const {
+    return OPERATOR_NODE;
   }
 
-  Int get(Int i) const {
-    return values_[i];
+  bool evaluate(Error *error, const RecordSet &record_set) {
+    try {
+      this->values_.resize(record_set.size());
+    } catch (...) {
+      GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
+      return false;
+    }
+    if (!lhs_->evaluate(error, record_set) ||
+        !rhs_->evaluate(error, record_set)) {
+      return false;
+    }
+    for (Int i = 0; i < record_set.size(); ++i) {
+      this->values_[i] = operator_(lhs_->get(i), rhs_->get(i));
+    }
+    return true;
   }
 
  private:
-  unique_ptr<T> lhs_;
-  unique_ptr<U> rhs_;
-  std::vector<Bool> values_;
+  Op operator_;
+  unique_ptr<Node<Arg1>> lhs_;
+  unique_ptr<Node<Arg2>> rhs_;
 };
 
+}  // namespace
+
 // -- Expression --
 
 unique_ptr<Expression> Expression::create(Error *error,
+                                          const Table *table,
                                           unique_ptr<ExpressionNode> &&root) {
-  // TODO: Not supported yet.
-  GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet");
-  return nullptr;
+  unique_ptr<Expression> expression(
+      new (nothrow) Expression(table, std::move(root)));
+  if (!expression) {
+    GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
+    return nullptr;
+  }
+  return expression;
 }
 
 Expression::~Expression() {}
@@ -307,12 +291,10 @@ DataType Expression::data_type() const {
 }
 
 bool Expression::filter(Error *error, RecordSet *record_set) {
-  // TODO
-  GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet");
-  return false;
+  return root_->filter(error, record_set);
 }
 
-Expression::Expression(Table *table, unique_ptr<ExpressionNode> &&root)
+Expression::Expression(const Table *table, unique_ptr<ExpressionNode> &&root)
     : table_(table),
       root_(std::move(root)) {}
 
@@ -338,16 +320,15 @@ bool ExpressionBuilder::push_datum(Error *error, const Datum &datum) {
     GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
     return false;
   }
-  // TODO: ExpressionDatumNode::create() should be provided to get error
-  //       information.
-  unique_ptr<ExpressionDatumNode> node;
+  // TODO: DatumNode::create() should be provided to get error information.
+  unique_ptr<ExpressionNode> node;
   switch (datum.type()) {
     case BOOL_DATA: {
-      node.reset(new (nothrow) ExpressionBoolDatumNode(datum.force_bool()));
+      node.reset(new (nothrow) DatumNode<Bool>(datum.force_bool()));
       break;
     }
     case INT_DATA: {
-      node.reset(new (nothrow) ExpressionIntDatumNode(datum.force_int()));
+      node.reset(new (nothrow) DatumNode<Int>(datum.force_int()));
       break;
     }
     default: {
@@ -373,25 +354,22 @@ bool ExpressionBuilder::push_column(Error *error, String name) {
   }
   unique_ptr<ExpressionNode> node;
   if (name == "_id") {
-    node.reset(new (nothrow) ExpressionRowIDNode());
+    node.reset(new (nothrow) RowIDNode());
   } else if (name == "_score") {
-    node.reset(new (nothrow) ExpressionScoreNode());
+    node.reset(new (nothrow) ScoreNode());
   } else {
     Column *column = table_->find_column(error, name);
     if (!column) {
       return false;
     }
-    // TODO: The following switch should be done in
-    //       ExpressionColumnNode::create().
+    // TODO: The following switch should be done in ColumnNode::create() or ...
     switch (column->data_type()) {
       case BOOL_DATA: {
-        node.reset(new (nothrow) ExpressionBoolColumnNode(
-            static_cast<BoolColumn *>(column)));
+        node.reset(new (nothrow) ColumnNode<Bool>(column));
         break;
       }
       case INT_DATA: {
-        node.reset(new (nothrow) ExpressionIntColumnNode(
-            static_cast<IntColumn *>(column)));
+        node.reset(new (nothrow) ColumnNode<Int>(column));
         break;
       }
       default: {
@@ -405,6 +383,7 @@ bool ExpressionBuilder::push_column(Error *error, String name) {
     GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
     return false;
   }
+  stack_.push_back(std::move(node));
   return true;
 }
 
@@ -418,15 +397,38 @@ bool ExpressionBuilder::push_operator(Error *error,
         GRNXX_ERROR_SET(error, INVALID_ARGUMENT, "Not enough operands");
         return false;
       }
-      if (stack_[stack_.size() - 1]->data_type() !=
-          stack_[stack_.size() - 2]->data_type()) {
+      auto &lhs = stack_[stack_.size() - 2];
+      auto &rhs = stack_[stack_.size() - 1];
+      if (lhs->data_type() != rhs->data_type()) {
         // TODO: Define a better error code.
         GRNXX_ERROR_SET(error, INVALID_ARGUMENT, "Type conflict");
         return false;
       }
-      // TODO: Not supported yet.
-      GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet");
-      return false;
+      switch (lhs->data_type()) {
+        case BOOL_DATA: {
+          Equal<Bool, Bool> equal;
+          node.reset(new (nothrow) BinaryNode<decltype(equal)>(
+              equal, std::move(lhs), std::move(rhs)));
+          break;
+        }
+        case INT_DATA: {
+          Equal<Int, Int> equal;
+          node.reset(new (nothrow) BinaryNode<decltype(equal)>(
+              equal, std::move(lhs), std::move(rhs)));
+          break;
+        }
+        default: {
+          GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet");
+          return false;
+        }
+      }
+      if (!node) {
+        GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
+        return false;
+      }
+      stack_.resize(stack_.size() - 2);
+      stack_.push_back(std::move(node));
+      return true;
     }
     case NOT_EQUAL_OPERATOR: {
       // TODO: Not supported yet.
@@ -439,12 +441,6 @@ bool ExpressionBuilder::push_operator(Error *error,
       return false;
     }
   }
-  if (!node) {
-    // TODO: Error information should be set in the above switch.
-    GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed");
-    return false;
-  }
-  return true;
 }
 
 void ExpressionBuilder::clear() {
@@ -458,7 +454,7 @@ unique_ptr<Expression> ExpressionBuilder::release(Error *error) {
   }
   unique_ptr<ExpressionNode> root_node = std::move(stack_[0]);
   stack_.clear();
-  return Expression::create(error, std::move(root_node));
+  return Expression::create(error, table_, std::move(root_node));
 }
 
 ExpressionBuilder::ExpressionBuilder(const Table *table) : table_(table) {}

  Modified: test/test_grnxx.cpp (+81 -0)
===================================================================
--- test/test_grnxx.cpp    2014-07-14 01:48:25 +0900 (646a877)
+++ test/test_grnxx.cpp    2014-07-14 14:07:30 +0900 (b7ac8e5)
@@ -16,12 +16,14 @@
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
 #include <cassert>
+#include <iostream>
 
 #include "grnxx/column.hpp"
 #include "grnxx/cursor.hpp"
 #include "grnxx/datum.hpp"
 #include "grnxx/db.hpp"
 #include "grnxx/error.hpp"
+#include "grnxx/expression.hpp"
 #include "grnxx/record.hpp"
 #include "grnxx/table.hpp"
 
@@ -244,12 +246,91 @@ void test_column() {
   assert(datum.force_int() == 123);
 }
 
+void test_expression() {
+  grnxx::Error error;
+
+  auto db = grnxx::open_db(&error, "", grnxx::DBOptions());
+  assert(db);
+
+  auto table = db->create_table(&error, "Table", grnxx::TableOptions());
+  assert(table);
+
+  auto bool_column = table->create_column(&error, "BoolColumn",
+                                          grnxx::BOOL_DATA,
+                                          grnxx::ColumnOptions());
+  assert(bool_column);
+
+  auto int_column = table->create_column(&error, "IntColumn",
+                                         grnxx::INT_DATA,
+                                         grnxx::ColumnOptions());
+  assert(int_column);
+
+  grnxx::Int row_id;
+  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                           grnxx::Datum(), &row_id));
+  assert(bool_column->set(&error, row_id, grnxx::Bool(false)));
+  assert(int_column->set(&error, row_id, grnxx::Int(123)));
+
+  assert(table->insert_row(&error, grnxx::NULL_ROW_ID,
+                           grnxx::Datum(), &row_id));
+  assert(bool_column->set(&error, row_id, grnxx::Bool(true)));
+  assert(int_column->set(&error, row_id, grnxx::Int(456)));
+
+  auto builder = grnxx::ExpressionBuilder::create(&error, table);
+  assert(builder);
+
+  assert(builder->push_datum(&error, grnxx::Bool(true)));
+
+  auto expression = builder->release(&error);
+  assert(expression);
+
+  auto cursor = table->create_cursor(&error, grnxx::CursorOptions());
+  assert(cursor);
+
+  grnxx::RecordSet record_set;
+  assert(cursor->read(&error, 2, &record_set) == 2);
+
+  assert(expression->filter(&error, &record_set));
+  assert(record_set.size() == 2);
+
+  assert(builder->push_datum(&error, grnxx::Int(100)));
+  assert(builder->push_datum(&error, grnxx::Int(100)));
+  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  assert(expression->filter(&error, &record_set));
+  assert(record_set.size() == 2);
+
+  assert(builder->push_column(&error, "BoolColumn"));
+  expression = builder->release(&error);
+  assert(expression);
+
+  assert(expression->filter(&error, &record_set));
+  assert(record_set.size() == 1);
+
+  cursor = table->create_cursor(&error, grnxx::CursorOptions());
+  assert(cursor);
+  record_set.clear();
+  assert(cursor->read(&error, 2, &record_set) == 2);
+
+  assert(builder->push_column(&error, "IntColumn"));
+  assert(builder->push_datum(&error, grnxx::Int(123)));
+  assert(builder->push_operator(&error, grnxx::EQUAL_OPERATOR));
+  expression = builder->release(&error);
+  assert(expression);
+
+  assert(expression->filter(&error, &record_set));
+  assert(record_set.size() == 1);
+}
+
 }  // namespace
 
 int main() {
   test_db();
   test_table();
   test_column();
+  test_expression();
 
   return 0;
 }
-------------- next part --------------
HTML����������������������������...
Download 



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