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