susumu.yata
null+****@clear*****
Tue Jul 8 13:10:16 JST 2014
susumu.yata 2014-07-08 13:10:16 +0900 (Tue, 08 Jul 2014) New Revision: 16a80abe5a30a4e642102a0065b8454adf51391b https://github.com/groonga/grnxx/commit/16a80abe5a30a4e642102a0065b8454adf51391b Message: Add TableCursor. Added files: lib/grnxx/cursor.cpp Modified files: include/grnxx/cursor.hpp include/grnxx/record.hpp include/grnxx/table.hpp include/grnxx/types.hpp lib/grnxx/Makefile.am lib/grnxx/table.cpp test/test_grnxx.cpp Modified: include/grnxx/cursor.hpp (+26 -11) =================================================================== --- include/grnxx/cursor.hpp 2014-07-07 11:59:41 +0900 (3c32dd4) +++ include/grnxx/cursor.hpp 2014-07-08 13:10:16 +0900 (9f0df0e) @@ -6,28 +6,43 @@ namespace grnxx { struct CursorOptions { + // The first "offset" records are skipped. + Int offset; + + // At most "limit" records are read. + Int limit; + + // The order of records. + OrderType order_type; + + // Initialize the options. + CursorOptions(); }; class Cursor { public: - Cursor() {} + explicit Cursor(Table *table) : table_(table) {} virtual ~Cursor() {} - // Skip at most the next "count" records. - // - // Returns the number of skipped records on success. - // On failure, returns -1 and stores error information into "*error" if - // "error" != nullptr. - virtual size_t seek(Error *error, size_t count) = 0; + // Return the associated table. + Table *table() const { + return table_; + } - // Read at most the next "count" records. + // Read the next records. // - // Records are stored into "*record_set". + // Reads at most "max_count" records and stores the records into + // "*record_set". // - // Returns the number of read records on success. + // On success, returns the number of records read on success. // On failure, returns -1 and stores error information into "*error" if // "error" != nullptr. - virtual size_t read(Error *error, size_t count, RecordSet *record_set) = 0; + virtual Int read(Error *error, + Int max_count, + RecordSet *record_set) = 0; + + protected: + Table *table_; }; } // namespace grnxx Modified: include/grnxx/record.hpp (+32 -14) =================================================================== --- include/grnxx/record.hpp 2014-07-07 11:59:41 +0900 (4246332) +++ include/grnxx/record.hpp 2014-07-08 13:10:16 +0900 (66d90d5) @@ -1,6 +1,8 @@ #ifndef GRNXX_RECORD_HPP #define GRNXX_RECORD_HPP +#include <vector> + #include "grnxx/types.hpp" namespace grnxx { @@ -8,34 +10,50 @@ namespace grnxx { struct Record { Int row_id; Float score; + + Record() = default; + Record(Int row_id, Float score) : row_id(row_id), score(score) {} }; class RecordSet { public: - RecordSet(); - ~RecordSet(); + RecordSet() : records_() {} + ~RecordSet() {} - // Return the associated table. - Table *table() const { - return table_; - } // Return the number of records. - size_t num_records() const { - return size_; + Int size() const { + return static_cast<Int>(records_.size()); + } + + // Append a record. + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool append(Error *error, const Record &record) { + try { + records_.push_back(record); + return true; + } catch (...) { + // TODO: Error report. + return false; + } } // Return the record identified by "i". // - // The result is undefined if "i" is invalid. - Record get(size_t i) const { + // If "i" is invalid, the result is undefined. + Record get(Int i) const { return records_[i]; } + // Clear all the records. + void clear() { + records_.clear(); + } + private: - Table table_; - size_t size_; - size_t capacity_; - unique_ptr<Record[]> records_; + std::vector<Record> records_; }; } // namespace grnxx Modified: include/grnxx/table.hpp (+11 -10) =================================================================== --- include/grnxx/table.hpp 2014-07-07 11:59:41 +0900 (701f065) +++ include/grnxx/table.hpp 2014-07-08 13:10:16 +0900 (774b339) @@ -168,31 +168,31 @@ class Table { // "error" != nullptr. unique_ptr<Cursor> create_cursor( Error *error, - const CursorOptions &options) const; + const CursorOptions &options); // Create an object to build expressions. // // Returns a pointer to the builder on success. // On failure, returns nullptr and stores error information into "*error" if // "error" != nullptr. -// virtual unique_ptr<ExpressionBuilder> create_expression_builder( -// Error *error) const = 0; +// unique_ptr<ExpressionBuilder> create_expression_builder( +// Error *error) const; // Create an object to build ordering information. // // Returns a pointer to the builder on success. // On failure, returns nullptr and stores error information into "*error" if // "error" != nullptr. -// virtual unique_ptr<OrderBuilder> create_order_builder( -// Error *error) const = 0; +// unique_ptr<OrderBuilder> create_order_builder( +// Error *error) const; // Create an object to build pipelines. // // Returns a pointer to the builder on success. // On failure, returns nullptr and stores error information into "*error" if // "error" != nullptr. -// virtual unique_ptr<PipelineBuilder> create_pipeline_builder( -// Error *error) const = 0; +// unique_ptr<PipelineBuilder> create_pipeline_builder( +// Error *error) const; // TODO: Grouping (drilldown). // @@ -203,10 +203,10 @@ class Table { // 失敗する状況としては,以下のようなものが挙げられる. // - オプションが不正である. // - リソースが確保できない. -// virtual unique_ptr<Grouper> create_grouper( +// unique_ptr<Grouper> create_grouper( // Error *error, -// Expression *expression, -// const GrouperOptions &options) const = 0; +// unique_ptr<Expression> &&expression, +// const GrouperOptions &options) const; private: DB *db_; @@ -273,6 +273,7 @@ class Table { size_t *column_id) const; friend class DB; + friend class TableCursor; }; } // namespace grnxx Modified: include/grnxx/types.hpp (+13 -0) =================================================================== --- include/grnxx/types.hpp 2014-07-07 11:59:41 +0900 (c807257) +++ include/grnxx/types.hpp 2014-07-08 13:10:16 +0900 (dd3660f) @@ -3,6 +3,7 @@ #include <cinttypes> #include <cstring> +#include <limits> #include <memory> namespace grnxx { @@ -26,6 +27,9 @@ using std::uint64_t; // Integer type for representing offset and size. using std::size_t; +// Limitations. +using std::numeric_limits; + // Smart pointer type. using std::unique_ptr; @@ -300,10 +304,19 @@ enum IndexType { HASH_INDEX }; +enum OrderType { + // The natural order. + REGULAR_ORDER, + + // The reverse order of REGULAR_ORDER. + REVERSE_ORDER +}; + // Database temporary object types. class Datum; class Cursor; class RecordSet; +class ExpressionBuilder; // Database temporary object option types. struct CursorOptions; Modified: lib/grnxx/Makefile.am (+1 -0) =================================================================== --- lib/grnxx/Makefile.am 2014-07-07 11:59:41 +0900 (6b92d83) +++ lib/grnxx/Makefile.am 2014-07-08 13:10:16 +0900 (d809770) @@ -10,6 +10,7 @@ libgrnxx_la_LDFLAGS = @AM_LTLDFLAGS@ libgrnxx_la_SOURCES = \ column.cpp \ + cursor.cpp \ db.cpp \ error.cpp \ index.cpp \ Added: lib/grnxx/cursor.cpp (+10 -0) 100644 =================================================================== --- /dev/null +++ lib/grnxx/cursor.cpp 2014-07-08 13:10:16 +0900 (de8202f) @@ -0,0 +1,10 @@ +#include "grnxx/cursor.hpp" + +namespace grnxx { + +CursorOptions::CursorOptions() + : offset(0), + limit(numeric_limits<Int>::max()), + order_type(REGULAR_ORDER) {} + +} // namespace grnxx Modified: lib/grnxx/table.cpp (+156 -4) =================================================================== --- lib/grnxx/table.cpp 2014-07-07 11:59:41 +0900 (89b13c9) +++ lib/grnxx/table.cpp 2014-07-08 13:10:16 +0900 (c9ba1e4) @@ -4,9 +4,163 @@ #include "grnxx/cursor.hpp" #include "grnxx/db.hpp" #include "grnxx/error.hpp" +#include "grnxx/record.hpp" namespace grnxx { +// -- TableCursor -- + +class TableCursor : public Cursor { + public: + // -- Public API -- + + ~TableCursor() {} + + Table *table() const { + return table_; + } + Int read(Error *error, Int max_count, RecordSet *record_set); + + // -- Internal API -- + + // Create a cursor. + // + // Returns a pointer to the cursor on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + static unique_ptr<TableCursor> create(Error *error, + Table *table, + const CursorOptions &options); + + // Read records in regular order. + Int regular_read(Error *error, Int max_count, RecordSet *record_set); + + // Read records in reverse order. + Int reverse_read(Error *error, Int max_count, RecordSet *record_set); + + private: + Int offset_left_; + Int limit_left_; + OrderType order_type_; + Int next_row_id_; + + explicit TableCursor(Table *table); +}; + +Int TableCursor::read(Error *error, Int max_count, RecordSet *record_set) { + if (max_count <= 0) { + return 0; + } + switch (order_type_) { + case REGULAR_ORDER: { + return regular_read(error, max_count, record_set); + } + case REVERSE_ORDER: { + return reverse_read(error, max_count, record_set); + } + default: { + GRNXX_ERROR_SET(error, BROKEN, "Broken cursor"); + return -1; + } + } +} + +unique_ptr<TableCursor> TableCursor::create(Error *error, + Table *table, + const CursorOptions &options) { + unique_ptr<TableCursor> cursor(new (nothrow) TableCursor(table)); + if (!cursor) { + GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed"); + return nullptr; + } + cursor->offset_left_ = options.offset; + cursor->limit_left_ = options.limit; + switch (options.order_type) { + case REGULAR_ORDER: { + cursor->order_type_ = REGULAR_ORDER; + cursor->next_row_id_ = MIN_ROW_ID; + break; + } + case REVERSE_ORDER: { + cursor->order_type_ = REVERSE_ORDER; + cursor->next_row_id_ = table->max_row_id(); + break; + } + default: { + GRNXX_ERROR_SET(error, INVALID_ARGUMENT, "Invalid order type"); + return nullptr; + } + } + return cursor; +} + +TableCursor::TableCursor(Table *table) + : Cursor(table), + offset_left_(), + limit_left_(), + order_type_(), + next_row_id_() {} + +Int TableCursor::regular_read(Error *error, + Int max_count, + RecordSet *record_set) { + Int count = 0; + while (next_row_id_ <= table_->max_row_id()) { + // TODO: This check should be skipped if there are no deleted rows. + if (!table_->get_bit(next_row_id_)) { + ++next_row_id_; + continue; + } + if (offset_left_ > 0) { + --offset_left_; + ++next_row_id_; + } else { + // TODO: Resize the buffer outside the loop and set records in the loop. + if (!record_set->append(error, Record(next_row_id_, 0.0))) { + return -1; + } + --limit_left_; + ++count; + ++next_row_id_; + if ((limit_left_ <= 0) || (count >= max_count)) { + break; + } + } + } + return count; +} + +Int TableCursor::reverse_read(Error *error, + Int max_count, + RecordSet *record_set) { + Int count = 0; + while (next_row_id_ >= MIN_ROW_ID) { + // TODO: This check should be skipped if there are no deleted rows. + if (!table_->get_bit(next_row_id_)) { + --next_row_id_; + continue; + } + if (offset_left_ > 0) { + --offset_left_; + --next_row_id_; + } else { + // TODO: Resize the buffer outside the loop and set records in the loop. + if (!record_set->append(error, Record(next_row_id_, 0.0))) { + return -1; + } + --limit_left_; + ++count; + --next_row_id_; + if ((limit_left_ <= 0) || (count >= max_count)) { + break; + } + } + } + return count; +} + +// -- Table -- + Table::~Table() {} Column *Table::create_column(Error *error, @@ -225,10 +379,8 @@ Int Table::find_row(Error *error, const Datum &key) const { unique_ptr<Cursor> Table::create_cursor( Error *error, - const CursorOptions &options) const { - // TODO: Cursor is not supported yet. - GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet"); - return nullptr; + const CursorOptions &options) { + return TableCursor::create(error, this, options); } unique_ptr<Table> Table::create(Error *error, Modified: test/test_grnxx.cpp (+28 -1) =================================================================== --- test/test_grnxx.cpp 2014-07-07 11:59:41 +0900 (eb26a4d) +++ test/test_grnxx.cpp 2014-07-08 13:10:16 +0900 (001d90c) @@ -18,9 +18,11 @@ #include <cassert> #include "grnxx/column.hpp" +#include "grnxx/cursor.hpp" #include "grnxx/datum.hpp" #include "grnxx/db.hpp" #include "grnxx/error.hpp" +#include "grnxx/record.hpp" #include "grnxx/table.hpp" namespace { @@ -159,7 +161,32 @@ void test_table() { // TODO: find_row(). - // TODO: create_cursor(). + grnxx::CursorOptions cursor_options; + auto cursor = table->create_cursor(&error, cursor_options); + assert(cursor); + + grnxx::RecordSet record_set; + assert(cursor->read(&error, 0, &record_set) == 0); + + assert(cursor->read(&error, 1, &record_set) == 1); + assert(record_set.size() == 1); + assert(record_set.get(0).row_id == 1); + + assert(cursor->read(&error, 2, &record_set) == 1); + assert(record_set.size() == 2); + assert(record_set.get(0).row_id == 1); + assert(record_set.get(1).row_id == 3); + + record_set.clear(); + + cursor_options.order_type = grnxx::REVERSE_ORDER; + cursor = table->create_cursor(&error, cursor_options); + assert(cursor); + + assert(cursor->read(&error, 100, &record_set) == 2); + assert(record_set.size() == 2); + assert(record_set.get(0).row_id == 3); + assert(record_set.get(1).row_id == 1); } void test_column() { -------------- next part -------------- HTML����������������������������...Download