[Groonga-commit] groonga/grnxx at 16a80ab [master] Add TableCursor.

Back to archive index

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 



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