susumu.yata
null+****@clear*****
Fri Jul 4 12:25:36 JST 2014
susumu.yata 2014-07-04 12:25:36 +0900 (Fri, 04 Jul 2014) New Revision: 83caa519f92570af49ebec401d22e9f2a67a816c https://github.com/groonga/grnxx/commit/83caa519f92570af49ebec401d22e9f2a67a816c Message: Initial commit for dummy13. Added files: include/Makefile.am include/grnxx/Makefile.am include/grnxx/column.hpp include/grnxx/cursor.hpp include/grnxx/datum.hpp include/grnxx/db.hpp include/grnxx/error.hpp include/grnxx/index.hpp include/grnxx/library.hpp include/grnxx/record.hpp include/grnxx/table.hpp include/grnxx/types.hpp lib/grnxx/db.cpp lib/grnxx/error.cpp lib/grnxx/name.cpp lib/grnxx/name.hpp Removed files: lib/grnxx/calc.cpp lib/grnxx/calc.hpp lib/grnxx/calc_impl.cpp lib/grnxx/calc_impl.hpp lib/grnxx/column.hpp lib/grnxx/column_impl.cpp lib/grnxx/database.cpp lib/grnxx/database.hpp lib/grnxx/datum.cpp lib/grnxx/datum.hpp lib/grnxx/index.hpp lib/grnxx/library.hpp lib/grnxx/sorter.cpp lib/grnxx/sorter.hpp lib/grnxx/sorter_impl.cpp lib/grnxx/sorter_impl.hpp lib/grnxx/string.cpp lib/grnxx/string.hpp lib/grnxx/table.hpp lib/grnxx/timer.cpp lib/grnxx/timer.hpp lib/grnxx/types.cpp lib/grnxx/types.hpp Modified files: Makefile.am configure.ac lib/grnxx/Makefile.am lib/grnxx/column.cpp lib/grnxx/column_impl.hpp lib/grnxx/index.cpp lib/grnxx/library.cpp lib/grnxx/table.cpp src/grnxx.cpp test/benchmark_grnxx.cpp test/test_grnxx.cpp Modified: Makefile.am (+1 -1) =================================================================== --- Makefile.am 2014-06-25 17:55:59 +0900 (336eec2) +++ Makefile.am 2014-07-04 12:25:36 +0900 (5c53eb7) @@ -1,6 +1,6 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = lib src test +SUBDIRS = include lib src test EXTRA_DIST = \ COPYING-GPL-3.0 \ Modified: configure.ac (+6 -14) =================================================================== --- configure.ac 2014-06-25 17:55:59 +0900 (354229a) +++ configure.ac 2014-07-04 12:25:36 +0900 (50ee79e) @@ -2,8 +2,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.67]) -AC_INIT([grnxx], [dummy12], [groonga �� razil.jp]) -AC_CONFIG_SRCDIR([lib/grnxx/library.hpp]) +AC_INIT([grnxx], [dummy13], [groonga �� razil.jp]) +AC_CONFIG_SRCDIR([lib/grnxx/library.cpp]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE @@ -14,7 +14,8 @@ AC_PROG_INSTALL AC_CONFIG_MACRO_DIR([m4]) -AM_CXXFLAGS="-Wall -std=c++11 -fno-strict-aliasing -I\${top_srcdir}/lib" +AM_CXXFLAGS="-Wall -std=c++11 -fno-strict-aliasing" +AM_CXXFLAGS="${AM_CXXFLAGS} -I\${top_srcdir}/include -I\${top_srcdir}/lib" AC_C_BIGENDIAN AC_CANONICAL_HOST @@ -34,17 +35,6 @@ AC_SUBST([AM_LTLDFLAGS]) # Checks for libraries. -AC_ARG_ENABLE([varint], - [AS_HELP_STRING([--enable-varint], - [enable variable integer type [default=no]])], - [], - [enable_varint="no"]) -if test "x$enable_varint" != "xno"; then - AC_DEFINE(GRNXX_ENABLE_VARIABLE_INTEGER_TYPE, - [1], - [Define to 1 for variable integer type]) -fi - AC_ARG_ENABLE([backtrace], [AS_HELP_STRING([--enable-backtrace], [show backtrace on error [default=no]])], @@ -60,6 +50,8 @@ fi AC_CHECK_LIB([rt], [clock_gettime]) AC_CONFIG_FILES([Makefile + include/Makefile + include/grnxx/Makefile lib/Makefile lib/grnxx/Makefile src/Makefile Added: include/Makefile.am (+1 -0) 100644 =================================================================== --- /dev/null +++ include/Makefile.am 2014-07-04 12:25:36 +0900 (767034a) @@ -0,0 +1 @@ +SUBDIRS = grnxx Added: include/grnxx/Makefile.am (+9 -0) 100644 =================================================================== --- /dev/null +++ include/grnxx/Makefile.am 2014-07-04 12:25:36 +0900 (e1a96ec) @@ -0,0 +1,9 @@ +pkginclude_HEADERS = \ + column.hpp \ + cursor.hpp \ + db.hpp \ + error.hpp \ + index.hpp \ + library.hpp \ + table.hpp \ + types.hpp Added: include/grnxx/column.hpp (+192 -0) 100644 =================================================================== --- /dev/null +++ include/grnxx/column.hpp 2014-07-04 12:25:36 +0900 (4b0652b) @@ -0,0 +1,192 @@ +#ifndef GRNXX_COLUMN_HPP +#define GRNXX_COLUMN_HPP + +#include <vector> + +#include "grnxx/types.hpp" + +#include "grnxx/name.hpp" + +namespace grnxx { + +struct ColumnOptions { +}; + +class Column { + public: + virtual ~Column(); + + // Return the table. + Table *table() const { + return table_; + } + // Return the name. + String name() const { + return name_.ref(); + } + // Return the data type. + DataType data_type() const { + return data_type_; + } + // Return the referenced (parent) table, or nullptr if the column is not a + // reference column. + Table *ref_table() const { + return ref_table_; + } + // Return whether the column has the key attribute or not. + bool has_key_attribute() const { + return has_key_attribute_; + } + // Return the number of indexes. + size_t num_indexes() const { + // TODO: Index is not supported yet. + return 0; + } + + // Create an index with "name", "index_type", and "index_options". + // + // Returns a pointer to the index on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + virtual Index *create_index(Error *error, + String name, + IndexType index_type, + const IndexOptions &index_options); + + // Remove an index named "name". + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + // + // Note: Pointers to the removed index must not be used after deletion. + bool remove_index(Error *error, String name); + + // Rename an index named "name" to "new_name". + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool rename_index(Error *error, + String name, + String new_name); + + // Change the order of indexes. + // + // Moves an index named "name" to the head if "prev_name" is + // nullptr or an empty string. + // Does nothing if "name" == "prev_name". + // Moves an index named "name" to next to an index named + // "prev_name". + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool reorder_index(Error *error, + String name, + String prev_name); + + // Get an index identified by "index_id". + // + // Returns a pointer to the index on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + Index *get_index(Error *error, size_t index_id) const; + + // Find an index named "name". + // + // Returns a pointer to the index on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + Index *find_index(Error *error, String name) const; + + // Set a value. + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + virtual bool set(Error *error, Int row_id, const Datum &datum); + + // Get a value. + // + // Stores a value identified by "row_id" into "*datum". + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + virtual bool get(Error *error, Int row_id, Datum *datum) const; + + // Create a cursor to get records. + // + // Returns a pointer to the cursor on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + virtual unique_ptr<Cursor> create_cursor( + Error *error, + const CursorOptions &options) const; + + protected: + Table *table_; + Name name_; + DataType data_type_; + Table *ref_table_; + bool has_key_attribute_; + + Column(); + + // Initialize the base members. + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool initialize_base(Error *error, + Table *table, + String name, + DataType data_type, + const ColumnOptions &options); + + private: + // 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<Column> create(Error *error, + Table *table, + String name, + DataType data_type, + const ColumnOptions &options); + + // Change the column name. + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool rename(Error *error, String new_name); + + // Return whether the column is removable or not. + bool is_removable(); + + // Set the initial key. + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + virtual bool set_initial_key(Error *error, Int row_id, const Datum &key); + + // Set the default value. + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + virtual bool set_default_value(Error *error, Int row_id); + + // Unset the value. + virtual void unset(Int row_id); + + friend class Table; +}; + +} // namespace grnxx + +#endif // GRNXX_COLUMN_HPP Added: include/grnxx/cursor.hpp (+35 -0) 100644 =================================================================== --- /dev/null +++ include/grnxx/cursor.hpp 2014-07-04 12:25:36 +0900 (3c32dd4) @@ -0,0 +1,35 @@ +#ifndef GRNXX_CURSOR_HPP +#define GRNXX_CURSOR_HPP + +#include "grnxx/types.hpp" + +namespace grnxx { + +struct CursorOptions { +}; + +class Cursor { + public: + Cursor() {} + 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; + + // Read at most the next "count" records. + // + // Records are stored into "*record_set". + // + // Returns the number of read records 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; +}; + +} // namespace grnxx + +#endif // GRNXX_CURSOR_HPP Added: include/grnxx/datum.hpp (+74 -0) 100644 =================================================================== --- /dev/null +++ include/grnxx/datum.hpp 2014-07-04 12:25:36 +0900 (d4eefbd) @@ -0,0 +1,74 @@ +#ifndef GRNXX_DATUM_HPP +#define GRNXX_DATUM_HPP + +#include "grnxx/types.hpp" + +namespace grnxx { + +class Datum { + public: + // The default constructor does nothing. + Datum() = default; + + Datum(Bool value) + : type_(BOOL_DATA), + bool_(value) {} + Datum(Int value) + : type_(INT_DATA), + int_(value) {} + Datum(Float value) + : type_(FLOAT_DATA), + float_(value) {} + Datum(Time value) + : type_(TIME_DATA), + time_(value) {} + Datum(GeoPoint value) + : type_(GEO_POINT_DATA), + geo_point_(value) {} + Datum(String value) + : type_(TEXT_DATA), + text_(value) {} + + // Return the data type. + DataType type() const { + return type_; + } + + // TODO: Typecast operators are dangerous. + // These should not be used. + // + // Force the specified interpretation. + explicit operator Bool() const { + return bool_; + } + explicit operator Int() const { + return int_; + } + explicit operator Float() const { + return float_; + } + explicit operator Time() const { + return time_; + } + explicit operator GeoPoint() const { + return geo_point_; + } + explicit operator Text() const { + return text_; + } + + private: + DataType type_; + union { + Bool bool_; + Int int_; + Float float_; + Time time_; + GeoPoint geo_point_; + String text_; + }; +}; + +} // namespace grnxx + +#endif // GRNXX_DATUM_HPP Added: include/grnxx/db.hpp (+127 -0) 100644 =================================================================== --- /dev/null +++ include/grnxx/db.hpp 2014-07-04 12:25:36 +0900 (7d3583b) @@ -0,0 +1,127 @@ +#ifndef GRNXX_DB_HPP +#define GRNXX_DB_HPP + +#include <vector> + +#include "grnxx/types.hpp" + +namespace grnxx { + +struct DBOptions { +}; + +class DB { + public: + ~DB(); + + // Return the number of tables. + size_t num_tables() const { + return tables_.size(); + } + + // Create a table with "name" and "options". + // + // Returns a pointer to the table on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + Table *create_table(Error *error, + String name, + const TableOptions &options); + + // Remove a table named "name". + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + // + // Note: Pointers to the removed table must not be used after deletion. + bool remove_table(Error *error, String name); + + // Rename a table named "name" to "new_name". + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool rename_table(Error *error, + String name, + String new_name); + + // Change the order of tables. + // + // Moves a table named "name" to the head if "prev_name" is nullptr or an + // empty string. + // Does nothing if "name" == "prev_name". + // Moves a table named "name" to next to a table named "prev_name". + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool reorder_table(Error *error, + String name, + String prev_name); + + // Get a table identified by "table_id". + // + // Returns a pointer to the table on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + Table *get_table(Error *error, size_t table_id) const; + + // Find a table named "name". + // + // Returns a pointer to the table on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + Table *find_table(Error *error, String name) const; + + // Save the database into a file. + // If "path" is nullptr or an empty string, saves the database into its + // associated file. + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool save(Error *error, + String path, + const DBOptions &options) const; + + private: + std::vector<unique_ptr<Table>> tables_; + + DB(); + + // Find a table with its ID. + // + // Returns a pointer to the table on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + Table *find_table_with_id(Error *error, + String name, + size_t *table_id) const; + + friend unique_ptr<DB> open_db(Error *error, + String path, + const DBOptions &options); +}; + +// Open or create a database. +// +// If "path" is nullptr or an empty string, creates a temporary database. +// +// Returns a pointer to the database on success. +// On failure, returns nullptr and stores error information into "*error" if +// "error" != nullptr. +unique_ptr<DB> open_db(Error *error, + String path, + const DBOptions &options); + +// Remove a database identified by "path". +// +// Returns true on success. +// On failure, returns false and stores error information into "*error" if +// "error" != nullptr. +bool remove_db(Error *error, String path, const DBOptions &options); + +} // namespace grnxx + +#endif // GRNXX_DB_HPP Added: include/grnxx/error.hpp (+100 -0) 100644 =================================================================== --- /dev/null +++ include/grnxx/error.hpp 2014-07-04 12:25:36 +0900 (bedc58e) @@ -0,0 +1,100 @@ +#ifndef GRNXX_ERROR_HPP +#define GRNXX_ERROR_HPP + +#include "grnxx/types.hpp" + +namespace grnxx { + +enum ErrorCode { + NO_ERROR, // No error occurred. + NOT_FOUND, // The target does not found. + ALREADY_EXISTS, // The target already exists. + NOT_REMOVABLE, // The target is not removable. + BROKEN, // The database is broken. + NO_MEMORY, // Memory allocation failed. + INVALID_NAME, // The string is invalid as an object name. + NO_KEY_COLUMN, // The table has no key column. + INVALID_ARGUMENT, // Invalid argument. + NOT_SUPPORTED_YET // The operation is not supported yet. +}; + +// Many functions take a pointer to an Error object as the first argument +// (except an implicit this argument) for returning error information on +// failure. On success, the Error object will not be modified. If the pointer +// is nullptr, the error information will not be generated even on failure. +class Error { + public: + // The error code of an initialized Error object is NO_ERROR. + Error() + : code_(NO_ERROR), + line_(0), + file_(""), + function_(""), + message_() { + message_[0] = '\0'; + } + + // Error code. + ErrorCode code() const { + return code_; + } + // Line number (__LINE__). + int line() const { + return code_; + } + // File name (__FILE__). + const char *file() const { + return file_; + } + // Function name (__func__, __FUNCTION__, or __PRETTY_FUNCTION__). + const char *function() const { + return function_; + } + // Error message. + const char *message() const { + return message_; + } + + // Set an error code. + void set_code(ErrorCode code) { + code_ = code; + } + // Set a line number. + void set_line(int line) { + line_ = line; + } + // Set a file name. + void set_file(const char *file) { + file_ = file; + } + // Set a function name. + void set_function(const char *function) { + function_ = function; + } + // Generate an error message with the printf syntax. + // If the expected message is too long, the result will be truncated. + // Return true on success. + bool set_message(const char *format, ...) + __attribute__ ((format (printf, 2, 3))); + + private: + static constexpr Int MESSAGE_BUF_SIZE = 256; + + ErrorCode code_; + int line_; + const char *file_; + const char *function_; + char message_[MESSAGE_BUF_SIZE]; +}; + +} // namespace grnxx + +// Set error information. +#define GRNXX_ERROR_SET(error, code, format, ...) \ + (((error) != nullptr) && ((error)->set_code(code), \ + (error)->set_line(__LINE__), \ + (error)->set_file(__FILE__), \ + (error)->set_function(__PRETTY_FUNCTION__), \ + (error)->set_message(format, ## __VA_ARGS__))) + +#endif // GRNXX_ERROR_HPP Added: include/grnxx/index.hpp (+72 -0) 100644 =================================================================== --- /dev/null +++ include/grnxx/index.hpp 2014-07-04 12:25:36 +0900 (26c958b) @@ -0,0 +1,72 @@ +#ifndef GRNXX_INDEX_HPP +#define GRNXX_INDEX_HPP + +#include "grnxx/types.hpp" + +#include "grnxx/name.hpp" + +namespace grnxx { + +struct IndexOptions { +}; + +class Index { + public: + virtual ~Index(); + + // Return the owner column. + Column *column() const { + return column_; + } + // Return the name. + String name() const { + return name_.ref(); + } + // Return the index type. + IndexType type() const { + return type_; + } + + // Create a cursor to get records. + // + // Returns a pointer to the cursor on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + virtual unique_ptr<Cursor> create_cursor( + Error *error, + const CursorOptions &options) const; + + private: + Column *column_; + Name name_; + IndexType type_; + + // Create a new index. + // + // Returns a pointer to the index on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + static unique_ptr<Index> create(Error *error, + Column *column, + String name, + IndexType type, + const IndexOptions &options); + + Index(); + + // Change the index name. + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool rename(Error *error, String new_name); + + // Return whether the index is removable or not. + bool is_removable(); + + friend class Column; +}; + +} // namespace grnxx + +#endif // GRNXX_INDEX_HPP Added: include/grnxx/library.hpp (+17 -0) 100644 =================================================================== --- /dev/null +++ include/grnxx/library.hpp 2014-07-04 12:25:36 +0900 (922f070) @@ -0,0 +1,17 @@ +#ifndef GRNXX_LIBRARY_HPP +#define GRNXX_LIBRARY_HPP + +namespace grnxx { + +class Library { + public: + // Return the library package name. + static const char *package(); + + // Return the library version. + static const char *version(); +}; + +} // namespace grnxx + +#endif // GRNXX_LIBRARY_HPP Added: include/grnxx/record.hpp (+43 -0) 100644 =================================================================== --- /dev/null +++ include/grnxx/record.hpp 2014-07-04 12:25:36 +0900 (4246332) @@ -0,0 +1,43 @@ +#ifndef GRNXX_RECORD_HPP +#define GRNXX_RECORD_HPP + +#include "grnxx/types.hpp" + +namespace grnxx { + +struct Record { + Int row_id; + Float score; +}; + +class RecordSet { + public: + RecordSet(); + ~RecordSet(); + + // Return the associated table. + Table *table() const { + return table_; + } + // Return the number of records. + size_t num_records() const { + return size_; + } + + // Return the record identified by "i". + // + // The result is undefined if "i" is invalid. + Record get(size_t i) const { + return records_[i]; + } + + private: + Table table_; + size_t size_; + size_t capacity_; + unique_ptr<Record[]> records_; +}; + +} // namespace grnxx + +#endif // GRNXX_RECORD_HPP Added: include/grnxx/table.hpp (+276 -0) 100644 =================================================================== --- /dev/null +++ include/grnxx/table.hpp 2014-07-04 12:25:36 +0900 (eb167ad) @@ -0,0 +1,276 @@ +#ifndef GRNXX_TABLE_HPP +#define GRNXX_TABLE_HPP + +#include <vector> + +#include "grnxx/types.hpp" + +#include "grnxx/name.hpp" + +namespace grnxx { + +struct TableOptions { +}; + +class Table { + public: + ~Table(); + + // Return the owner DB. + DB *db() const { + return db_; + } + // Return the name. + String name() const { + return name_.ref(); + } + // Return the number of columns. + size_t num_columns() const { + return columns_.size(); + } + // Return the key column, or nullptr if the table has no key column. + Column *key_column() const { + return key_column_; + } + // Return the maximum row ID. + Int max_row_id() const { + return max_row_id_; + } + + // Create a column with "name", "data_type", and "options". + // + // Returns a pointer to the table on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + Column *create_column(Error *error, + String name, + DataType data_type, + const ColumnOptions &options); + + // Remove a column named "name". + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + // + // Note: Pointers to the removed column must not be used after deletion. + bool remove_column(Error *error, String name); + + // Rename a column named "name" to "new_name". + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool rename_column(Error *error, + String name, + String new_name); + + // Change the order of columns. + // + // Moves a column named "name" to the head if "prev_name" is nullptr or an + // empty string. + // Does nothing if "name" == "prev_name". + // Moves a column named "name" to next to a column named "prev_name". + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool reorder_column(Error *error, + String name, + String prev_name); + + // Get a column identified by "column_id". + // + // Returns a pointer to the column on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + Column *get_column(Error *error, size_t column_id) const; + + // Find a column named "name". + // + // Returns a pointer to the column on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + Column *find_column(Error *error, String name) const; + + // Set the key attribute to the column named "name". + // + // Fails if the table already has a key column. + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool set_key_column(Error *error, String name); + + // Unset the key attribute of the key column. + // + // Fails if the table does not have a key column. + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool unset_key_column(Error *error); + + // Insert a row. + // + // If "request_row_id" is valid, inserts a row identified by + // "request_row_id". + // If "key" is valid, inserts a row identified by "key". + // If both are invalid, the next row ID is allocated. + // + // Fails if "request_row_id" is valid and points to an existing row. + // Fails if the table has a key column and "key" is invalid. + // Fails if the table does not have a key column and "key" is valid. + // + // On success, stores the inserted row ID into "*result_row_id". + // On failure, if "request_row_id" or "key" matches an existing row, + // stores the matched row ID into "*result_row_id". + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool insert_row(Error *error, + Int request_row_id, + const Datum &key, + Int *result_row_id); + + // Remove a row identified by "row_id". + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool remove_row(Error *error, Int row_id); + + // Check the validity of a row. + // + // Returns true if the row is valid. + // Otherwise, returns false and stores error information into "*error" if + // "error" != nullptr. + bool test_row(Error *error, Int row_id) const; + + // Find a row identified by "key". + // + // Fails if the table does not have a key column. + // + // Returns the row ID on success. + // On failure, returns NULL_ROW_ID and stores error information into + // "*error" if "error" != nullptr. + Int find_row(Error *error, const Datum &key) const; + + // Create a cursor to get records. + // + // Returns a pointer to the cursor on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + unique_ptr<Cursor> create_cursor( + Error *error, + const CursorOptions &options) const; + + // 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; + + // 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; + + // 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; + + // TODO: Grouping (drilldown). + // + // 分類器を作成する. + // 成功すれば有効なオブジェクトへのポインタを返す. + // 失敗したときは *error にその内容を格納し, nullptr を返す. + // + // 失敗する状況としては,以下のようなものが挙げられる. + // - オプションが不正である. + // - リソースが確保できない. +// virtual unique_ptr<Grouper> create_grouper( +// Error *error, +// Expression *expression, +// const GrouperOptions &options) const = 0; + + private: + DB *db_; + Name name_; + std::vector<unique_ptr<Column>> columns_; + Column *key_column_; + Int max_row_id_; + std::vector<uint64_t> bitmap_; + + // Create a new table. + // + // Returns a pointer to the table on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + static unique_ptr<Table> create(Error *error, + DB *db, + String name, + const TableOptions &options); + + Table(); + + // Read a bit from the bitmap. + // + // Assumes that the bitmap size is greater than "row_id". + bool get_bit(Int row_id) const { + return (bitmap_[row_id / 64] & (Int(1) << (row_id % 64))) != 0; + } + // Set a bit of the bitmap. + // + // Assumes that the bitmap size is greater than "row_id". + void set_bit(Int row_id) { + bitmap_[row_id / 64] |= Int(1) << (row_id % 64); + } + // Unset a bit of the bitmap. + // + // Assumes that the bitmap size is greater than "row_id". + void unset_bit(Int row_id) { + bitmap_[row_id / 64] &= ~(Int(1) << (row_id % 64)); + } + // Reverse a bit. + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool reserve_bit(Error *error, Int row_id); + + // Change the table name. + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool rename(Error *error, String new_name); + + // Return whether the table is removable or not. + bool is_removable(); + + // Find a column with its ID. + // + // Returns a pointer to the column on success. + // On failure, returns nullptr and stores error information into "*error" if + // "error" != nullptr. + Column *find_column_with_id(Error *error, + String name, + size_t *column_id) const; + + friend class DB; +}; + +} // namespace grnxx + +#endif // GRNXX_TABLE_HPP Added: include/grnxx/types.hpp (+304 -0) 100644 =================================================================== --- /dev/null +++ include/grnxx/types.hpp 2014-07-04 12:25:36 +0900 (7411b3b) @@ -0,0 +1,304 @@ +#ifndef GRNXX_TYPES_HPP +#define GRNXX_TYPES_HPP + +#include <cinttypes> +#include <cstring> +#include <memory> + +namespace grnxx { + +// Fixed-width signed integer types. +using std::int8_t; +using std::int16_t; +using std::int32_t; +using std::int64_t; + +// Fixed-width unsigned integer types. +using std::uint8_t; +using std::uint16_t; +using std::uint32_t; +using std::uint64_t; + +// Note that PRI*8/16/32/64 are available as format macro constants. +// For example, "%" PRIi64 is used to print a 64-bit signed integer. +// Also, "%" PRIu64 is used to print a 64-bit unsigned integer. + +// Integer type for representing offset and size. +using std::size_t; + +// Smart pointer type. +using std::unique_ptr; + +// An object to make a memory allocation (new) returns nullptr on failure. +using std::nothrow; + +// Reference to a byte string. +class String { + public: + // The default constructor does nothing. + String() = default; + + // Refer to a zero-terminated string. + String(const char *str) : data_(str), size_(str ? std::strlen(str) : 0) {} + + // Refer to an arbitrary byte string. + String(const char *data, size_t size) : data_(data), size_(size) {} + + const char &operator[](size_t i) const { + return data_[i]; + } + + const char *data() const { + return data_; + } + size_t size() const { + return size_; + } + + private: + const char *data_; + size_t size_; +}; + +inline bool operator==(String lhs, String rhs) { + return (lhs.size() == rhs.size()) && + (std::memcmp(lhs.data(), rhs.data(), lhs.size()) == 0); +} + +inline bool operator!=(String lhs, String rhs) { + return (lhs.size() != rhs.size()) || + (std::memcmp(lhs.data(), rhs.data(), lhs.size()) != 0); +} + +inline bool operator<(String lhs, String rhs) { + size_t min_size = lhs.size() < rhs.size() ? lhs.size() : rhs.size(); + int result = std::memcmp(lhs.data(), rhs.data(), min_size); + return (result < 0) || ((result == 0) && (lhs.size() < rhs.size())); +} + +inline bool operator>(String lhs, String rhs) { + size_t min_size = lhs.size() < rhs.size() ? lhs.size() : rhs.size(); + int result = std::memcmp(lhs.data(), rhs.data(), min_size); + return (result > 0) || ((result == 0) && (lhs.size() > rhs.size())); +} + +inline bool operator<=(String lhs, String rhs) { + size_t min_size = lhs.size() < rhs.size() ? lhs.size() : rhs.size(); + int result = std::memcmp(lhs.data(), rhs.data(), min_size); + return (result < 0) || ((result == 0) && (lhs.size() <= rhs.size())); +} + +inline bool operator>=(String lhs, String rhs) { + size_t min_size = lhs.size() < rhs.size() ? lhs.size() : rhs.size(); + int result = std::memcmp(lhs.data(), rhs.data(), min_size); + return (result > 0) || ((result == 0) && (lhs.size() >= rhs.size())); +} + +// Error information. +class Error; + +// Database object types. +class DB; +class Table; +class Column; +class Index; + +// Database option types. +struct DBOptions; +struct TableOptions; +struct ColumnOptions; +struct IndexOptions; + +// Database built-in data types. +enum DataType { + INVALID_DATA, + + // Type: Boolean. + // Value: True or false. + // Default: false. + BOOL_DATA, + // Type: Integer. + // Value: 64-bit signed integer. + // Default: 0. + INT_DATA, + // Type: Floating point number. + // Value: Double precision (64-bit) floating point number. + // Default: 0.0. + FLOAT_DATA, + // TODO + // Type: Time. + // Value: Microseconds since the Unix epoch (1970-01-01 00:00:00 UTC). + // Default: The Unix epoch. + TIME_DATA, + // TODO + // Type: GeoPoint. + // Value: Latitude-longitude in milliseconds. + // Default: (0, 0). + GEO_POINT_DATA, + // Type: String. + // Value: Byte string. + // Default: "". + TEXT_DATA, + // Type: Reference. + // Value: Reference to a row. + // Default: NULL. + ROW_REF_DATA, + + // TODO + // Type: Vector. + // Value: Vector of above data types. + // Default: {}. + BOOL_VECTOR_DATA, + INT_VECTOR_DATA, + FLOAT_VECTOR_DATA, + TIME_VECTOR_DATA, + GEO_POINT_VECTOR_DATA, + TEXT_VECTOR_DATA, + ROW_REF_VECTOR_DATA +}; + +using Bool = bool; + +using Int = int64_t; + +using Float = double; + +class Time { + public: + // The default constructor does nothing. + Time() = default; + + explicit Time(Int tick_count) : tick_count_(tick_count) {} + + void set_tick_count(Int tick_count) { + tick_count_ = tick_count; + } + + Int tick_count() const { + return tick_count_; + } + + private: + Int tick_count_; // Microseconds since the Unix epoch + // (1970-01-01 00:00:00 UTC). +}; + +inline bool operator==(Time lhs, Time rhs) { + return lhs.tick_count() == rhs.tick_count(); +} + +inline bool operator!=(Time lhs, Time rhs) { + return lhs.tick_count() != rhs.tick_count(); +} + +inline bool operator<(Time lhs, Time rhs) { + return lhs.tick_count() < rhs.tick_count(); +} + +inline bool operator>(Time lhs, Time rhs) { + return lhs.tick_count() > rhs.tick_count(); +} + +inline bool operator<=(Time lhs, Time rhs) { + return lhs.tick_count() <= rhs.tick_count(); +} + +inline bool operator>=(Time lhs, Time rhs) { + return lhs.tick_count() >= rhs.tick_count(); +} + +class GeoPoint { + public: + // The default constructor does nothing. + GeoPoint() = default; + + // The upper 32 bits of "latitude" and "longitude" are ignored. + GeoPoint(Int latitude, Int longitude) + : latitude_(static_cast<int32_t>(latitude)), + longitude_(static_cast<int32_t>(longitude)) {} + + // The upper 32 bits of "latitude" are ignored. + void set_latitude(Int latitude) { + latitude_ = static_cast<int32_t>(latitude); + } + + // The upper 32 bits of "longitude" are ignored. + void set_longitude(Int longitude) { + longitude_ = static_cast<int32_t>(longitude); + } + + Int latitude() const { + return latitude_; + } + Int longitude() const { + return longitude_; + } + + private: + int32_t latitude_; // Latitude in milliseconds. + int32_t longitude_; // Longitude in milliseconds. +}; + +inline bool operator==(GeoPoint lhs, GeoPoint rhs) { + return (lhs.latitude() == rhs.latitude()) && + (lhs.longitude() == rhs.longitude()); +} + +inline bool operator!=(GeoPoint lhs, GeoPoint rhs) { + return (lhs.latitude() != rhs.latitude()) || + (lhs.longitude() != rhs.longitude()); +} + +using Text = String; + +class RowRef { + public: + // The default constructor does nothing. + RowRef() = default; + + // Set the table and the row ID. + RowRef(Table *table, Int row_id) : table_(table), row_id_(row_id) {} + + // Set the table. + void set_table(Table *table) { + table_ = table; + } + // Set the row ID. + void set_row_id(Int row_id) { + row_id_ = row_id; + } + + // Return the table. + Table *table() const { + return table_; + } + // Return the row ID. + Int row_id() const { + return row_id_; + } + + private: + Table *table_; // Target table. + Int row_id_; // Row ID number. +}; + +// Zero is reserved for representing a null reference. +constexpr Int NULL_ROW_ID = 0; +constexpr Int MIN_ROW_ID = 1; +constexpr Int MAX_ROW_ID = (Int(1) << 40) - 1; + +// TODO +struct CursorOptions; + +class Datum; +class Cursor; +class RecordSet; + +enum IndexType { + TREE_INDEX, + HASH_INDEX +}; + +} // namespace grnxx + +#endif // GRNXX_TYPES_HPP Modified: lib/grnxx/Makefile.am (+5 -24) =================================================================== --- lib/grnxx/Makefile.am 2014-06-25 17:55:59 +0900 (592f915) +++ lib/grnxx/Makefile.am 2014-07-04 12:25:36 +0900 (6b87d95) @@ -9,37 +9,18 @@ lib_LTLIBRARIES = libgrnxx.la libgrnxx_la_LDFLAGS = @AM_LTLDFLAGS@ libgrnxx_la_SOURCES = \ - calc.cpp \ - calc_impl.cpp \ column.cpp \ - column_impl.cpp \ - database.cpp \ - datum.cpp \ + db.cpp \ + error.cpp \ index.cpp \ library.cpp \ - sorter.cpp \ - sorter_impl.cpp \ - string.cpp \ - table.cpp \ - timer.cpp \ - types.cpp + name.cpp \ + table.cpp libgrnxx_includedir = ${includedir}/grnxx libgrnxx_include_HEADERS = \ - calc.hpp \ - calc_impl.hpp \ - column.hpp \ column_impl.hpp \ - database.hpp \ - datum.hpp \ - index.hpp \ - library.hpp \ - sorter.hpp \ - sorter_impl.hpp \ - string.hpp \ - table.hpp \ - types.hpp \ - timer.hpp \ + name.hpp \ version.h EXTRA_DIST = version.sh Deleted: lib/grnxx/calc.cpp (+0 -16) 100644 =================================================================== --- lib/grnxx/calc.cpp 2014-06-25 17:55:59 +0900 (b55c497) +++ /dev/null @@ -1,16 +0,0 @@ -#include "grnxx/calc.hpp" - -#include "grnxx/calc_impl.hpp" - -namespace grnxx { - -// 演算器を作成する. -Calc *CalcHelper::create(const Table *table, const String &query) { - std::unique_ptr<CalcImpl> calc(new CalcImpl); - if (!calc->parse(table, query)) { - return nullptr; - } - return calc.release(); -} - -} // namespace grnxx Deleted: lib/grnxx/calc.hpp (+0 -30) 100644 =================================================================== --- lib/grnxx/calc.hpp 2014-06-25 17:55:59 +0900 (563e1b5) +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef GRNXX_CALC_HPP -#define GRNXX_CALC_HPP - -#include "grnxx/types.hpp" - -namespace grnxx { - -class Calc { - public: - // 演算器を初期化する. - Calc() {} - // 演算器を破棄する. - virtual ~Calc() {} - - // 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. - virtual Int64 filter(RowID *row_ids, Int64 num_row_ids) = 0; - - // 条件がひとつでもあれば true を返し,そうでなければ false を返す. - virtual bool empty() const = 0; -}; - -class CalcHelper { - public: - // 演算器を作成する. - static Calc *create(const Table *table, const String &query); -}; - -} // namespace grnxx - -#endif // GRNXX_CALC_HPP Deleted: lib/grnxx/calc_impl.cpp (+0 -1857) 100644 =================================================================== --- lib/grnxx/calc_impl.cpp 2014-06-25 17:55:59 +0900 (d4a4e54) +++ /dev/null @@ -1,1857 +0,0 @@ -#include "grnxx/calc_impl.hpp" - -#include "grnxx/column_impl.hpp" -#include "grnxx/table.hpp" - -namespace grnxx { -namespace { - -// 定数と対応するノード. -template <typename T> -class ConstantNode : public CalcNode { - public: - // 指定された定数と対応するノードとして初期化する. - explicit ConstantNode(T value) - : CalcNode(CONSTANT_NODE, TypeTraits<T>::data_type()), - value_(value) {} - // ノードを破棄する. - ~ConstantNode() {} - - // 指定された値を返す. - T get(Int64 i, RowID row_id) const { - return value_; - } - - private: - T value_; -}; - -// 真偽値と対応するノード. -template <> -class ConstantNode<Boolean> : public CalcNode { - public: - // 指定された定数と対応するノードとして初期化する. - explicit ConstantNode(Boolean value) - : CalcNode(CONSTANT_NODE, TypeTraits<Boolean>::data_type()), - value_(value) {} - // ノードを破棄する. - ~ConstantNode() {} - - // 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. - Int64 filter(RowID *row_ids, Int64 num_row_ids) { - // 真のときは何もせず,偽のときはクリアする. - return value_ ? num_row_ids : 0; - } - - // 指定された値を返す. - Boolean get(Int64 i, RowID row_id) const { - return value_; - } - - private: - Boolean value_; -}; - -// 文字列の定数と対応するノード. -template <> -class ConstantNode<String> : public CalcNode { - public: - // 指定された定数と対応するノードとして初期化する. - explicit ConstantNode(const String &value) - : CalcNode(CONSTANT_NODE, TypeTraits<String>::data_type()), - value_(), - buf_() { - buf_.assign(reinterpret_cast<const char *>(value.data()), value.size()); - value_ = buf_; - } - // ノードを破棄する. - ~ConstantNode() {} - - // 指定された値を返す. - String get(Int64 i, RowID row_id) const { - return value_; - } - - private: - String value_; - std::string buf_; -}; - -// カラムと対応するノード. -template <typename T> -class ColumnNode : public CalcNode { - public: - // 指定されたカラムに対応するノードとして初期化する. - explicit ColumnNode(Column *column) - : CalcNode(COLUMN_NODE, TypeTraits<T>::data_type()), - column_(static_cast<ColumnImpl<T> *>(column)) {} - // ノードを破棄する. - ~ColumnNode() {} - - ColumnImpl<T> *column() const { - return column_; - } - - // 指定された値を返す. - T get(Int64 i, RowID row_id) const { - return column_->get(row_id); - } - - private: - ColumnImpl<T> *column_; -}; - -// 真偽値のカラムと対応するノード. -template <> -class ColumnNode<Boolean> : public CalcNode { - public: - // 指定されたカラムに対応するノードとして初期化する. - explicit ColumnNode(Column *column) - : CalcNode(COLUMN_NODE, TypeTraits<Boolean>::data_type()), - column_(static_cast<ColumnImpl<Boolean> *>(column)) {} - // ノードを破棄する. - ~ColumnNode() {} - - // 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. - Int64 filter(RowID *row_ids, Int64 num_row_ids); - - // 指定された値を返す. - Boolean get(Int64 i, RowID row_id) const { - return column_->get(row_id); - } - - private: - ColumnImpl<Boolean> *column_; -}; - -// 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. -Int64 ColumnNode<Boolean>::filter(RowID *row_ids, Int64 num_row_ids) { - // 真になる行だけを残す. - Int64 count = 0; - for (Int64 i = 0; i < num_row_ids; ++i) { - RowID row_id = row_ids[i]; - if (column_->get(row_id)) { - row_ids[count++] = row_id; - } - } - return count; -} - -// 演算子と対応するノード. -template <typename T> -class OperatorNode : public CalcNode { - public: - // 指定された定数と対応するノードとして初期化する. - OperatorNode() - : CalcNode(OPERATOR_NODE, TypeTraits<T>::data_type()), - data_() {} - // ノードを破棄する. - virtual ~OperatorNode() {} - - // 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. - virtual Int64 filter(RowID *row_ids, Int64 num_row_ids) = 0; - - // 与えられた行の一覧について演算をおこない,その結果を取得できる状態にする. - virtual void fill(const RowID *row_ids, Int64 num_row_ids) = 0; - - // 指定された値を返す. - T get(Int64 i, RowID row_id) const { - return data_[i]; - } - - protected: - std::vector<T> data_; -}; - -// LOGICAL_NOT と対応するノード. -template <typename T> -class LogicalNotNode : public OperatorNode<Boolean> { - public: - // 指定されたノードに対する LOGICAL_NOT と対応するノードとして初期化する. - explicit LogicalNotNode(CalcNode *operand) - : OperatorNode<Boolean>(), - operand_(static_cast<T *>(operand)) {} - // ノードを破棄する. - ~LogicalNotNode() {} - - // 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. - Int64 filter(RowID *row_ids, Int64 num_row_ids); - - // 与えられた行の一覧について演算をおこない,その結果を取得できる状態にする. - void fill(const RowID *row_ids, Int64 num_row_ids); - - private: - T *operand_; -}; - -// 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. -template <typename T> -Int64 LogicalNotNode<T>::filter(RowID *row_ids, Int64 num_row_ids) { - // 真になる行だけを残す. - operand_->fill(row_ids, num_row_ids); - Int64 count = 0; - for (Int64 i = 0; i < num_row_ids; ++i) { - RowID row_id = row_ids[i]; - if (!operand_->get(i, row_id)) { - row_ids[count++] = row_id; - } - } - return count; -} - -// 与えられた行の一覧について演算をおこない,その結果を取得できる状態にする. -template <typename T> -void LogicalNotNode<T>::fill(const RowID *row_ids, Int64 num_row_ids) { - operand_->fill(row_ids, num_row_ids); - data_.resize(num_row_ids); - for (Int64 i = 0; i < num_row_ids; ++i) { - data_[i] = !operand_->get(i, row_ids[i]); - } -} - -// 比較演算子の実装. -template <typename T> using EqualOperator = std::equal_to<T>; -template <typename T> using NotEqualOperator = std::not_equal_to<T>; -template <typename T> using LessOperator = std::less<T>; -template <typename T> using LessEqualOperator = std::less_equal<T>; -template <typename T> using GreaterOperator = std::greater<T>; -template <typename T> using GreaterEqualOperator = std::greater_equal<T>; - -// 比較演算子と対応するノード. -template <typename T, typename U, typename V> -class ComparerNode : public OperatorNode<Boolean> { - public: - // 指定されたノードに対する比較演算子と対応するノードとして初期化する. - explicit ComparerNode(CalcNode *lhs, CalcNode *rhs) - : OperatorNode<Boolean>(), - comparer_(), - lhs_(static_cast<U *>(lhs)), - rhs_(static_cast<V *>(rhs)) {} - // ノードを破棄する. - ~ComparerNode() {} - - // 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. - Int64 filter(RowID *row_ids, Int64 num_row_ids); - - // 与えられた行の一覧について演算をおこない,その結果を取得できる状態にする. - void fill(const RowID *row_ids, Int64 num_row_ids); - - private: - T comparer_; - U *lhs_; - V *rhs_; -}; - -// 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. -template <typename T, typename U, typename V> -Int64 ComparerNode<T, U, V>::filter(RowID *row_ids, Int64 num_row_ids) { - // 真になる行だけを残す. - lhs_->fill(row_ids, num_row_ids); - rhs_->fill(row_ids, num_row_ids); - Int64 count = 0; - for (Int64 i = 0; i < num_row_ids; ++i) { - RowID row_id = row_ids[i]; - if (comparer_(lhs_->get(i, row_id), rhs_->get(i, row_id))) { - row_ids[count++] = row_id; - } - } - return count; -} - -// 与えられた行の一覧について演算をおこない,その結果を取得できる状態にする. -template <typename T, typename U, typename V> -void ComparerNode<T, U, V>::fill(const RowID *row_ids, Int64 num_row_ids) { - lhs_->fill(row_ids, num_row_ids); - rhs_->fill(row_ids, num_row_ids); - data_.resize(num_row_ids); - for (Int64 i = 0; i < num_row_ids; ++i) { - RowID row_id = row_ids[i]; - data_[i] = comparer_(lhs_->get(i, row_id), rhs_->get(i, row_id)); - } -} - -// LOGICAL_AND と対応するノード. -template <typename T, typename U> -class LogicalAndNode : public OperatorNode<Boolean> { - public: - // 指定されたノードに対する LOGICAL_AND と対応するノードとして初期化する. - explicit LogicalAndNode(CalcNode *lhs, CalcNode *rhs) - : OperatorNode<Boolean>(), - lhs_(static_cast<T *>(lhs)), - rhs_(static_cast<U *>(rhs)), - local_row_ids_() {} - // ノードを破棄する. - ~LogicalAndNode() {} - - // 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. - Int64 filter(RowID *row_ids, Int64 num_row_ids); - - // 与えられた行の一覧について演算をおこない,その結果を取得できる状態にする. - void fill(const RowID *row_ids, Int64 num_row_ids); - - private: - T *lhs_; - U *rhs_; - std::vector<RowID> local_row_ids_; -}; - -// 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. -template <typename T, typename U> -Int64 LogicalAndNode<T, U>::filter(RowID *row_ids, Int64 num_row_ids) { - num_row_ids = lhs_->filter(row_ids, num_row_ids); - num_row_ids = rhs_->filter(row_ids, num_row_ids); - return num_row_ids; -} - -// 与えられた行の一覧について演算をおこない,その結果を取得できる状態にする. -template <typename T, typename U> -void LogicalAndNode<T, U>::fill(const RowID *row_ids, Int64 num_row_ids) { - // 必要な領域を確保する. - data_.resize(num_row_ids); - local_row_ids_.resize(num_row_ids); - - lhs_->fill(row_ids, num_row_ids); - - // 左側の評価が真になる行のみを抜き出す. - Int64 count = 0; - for (Int64 i = 0; i < num_row_ids; ++i) { - RowID row_id = row_ids[i]; - if (lhs_->get(i, row_id)) { - local_row_ids_[count] = row_id; - ++count; - } - } - - rhs_->fill(&*local_row_ids_.begin(), count); - - // 左右ともに真になる行のみ真にする. - Int64 j = 0; - for (Int64 i = 0; i < num_row_ids; ++i) { - auto lhs_value = lhs_->get(i, row_ids[i]); - if (lhs_value) { - data_[i] = rhs_->get(j, row_ids[j]); - ++j; - } else { - data_[i] = lhs_value; - } - } -} - -// LOGICAL_OR と対応するノード. -template <typename T, typename U> -class LogicalOrNode : public OperatorNode<Boolean> { - public: - // 指定されたノードに対する LOGICAL_OR と対応するノードとして初期化する. - explicit LogicalOrNode(CalcNode *lhs, CalcNode *rhs) - : OperatorNode<Boolean>(), - lhs_(static_cast<T *>(lhs)), - rhs_(static_cast<U *>(rhs)), - local_row_ids_() {} - // ノードを破棄する. - ~LogicalOrNode() {} - - // 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. - Int64 filter(RowID *row_ids, Int64 num_row_ids); - - // 与えられた行の一覧について演算をおこない,その結果を取得できる状態にする. - void fill(const RowID *row_ids, Int64 num_row_ids); - - private: - T *lhs_; - U *rhs_; - std::vector<RowID> local_row_ids_; -}; - -// 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. -template <typename T, typename U> -Int64 LogicalOrNode<T, U>::filter(RowID *row_ids, Int64 num_row_ids) { - // 入力のコピーを作成する. - // 番兵を追加しても大丈夫なように,サイズは num_row_ids + 2 としている. - local_row_ids_.resize(num_row_ids + 2); - RowID *left_row_ids = &*local_row_ids_.begin(); - for (Int64 i = 0; i < num_row_ids; ++i) { - local_row_ids_[i] = row_ids[i]; - } - - // 入力のコピーを左のフィルタにかける. - Int64 num_left_row_ids = lhs_->filter(left_row_ids, num_row_ids); - if (num_left_row_ids == 0) { - // すべて偽なので,入力を丸ごと右のフィルタにかける. - return rhs_->filter(row_ids, num_row_ids); - } else if (num_left_row_ids == num_row_ids) { - // すべて真なので,何もしなくてよい. - return num_row_ids; - } - - // 番兵を配置する. - left_row_ids[num_left_row_ids] = 0; - - // 左のフィルタにかけて残らなかった行 ID を列挙する. - RowID *right_row_ids = left_row_ids + num_left_row_ids + 1; - Int64 left_count = 0; - Int64 right_count = 0; - for (Int64 i = 0; i < num_row_ids; ++i) { - if (row_ids[i] == left_row_ids[left_count]) { - ++left_count; - } else { - right_row_ids[right_count] = row_ids[i]; - ++right_count; - } - } - - // 右のフィルタにかける. - Int64 num_right_row_ids = rhs_->filter(right_row_ids, right_count); - if (num_right_row_ids == 0) { - // すべて偽なので,左のをそのまま返せばよい. - for (Int64 i = 0; i < num_left_row_ids; ++i) { - row_ids[i] = left_row_ids[i]; - } - return num_left_row_ids; - } else if (num_right_row_ids == right_count) { - // すべて真なので,何もしなくてよい. - return num_row_ids; - } - - // 番兵を配置する. - right_row_ids[num_right_row_ids] = 0; - - // 左右の結果をマージする(どちらかに含まれている行 ID を取り出す). - left_count = 0; - right_count = 0; - for (Int64 i = 0; i < num_row_ids; ++i) { - if (row_ids[i] == left_row_ids[left_count]) { - row_ids[left_count + right_count] = row_ids[i]; - ++left_count; - } else if (row_ids[i] == right_row_ids[right_count]) { - row_ids[left_count + right_count] = row_ids[i]; - ++right_count; - } - } - return left_count + right_count; -} - -// 与えられた行の一覧について演算をおこない,その結果を取得できる状態にする. -template <typename T, typename U> -void LogicalOrNode<T, U>::fill(const RowID *row_ids, Int64 num_row_ids) { - // 必要な領域を確保する. - data_.resize(num_row_ids); - local_row_ids_.resize(num_row_ids); - - lhs_->fill(row_ids, num_row_ids); - - // 左側の評価が偽になる行のみを抜き出す. - Int64 count = 0; - for (Int64 i = 0; i < num_row_ids; ++i) { - RowID row_id = row_ids[i]; - if (!lhs_->get(i, row_id)) { - local_row_ids_[count] = row_id; - ++count; - } - } - - rhs_->fill(&*local_row_ids_.begin(), count); - - // 左右ともに真になる行のみ真にする. - Int64 j = 0; - for (Int64 i = 0; i < num_row_ids; ++i) { - auto lhs_value = lhs_->get(i, row_ids[i]); - if (lhs_value) { - data_[i] = lhs_value; - } else { - data_[i] = rhs_->get(j, row_ids[j]); - ++j; - } - } -} - -// FIXME: 以下,同じような演算子を定義するのはマクロで簡略化できるし, -// そうした方が誤りを減らせそうである. - -// FIXME: オーバーフロー時の例外には専用の型を用意すべきである. - -// 無効な型. -struct InvalidResult {}; - -// 算術加算. -//Int64 plus_with_overflow_check(Int64 lhs, Int64 rhs) { -// Int64 result = lhs + rhs; -// lhs ^= result; -// rhs ^= result; -// if ((lhs & rhs) >> 63) { -// throw "overflow or underflow!"; -// } -// return result; -//} -#ifdef __clang__ -Int64 plus_with_overflow_check(Int64 lhs, Int64 rhs) { - UInt8 overflow_flag = 0; - __asm__ ("add %2, %0; seto %1;" - : "+r" (lhs), "=r" (overflow_flag) : "r" (rhs)); - if (overflow_flag) { - throw "overflow or underflow!"; - } - return lhs; -} -#else // __clang__ -Int64 plus_with_overflow_check(Int64 lhs, Int64 rhs) { - __asm__ volatile ("add %1, %0;" - : "+r" (lhs) : "r" (rhs)); - __asm__ goto ("jo %l0;" - : : : : PLUS_WITH_OVERFLOW_CHECK_LABEL); - return lhs; - - PLUS_WITH_OVERFLOW_CHECK_LABEL: - throw "overflow or underflow!"; -} -#endif // __clang__ -template <typename T, typename U> struct PlusOperator { - using Result = InvalidResult; - using Lhs = T; - using Rhs = U; -}; -template <> struct PlusOperator<Int64, Int64> { - using Result = Int64; - using Lhs = Int64; - using Rhs = Int64; - Result operator()(Int64 lhs, Int64 rhs) const { - return plus_with_overflow_check(lhs, rhs); - } -}; -template <> struct PlusOperator<Float, Float> { - using Result = Float; - using Lhs = Float; - using Rhs = Float; - Result operator()(Float lhs, Float rhs) const { return lhs + rhs; } -}; - -// 算術減算. -//Int64 minus_with_overflow_check(Int64 lhs, Int64 rhs) { -// Int64 result = lhs - rhs; -// rhs ^= lhs; -// lhs ^= result; -// if ((lhs & rhs) >> 63) { -// throw "overflow or underflow!"; -// } -// return result; -//} -#ifdef __clang__ -Int64 minus_with_overflow_check(Int64 lhs, Int64 rhs) { - UInt8 overflow_flag = 0; - __asm__ ("sub %2, %0; seto %1;" - : "+r" (lhs), "=r" (overflow_flag) : "r" (rhs)); - if (overflow_flag) { - throw "overflow or underflow!"; - } - return lhs; -} -#else // __clang__ -Int64 minus_with_overflow_check(Int64 lhs, Int64 rhs) { - __asm__ volatile ("sub %1, %0;" - : "+r" (lhs) : "r" (rhs)); - __asm__ goto ("jo %l0;" - : : : : MINUS_WITH_OVERFLOW_CHECK_LABEL); - return lhs; - - MINUS_WITH_OVERFLOW_CHECK_LABEL: - throw "overflow or underflow!"; -} -#endif // __clang__ -template <typename T, typename U> struct MinusOperator { - using Result = InvalidResult; - using Lhs = T; - using Rhs = U; -}; -template <> struct MinusOperator<Int64, Int64> { - using Result = Int64; - using Lhs = Int64; - using Rhs = Int64; - Result operator()(Int64 lhs, Int64 rhs) const { - return minus_with_overflow_check(lhs, rhs); - } -}; -template <> struct MinusOperator<Float, Float> { - using Result = Float; - using Lhs = Float; - using Rhs = Float; - Result operator()(Float lhs, Float rhs) const { return lhs - rhs; } -}; - -// 算術乗算. -//Int64 multiplies_with_overflow_check(Int64 lhs, Int64 rhs) { -// if (lhs == 0) { -// return 0; -// } -// Int64 result = lhs * rhs; -// if ((result / rhs) != lhs) { -// throw "overflow or underflow"; -// } -// return result; -//} -#ifdef __clang__ -Int64 multiplies_with_overflow_check(Int64 lhs, Int64 rhs) { - UInt8 overflow_flag = 0; - __asm__ ("imul %2, %0; seto %1;" - : "+r" (lhs), "=r" (overflow_flag) : "r" (rhs)); - if (overflow_flag) { - throw "overflow or underflow!"; - } - return lhs; -} -#else // __clang__ -Int64 multiplies_with_overflow_check(Int64 lhs, Int64 rhs) { - __asm__ volatile ("imul %1, %0;" - : "+r" (lhs) : "r" (rhs)); - __asm__ goto ("jo %l0;" - : : : : MULTIPLIES_WITH_OVERFLOW_CHECK_LABEL); - return lhs; - - MULTIPLIES_WITH_OVERFLOW_CHECK_LABEL: - throw "overflow or underflow!"; -} -#endif // __clang__ -template <typename T, typename U> struct MultipliesOperator { - using Result = InvalidResult; - using Lhs = T; - using Rhs = U; -}; -template <> struct MultipliesOperator<Int64, Int64> { - using Result = Int64; - using Lhs = Int64; - using Rhs = Int64; - Result operator()(Int64 lhs, Int64 rhs) const { - return multiplies_with_overflow_check(lhs, rhs); - } -}; -template <> struct MultipliesOperator<Float, Float> { - using Result = Float; - using Lhs = Float; - using Rhs = Float; - Result operator()(Float lhs, Float rhs) const { return lhs * rhs; } -}; - -// 算術除算. -Int64 divides_with_overflow_check(Int64 lhs, Int64 rhs) { - if (rhs == 0) { - throw "division by zero"; - } - if ((lhs == INT64_MIN) && (rhs == -1)) { - throw "overflow or underflow"; - } - return lhs / rhs; -} -template <typename T, typename U> struct DividesOperator { - using Result = InvalidResult; - using Lhs = T; - using Rhs = U; -}; -template <> struct DividesOperator<Int64, Int64> { - using Result = Int64; - using Lhs = Int64; - using Rhs = Int64; - Result operator()(Int64 lhs, Int64 rhs) const { - return divides_with_overflow_check(lhs, rhs); - } -}; -template <> struct DividesOperator<Float, Float> { - using Result = Float; - using Lhs = Float; - using Rhs = Float; - Result operator()(Float lhs, Float rhs) const { return lhs / rhs; } -}; - -// 算術剰余算. -Int64 modulus_with_overflow_check(Int64 lhs, Int64 rhs) { - if (rhs == 0) { - throw "division by zero"; - } - if ((lhs == INT64_MIN) && (rhs == -1)) { - throw "overflow or underflow"; - } - return lhs % rhs; -} -template <typename T, typename U> struct ModulusOperator { - using Result = InvalidResult; - using Lhs = T; - using Rhs = U; -}; -template <> struct ModulusOperator<Int64, Int64> { - using Result = Int64; - using Lhs = Int64; - using Rhs = Int64; - Result operator()(Int64 lhs, Int64 rhs) const { - return modulus_with_overflow_check(lhs, rhs); - } -}; - -// 四則演算と対応するノード. -template <typename T, typename U, typename V> -class ArithmeticNode : public OperatorNode<typename T::Result> { - public: - using Result = typename T::Result; - - // 指定されたノードに対する四則演算と対応するノードとして初期化する. - explicit ArithmeticNode(CalcNode *lhs, CalcNode *rhs) - : OperatorNode<Result>(), - operator_(), - lhs_(static_cast<U *>(lhs)), - rhs_(static_cast<V *>(rhs)) {} - // ノードを破棄する. - ~ArithmeticNode() {} - - // 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. - Int64 filter(RowID *row_ids, Int64 num_row_ids); - - // 与えられた行の一覧について演算をおこない,その結果を取得できる状態にする. - void fill(const RowID *row_ids, Int64 num_row_ids); - - private: - T operator_; - U *lhs_; - V *rhs_; -}; - -// 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. -template <typename T, typename U, typename V> -Int64 ArithmeticNode<T, U, V>::filter(RowID *row_ids, Int64 num_row_ids) { - // 真になる行だけを残す. - lhs_->fill(row_ids, num_row_ids); - rhs_->fill(row_ids, num_row_ids); - Int64 count = 0; - for (Int64 i = 0; i < num_row_ids; ++i) { - RowID row_id = row_ids[i]; - if (operator_(lhs_->get(i, row_ids[i]), rhs_->get(i, row_ids[i]))) { - row_ids[count++] = row_id; - } - } - return count; -} - -// 与えられた行の一覧について演算をおこない,その結果を取得できる状態にする. -template <typename T, typename U, typename V> -void ArithmeticNode<T, U, V>::fill(const RowID *row_ids, Int64 num_row_ids) { - lhs_->fill(row_ids, num_row_ids); - rhs_->fill(row_ids, num_row_ids); - this->data_.resize(num_row_ids); - for (Int64 i = 0; i < num_row_ids; ++i) { - this->data_[i] = operator_(lhs_->get(i, row_ids[i]), - rhs_->get(i, row_ids[i])); - } -} - -// 四則演算と対応するノードの作成を補助する. -template <typename T, typename U = typename T::Result> -class ArithmeticNodeHelper { - public: - template <typename V, typename W> - static CalcNode *create(CalcNode *lhs, CalcNode *rhs) { - return new ArithmeticNode<T, V, W>(lhs, rhs); - } - - static CalcNode *create(typename T::Lhs lhs, typename T::Rhs rhs) { - return new ConstantNode<typename T::Result>(T()(lhs, rhs)); - } -}; -template <typename T> -class ArithmeticNodeHelper<T, InvalidResult> { - public: - template <typename V, typename W> - static CalcNode *create(CalcNode *lhs, CalcNode *rhs) { - return nullptr; - } - - static CalcNode *create(typename T::Lhs lhs, typename T::Rhs rhs) { - return nullptr; - } -}; - -// 参照と対応するノード. -template <typename T, typename U> -class ReferenceNode : public OperatorNode<T> { - public: - using Result = T; - - // 指定されたノードに対する四則演算と対応するノードとして初期化する. - explicit ReferenceNode(CalcNode *lhs, CalcNode *rhs) - : OperatorNode<Result>(), - lhs_(static_cast<U *>(lhs)), - rhs_(static_cast<ColumnNode<T> *>(rhs)) {} - // ノードを破棄する. - ~ReferenceNode() {} - - // 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. - Int64 filter(RowID *row_ids, Int64 num_row_ids); - - // 与えられた行の一覧について演算をおこない,その結果を取得できる状態にする. - void fill(const RowID *row_ids, Int64 num_row_ids); - - private: - U *lhs_; - ColumnNode<T> *rhs_; - std::vector<RowID> local_row_ids_; -}; - -// 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. -template <typename T, typename U> -Int64 ReferenceNode<T, U>::filter(RowID *row_ids, Int64 num_row_ids) { - // 参照先が真になる行だけを残す. - lhs_->fill(row_ids, num_row_ids); - // FIXME: lhs が行 ID の一覧を持っているのにコピーしなければならない. - local_row_ids_.resize(num_row_ids); - for (Int64 i = 0; i < num_row_ids; ++i) { - local_row_ids_[i] = lhs_->get(i, row_ids[i]); - } - rhs_->fill(&*local_row_ids_.begin(), num_row_ids); - Int64 count = 0; - for (Int64 i = 0; i < num_row_ids; ++i) { - if (rhs_->get(i, local_row_ids_[i])) { - row_ids[count++] = row_ids[i]; - } - } - return count; -} - -// 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. -template <> -Int64 ReferenceNode<String, ColumnNode<Int64>>::filter( - RowID *row_ids, Int64 num_row_ids) { - // FIXME: String から Boolean の変換は未定義. - return 0; -} - -// 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. -template <> -Int64 ReferenceNode<String, OperatorNode<Int64>>::filter( - RowID *row_ids, Int64 num_row_ids) { - // FIXME: String から Boolean の変換は未定義. - return 0; -} - -// 与えられた行の一覧について演算をおこない,その結果を取得できる状態にする. -template <typename T, typename U> -void ReferenceNode<T, U>::fill(const RowID *row_ids, Int64 num_row_ids) { - lhs_->fill(row_ids, num_row_ids); - // FIXME: lhs が行 ID の一覧を持っているのにコピーしなければならない. - local_row_ids_.resize(num_row_ids); - for (Int64 i = 0; i < num_row_ids; ++i) { - local_row_ids_[i] = lhs_->get(i, row_ids[i]); - } - // FIXME: わざわざコピーしなければならない. - rhs_->fill(&*local_row_ids_.begin(), num_row_ids); - this->data_.resize(num_row_ids); - for (Int64 i = 0; i < num_row_ids; ++i) { - this->data_[i] = rhs_->get(i, local_row_ids_[i]); - } -} - -} // namespace - -// 指定された種類のノードとして初期化する. -CalcNode::CalcNode(CalcNodeType type, DataType data_type) - : type_(type), - data_type_(data_type) {} - -// ノードを破棄する. -CalcNode::~CalcNode() {} - -// 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. -Int64 CalcNode::filter(RowID *row_ids, Int64 num_row_ids) { - // すべて破棄する. - return 0; -} - -// 与えられた行の一覧について演算をおこない,その結果を取得できる状態にする. -void CalcNode::fill(const RowID *row_ids, Int64 num_row_ids) { - // 何もしない -} - -// 二項演算子の優先度を返す. -int CalcToken::get_binary_operator_priority(BinaryOperatorType operator_type) { - switch (operator_type) { - case EQUAL_OPERATOR: { - return 6; - } - case NOT_EQUAL_OPERATOR: { - return 6; - } - case LESS_OPERATOR: { - return 7; - } - case LESS_EQUAL_OPERATOR: { - return 7; - } - case GREATER_OPERATOR: { - return 7; - } - case GREATER_EQUAL_OPERATOR: { - return 7; - } - case LOGICAL_AND_OPERATOR: { - return 2; - } - case LOGICAL_OR_OPERATOR: { - return 1; - } - case PLUS_OPERATOR: { - return 8; - } - case MINUS_OPERATOR: { - return 8; - } - case MULTIPLIES_OPERATOR: { - return 9; - } - case DIVIDES_OPERATOR: { - return 9; - } - case MODULUS_OPERATOR: { - return 9; - } - case REFERENCE_OPERATOR: { - return 10; - } - } - return 0; -} - -CalcImpl::CalcImpl() - : Calc(), - table_(nullptr), - nodes_() {} - -CalcImpl::~CalcImpl() {} - -// 指定された文字列に対応する演算器を作成する. -bool CalcImpl::parse(const Table *table, const String &query) try { - if (!table) { - return false; - } - table_ = table; - nodes_.clear(); - - // 指定された文字列をトークンに分割する. - // 先頭と末尾に括弧を配置することで例外をなくす. - std::vector<CalcToken> tokens; - tokens.push_back(CalcToken(LEFT_BRACKET)); - if (!tokenize_query(query, &tokens)) { - return false; - } - tokens.push_back(CalcToken(RIGHT_BRACKET)); - - // トークンがひとつもないときは何もしない. - if (tokens.size() == 2) { - return true; - } - - // スタックを使って木を構築する. - std::vector<CalcToken> stack; - for (const auto &token : tokens) { - if (!push_token(token, &stack)) { - return false; - } - } - - // 最終的にはトークンがひとつだけ残らなければならない. - if (stack.size() != 1) { - return false; - } - return true; -} catch (const char *) { - // ゼロによる除算もしくはオーバーフローによる例外. - return false; -} - -// 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. -Int64 CalcImpl::filter(RowID *row_ids, Int64 num_row_ids) { - if (nodes_.empty()) { - return num_row_ids; - } - try { - return nodes_.back()->filter(row_ids, num_row_ids); - } catch (const char *) { - // ゼロによる除算もしくはオーバーフローによる例外. - return 0; - } -} - -// 演算が何も指定されていなければ true を返し,そうでなければ false を返す. -bool CalcImpl::empty() const { - return nodes_.empty(); -} - -// クエリをトークンに分割する. -bool CalcImpl::tokenize_query(const String &query, - std::vector<CalcToken> *tokens) { - String left = query; - while (!left.empty()) { - // 先頭の空白は無視する. - auto delim_pos = left.find_first_not_of(" \t\r\n"); - if (delim_pos == left.npos) { - break; - } - left = left.except_prefix(delim_pos); - switch (left[0]) { - case '!': { - if (left[1] == '=') { - tokens->push_back(CalcToken(NOT_EQUAL_OPERATOR)); - left = left.except_prefix(2); - } else { - tokens->push_back(CalcToken(LOGICAL_NOT_OPERATOR)); - left = left.except_prefix(1); - } - break; - } - case '=': { - if (left[1] == '=') { - tokens->push_back(CalcToken(EQUAL_OPERATOR)); - left = left.except_prefix(2); - } else { - return false; - } - break; - } - case '<': { - if (left[1] == '=') { - tokens->push_back(CalcToken(LESS_EQUAL_OPERATOR)); - left = left.except_prefix(2); - } else { - tokens->push_back(CalcToken(LESS_OPERATOR)); - left = left.except_prefix(1); - } - break; - } - case '>': { - if (left[1] == '=') { - tokens->push_back(CalcToken(GREATER_EQUAL_OPERATOR)); - left = left.except_prefix(2); - } else { - tokens->push_back(CalcToken(GREATER_OPERATOR)); - left = left.except_prefix(1); - } - break; - } - case '&': { - if (left[1] == '&') { - tokens->push_back(CalcToken(LOGICAL_AND_OPERATOR)); - left = left.except_prefix(2); - } else { - return false; - } - break; - } - case '|': { - if (left[1] == '|') { - tokens->push_back(CalcToken(LOGICAL_OR_OPERATOR)); - left = left.except_prefix(2); - } else { - return false; - } - break; - } - case '+': { - tokens->push_back(CalcToken(PLUS_OPERATOR)); - left = left.except_prefix(1); - break; - } - case '-': { - tokens->push_back(CalcToken(MINUS_OPERATOR)); - left = left.except_prefix(1); - break; - } - case '*': { - tokens->push_back(CalcToken(MULTIPLIES_OPERATOR)); - left = left.except_prefix(1); - break; - } - case '/': { - tokens->push_back(CalcToken(DIVIDES_OPERATOR)); - left = left.except_prefix(1); - break; - } - case '%': { - tokens->push_back(CalcToken(MODULUS_OPERATOR)); - left = left.except_prefix(1); - break; - } - case '(': { - tokens->push_back(CalcToken(LEFT_BRACKET)); - left = left.except_prefix(1); - break; - } - case ')': { - tokens->push_back(CalcToken(RIGHT_BRACKET)); - left = left.except_prefix(1); - break; - } - case '"': { - auto end = left.find_first_of('"', 1); - if (end == left.npos) { - // 文字列の終端がないときは失敗する. - return false; - } - // 文字列の定数に対応するノードを作成する. - String value = left.extract(1, end - 1); - auto node = create_string_node(value); - if (!node) { - return false; - } - tokens->push_back(CalcToken(node)); - left = left.except_prefix(end + 1); - break; - } - default: { - auto end = left.find_first_of(" \t\r\n!=<>&|+-*/%()"); - if (end == left.npos) { - end = left.size(); - } - // FIXME: アドホックに書き足したのでひどいことになっている. - // 最初に Boolean, Int64, Float の可能性を調べる. - String token = left.prefix(end); - CalcNode *node = nullptr; - if (token == "TRUE") { - node = create_boolean_node(true); - } else if (token == "FALSE") { - node = create_boolean_node(false); - } else if (std::isdigit(static_cast<UInt8>(token[0]))) { - if (token.find_first_of('.') != token.npos) { - node = create_float_node(static_cast<Float>(std::stod( - std::string(reinterpret_cast<const char *>(token.data()), - token.size())))); - } else { - node = create_int64_node(static_cast<Int64>(std::stol( - std::string(reinterpret_cast<const char *>(token.data()), - token.size())))); - } - } else { - // カラムと参照演算子に対応するノードを作成する. - // 参照演算子は単項演算子より優先順位が高いため,以降の処理における面倒を - // なくすべく,最後に作成したノードのみをトークン化する. - const Table *current_table = table_; - CalcNode *src_node = nullptr; - for ( ; ; ) { - auto delim_pos = token.find_first_of('.'); - auto column = current_table->get_column_by_name( - (delim_pos != token.npos) ? token.prefix(delim_pos) : token); - if (!column) { - return false; - } - node = create_column_node(column); - if (!node) { - return false; - } - if (src_node) { - node = create_binary_operator_node(REFERENCE_OPERATOR, - src_node, node); - if (!node) { - return false; - } - } - if (delim_pos == token.npos) { - // 参照演算子がなければここで終わる. - break; - } - - token = token.except_prefix(delim_pos + 1); - if (column->data_type() != INTEGER) { - return false; - } - src_node = node; - current_table = - static_cast<ColumnImpl<Int64> *>(column)->dest_table(); - if (!current_table) { - return false; - } - } - } - if (!node) { - return false; - } - tokens->push_back(CalcToken(node)); - left = left.except_prefix(end); - break; - } - } - } - return true; -} - -// トークンをひとつずつ解釈する. -bool CalcImpl::push_token(const CalcToken &token, - std::vector<CalcToken> *stack) { - switch (token.type()) { - case BRACKET_TOKEN: { - if (token.bracket_type() == LEFT_BRACKET) { - // 開き括弧の直前にノードがあるのはおかしい. - if (!stack->empty() && (stack->back().type() == NODE_TOKEN)) { - return false; - } - stack->push_back(token); - } else { - // 閉じ括弧の直前にはノードが必要であり,それより前に開き括弧が必要である. - if ((stack->size() < 2) || (stack->back().type() != NODE_TOKEN)) { - return false; - } - // 括弧内にある演算子をすべて解決する. - // 単項演算子は既に片付いているはずなので,二項演算子のみに注意すればよい. - while (stack->size() >= 3) { - CalcToken operator_token = (*stack)[stack->size() - 2]; - if (operator_token.type() != BINARY_OPERATOR_TOKEN) { - break; - } - CalcToken lhs_token = (*stack)[stack->size() - 3]; - CalcToken rhs_token = (*stack)[stack->size() - 1]; - stack->resize(stack->size() - 3); - - // 演算に対応するノードに置き換える. - auto node = create_binary_operator_node( - operator_token.binary_operator_type(), - lhs_token.node(), rhs_token.node()); - if (!node) { - return false; - } - if (!push_token(CalcToken(node), stack)) { - return false; - } - } - // 括弧を取り除き,中身だけを残す. - if ((stack->size() < 2) || - ((*stack)[stack->size() - 2].type() != BRACKET_TOKEN) || - ((*stack)[stack->size() - 2].bracket_type() != LEFT_BRACKET)) { - return false; - } - CalcToken content_token = stack->back(); - stack->resize(stack->size() - 2); - push_token(content_token, stack); - break; - } - break; - } - case NODE_TOKEN: { - if (stack->empty()) { - // 最初のトークンであれば追加する. - stack->push_back(token); - break; - } - // ノードが連続するのはおかしい. - if (stack->back().type() == NODE_TOKEN) { - return false; - } - // 直前が単項演算子でなければ末尾に追加する. - if (stack->back().type() != UNARY_OPERATOR_TOKEN) { - stack->push_back(token); - break; - } - // 直前の単項演算子を適用した結果に置き換える. - auto node = create_unary_operator_node( - LOGICAL_NOT_OPERATOR, token.node()); - if (!node) { - return false; - } - stack->pop_back(); - // FIXME: 単項演算子が大量に連続していると,再帰呼び出しが深くなりすぎて - // スタックオーバーフローになる. - if (!push_token(CalcToken(node), stack)) { - return false; - } - break; - } - case UNARY_OPERATOR_TOKEN: { - // 単項演算子の直前にオペランドがあるのはおかしい. - if (!stack->empty() && (stack->back().type() == NODE_TOKEN)) { - return false; - } - stack->push_back(token); - break; - } - case BINARY_OPERATOR_TOKEN: { - // 二項演算子の直前にはノードがなければならない. - if (stack->empty() || (stack->back().type() != NODE_TOKEN)) { - return false; - } - // 前方に優先度の高い二項演算子があるときは,それらを先に適用する. - while (stack->size() >= 3) { - CalcToken operator_token = (*stack)[stack->size() - 2]; - if ((operator_token.type() != BINARY_OPERATOR_TOKEN) || - (operator_token.priority() < token.priority())) { - break; - } - CalcToken lhs_token = (*stack)[stack->size() - 3]; - CalcToken rhs_token = (*stack)[stack->size() - 1]; - stack->resize(stack->size() - 3); - auto node = create_binary_operator_node( - operator_token.binary_operator_type(), - lhs_token.node(), rhs_token.node()); - if (!node) { - return false; - } - if (!push_token(CalcToken(node), stack)) { - return false; - } - } - stack->push_back(token); - break; - } - } - return true; -} - -// 指定された名前のカラムと対応するノードを作成する. -CalcNode *CalcImpl::create_column_node(Column *column) { - std::unique_ptr<CalcNode> node; - switch (column->data_type()) { - case BOOLEAN: { - node.reset(new ColumnNode<Boolean>(column)); - break; - } - case INTEGER: { - node.reset(new ColumnNode<Int64>(column)); - break; - } - case FLOAT: { - node.reset(new ColumnNode<Float>(column)); - break; - } - case STRING: { - node.reset(new ColumnNode<String>(column)); - break; - } - } - if (!node) { - return nullptr; - } - nodes_.push_back(std::move(node)); - return nodes_.back().get(); -} - -// 指定された Boolean の定数と対応するノードを作成する. -CalcNode *CalcImpl::create_boolean_node(Boolean value) { - std::unique_ptr<CalcNode> node(new ConstantNode<Boolean>(value)); - nodes_.push_back(std::move(node)); - return nodes_.back().get(); -} - -// 指定された Int64 の定数と対応するノードを作成する. -CalcNode *CalcImpl::create_int64_node(Int64 value) { - std::unique_ptr<CalcNode> node(new ConstantNode<Int64>(value)); - nodes_.push_back(std::move(node)); - return nodes_.back().get(); -} - -// 指定された Float の定数と対応するノードを作成する. -CalcNode *CalcImpl::create_float_node(Float value) { - std::unique_ptr<CalcNode> node(new ConstantNode<Float>(value)); - nodes_.push_back(std::move(node)); - return nodes_.back().get(); -} - -// 指定された String の定数と対応するノードを作成する. -CalcNode *CalcImpl::create_string_node(const String &value) { - std::unique_ptr<CalcNode> node(new ConstantNode<String>(value)); - nodes_.push_back(std::move(node)); - return nodes_.back().get(); -} - -// 指定された単項演算子と対応するノードを作成する. -CalcNode *CalcImpl::create_unary_operator_node( - UnaryOperatorType unary_operator_type, CalcNode *operand) { - std::unique_ptr<CalcNode> node; - switch (unary_operator_type) { - case LOGICAL_NOT_OPERATOR: { - node.reset(create_logical_not_operator_node(operand)); - break; - } - } - if (!node) { - return nullptr; - } - nodes_.push_back(std::move(node)); - return nodes_.back().get(); -} - -// LOGICAL_NOT と対応するノードを作成する. -CalcNode *CalcImpl::create_logical_not_operator_node(CalcNode *operand) { - if (operand->data_type() != BOOLEAN) { - return nullptr; - } - switch (operand->type()) { - case CONSTANT_NODE: { - auto value = !static_cast<ConstantNode<Boolean> *>(operand)->get(0, 0); - return new ConstantNode<Boolean>(value); - } - case COLUMN_NODE: { - return new LogicalNotNode<ColumnNode<Boolean>>(operand); - } - case OPERATOR_NODE: { - return new LogicalNotNode<OperatorNode<Boolean>>(operand); - } - } - return nullptr; -} - -// 指定された二項演算子と対応するノードを作成する. -CalcNode *CalcImpl::create_binary_operator_node( - BinaryOperatorType binary_operator_type, CalcNode *lhs, CalcNode *rhs) { - std::unique_ptr<CalcNode> node; - switch (binary_operator_type) { - case EQUAL_OPERATOR: - case NOT_EQUAL_OPERATOR: - case LESS_OPERATOR: - case LESS_EQUAL_OPERATOR: - case GREATER_OPERATOR: - case GREATER_EQUAL_OPERATOR: { - node.reset(create_comparer_node(binary_operator_type, lhs, rhs)); - break; - } - case LOGICAL_AND_OPERATOR: { - node.reset(create_logical_and_node(lhs, rhs)); - break; - } - case LOGICAL_OR_OPERATOR: { - node.reset(create_logical_or_node(lhs, rhs)); - break; - } - case PLUS_OPERATOR: - case MINUS_OPERATOR: - case MULTIPLIES_OPERATOR: - case DIVIDES_OPERATOR: - case MODULUS_OPERATOR: { - node.reset(create_arithmetic_node(binary_operator_type, lhs, rhs)); - break; - } - case REFERENCE_OPERATOR: { - node.reset(create_reference_node(lhs, rhs)); - break; - } - } - if (!node) { - return nullptr; - } - nodes_.push_back(std::move(node)); - return nodes_.back().get(); -} - -// 指定された比較演算子と対応するノードを作成する. -CalcNode *CalcImpl::create_comparer_node( - BinaryOperatorType binary_operator_type, CalcNode *lhs, CalcNode *rhs) { - // 同じ型同士と整数同士のみを許す. - switch (lhs->data_type()) { - case BOOLEAN: { - if (rhs->data_type() != BOOLEAN) { - return nullptr; - } - return create_comparer_node_2<Boolean, Boolean, Boolean>( - binary_operator_type, lhs, rhs); - } - case INTEGER: { - if (rhs->data_type() != INTEGER) { - return nullptr; - } - return create_comparer_node_2<Int64, Int64, Int64>( - binary_operator_type, lhs, rhs); - } - case FLOAT: { - if (rhs->data_type() != FLOAT) { - return nullptr; - } - return create_comparer_node_2<Float, Float, Float>( - binary_operator_type, lhs, rhs); - } - case STRING: { - if (rhs->data_type() != STRING) { - return nullptr; - } - return create_comparer_node_2<String, String, String>( - binary_operator_type, lhs, rhs); - } - } - return nullptr; -} - -// 指定された比較演算子と対応するノードを作成する. -// T = 比較に用いる型, U = lhs のデータ型, V = rhs のデータ型. -template <typename T, typename U, typename V> -CalcNode *CalcImpl::create_comparer_node_2( - BinaryOperatorType binary_operator_type, CalcNode *lhs, CalcNode *rhs) { - switch (binary_operator_type) { - case EQUAL_OPERATOR: { - return create_comparer_node_3<EqualOperator<T>, U, V>(lhs, rhs); - } - case NOT_EQUAL_OPERATOR: { - return create_comparer_node_3<NotEqualOperator<T>, U, V>(lhs, rhs); - } - case LESS_OPERATOR: { - return create_comparer_node_3<LessOperator<T>, U, V>(lhs, rhs); - } - case LESS_EQUAL_OPERATOR: { - return create_comparer_node_3<LessEqualOperator<T>, U, V>(lhs, rhs); - } - case GREATER_OPERATOR: { - return create_comparer_node_3<GreaterOperator<T>, U, V>(lhs, rhs); - } - case GREATER_EQUAL_OPERATOR: { - return create_comparer_node_3<GreaterEqualOperator<T>, U, V>(lhs, rhs); - } - default: { - return nullptr; - } - } -} - -// 指定された比較演算子と対応するノードを作成する. -// T = 比較演算子, U = lhs のデータ型, V = rhs のデータ型. -template <typename T, typename U, typename V> -CalcNode *CalcImpl::create_comparer_node_3( - CalcNode *lhs, CalcNode *rhs) { - switch (lhs->type()) { - case CONSTANT_NODE: { - switch (rhs->type()) { - case CONSTANT_NODE: { - auto lhs_value = static_cast<ConstantNode<U> *>(lhs)->get(0, 0); - auto rhs_value = static_cast<ConstantNode<V> *>(rhs)->get(0, 0); - return new ConstantNode<Boolean>(T()(lhs_value, rhs_value)); - } - case COLUMN_NODE: { - return new ComparerNode<T, ConstantNode<U>, ColumnNode<V>>( - lhs, rhs); - } - case OPERATOR_NODE: { - return new ComparerNode<T, ConstantNode<U>, OperatorNode<V>>( - lhs, rhs); - } - } - break; - } - case COLUMN_NODE: { - switch (rhs->type()) { - case CONSTANT_NODE: { - return new ComparerNode<T, ColumnNode<U>, ConstantNode<V>>( - lhs, rhs); - } - case COLUMN_NODE: { - return new ComparerNode<T, ColumnNode<U>, ColumnNode<V>>( - lhs, rhs); - } - case OPERATOR_NODE: { - return new ComparerNode<T, ColumnNode<U>, OperatorNode<V>>( - lhs, rhs); - } - } - break; - } - case OPERATOR_NODE: { - switch (rhs->type()) { - case CONSTANT_NODE: { - return new ComparerNode<T, OperatorNode<U>, ConstantNode<V>>( - lhs, rhs); - } - case COLUMN_NODE: { - return new ComparerNode<T, OperatorNode<U>, ColumnNode<V>>( - lhs, rhs); - } - case OPERATOR_NODE: { - return new ComparerNode<T, OperatorNode<U>, OperatorNode<V>>( - lhs, rhs); - } - } - } - } - return nullptr; -} - -// LOGICAL_AND と対応するノードを作成する. -CalcNode *CalcImpl::create_logical_and_node(CalcNode *lhs, CalcNode *rhs) { - if ((lhs->data_type() != BOOLEAN) || (rhs->data_type() != BOOLEAN)) { - return nullptr; - } - using ConstantNodeB = ConstantNode<Boolean>; - using ColumnNodeB = ColumnNode<Boolean>; - using OperatorNodeB = OperatorNode<Boolean>; - switch (lhs->type()) { - case CONSTANT_NODE: { - switch (rhs->type()) { - case CONSTANT_NODE: { - auto lhs_value = static_cast<ConstantNodeB *>(lhs)->get(0, 0); - auto rhs_value = static_cast<ConstantNodeB *>(rhs)->get(0, 0); - return new ConstantNode<Boolean>(lhs_value && rhs_value); - } - case COLUMN_NODE: { - return new LogicalAndNode<ConstantNodeB, ColumnNodeB>(lhs, rhs); - } - case OPERATOR_NODE: { - return new LogicalAndNode<ConstantNodeB, OperatorNodeB>(lhs, rhs); - } - } - break; - } - case COLUMN_NODE: { - switch (rhs->type()) { - case CONSTANT_NODE: { - return new LogicalAndNode<ColumnNodeB, ConstantNodeB>(lhs, rhs); - } - case COLUMN_NODE: { - return new LogicalAndNode<ColumnNodeB, ColumnNodeB>(lhs, rhs); - } - case OPERATOR_NODE: { - return new LogicalAndNode<ColumnNodeB, OperatorNodeB>(lhs, rhs); - } - } - break; - } - case OPERATOR_NODE: { - switch (rhs->type()) { - case CONSTANT_NODE: { - return new LogicalAndNode<OperatorNodeB, ConstantNodeB>(lhs, rhs); - } - case COLUMN_NODE: { - return new LogicalAndNode<OperatorNodeB, ColumnNodeB>(lhs, rhs); - } - case OPERATOR_NODE: { - return new LogicalAndNode<OperatorNodeB, OperatorNodeB>(lhs, rhs); - } - } - } - } - return nullptr; -} - -// LOGICAL_OR と対応するノードを作成する. -CalcNode *CalcImpl::create_logical_or_node(CalcNode *lhs, CalcNode *rhs) { - if ((lhs->data_type() != BOOLEAN) || (rhs->data_type() != BOOLEAN)) { - return nullptr; - } - using ConstantNodeB = ConstantNode<Boolean>; - using ColumnNodeB = ColumnNode<Boolean>; - using OperatorNodeB = OperatorNode<Boolean>; - switch (lhs->type()) { - case CONSTANT_NODE: { - switch (rhs->type()) { - case CONSTANT_NODE: { - auto lhs_value = static_cast<ConstantNodeB *>(lhs)->get(0, 0); - auto rhs_value = static_cast<ConstantNodeB *>(rhs)->get(0, 0); - return new ConstantNode<Boolean>(lhs_value || rhs_value); - } - case COLUMN_NODE: { - return new LogicalOrNode<ConstantNodeB, ColumnNodeB>(lhs, rhs); - } - case OPERATOR_NODE: { - return new LogicalOrNode<ConstantNodeB, OperatorNodeB>(lhs, rhs); - } - } - break; - } - case COLUMN_NODE: { - switch (rhs->type()) { - case CONSTANT_NODE: { - return new LogicalOrNode<ColumnNodeB, ConstantNodeB>(lhs, rhs); - } - case COLUMN_NODE: { - return new LogicalOrNode<ColumnNodeB, ColumnNodeB>(lhs, rhs); - } - case OPERATOR_NODE: { - return new LogicalOrNode<ColumnNodeB, OperatorNodeB>(lhs, rhs); - } - } - break; - } - case OPERATOR_NODE: { - switch (rhs->type()) { - case CONSTANT_NODE: { - return new LogicalOrNode<OperatorNodeB, ConstantNodeB>(lhs, rhs); - } - case COLUMN_NODE: { - return new LogicalOrNode<OperatorNodeB, ColumnNodeB>(lhs, rhs); - } - case OPERATOR_NODE: { - return new LogicalOrNode<OperatorNodeB, OperatorNodeB>(lhs, rhs); - } - } - } - } - return nullptr; -} - -// 指定された算術演算子と対応するノードを作成する. -CalcNode *CalcImpl::create_arithmetic_node( - BinaryOperatorType binary_operator_type, CalcNode *lhs, CalcNode *rhs) { - // 整数同士およびに浮動小数点数同士のみを許す. - switch (lhs->data_type()) { - case BOOLEAN: { - return create_arithmetic_node_2<Boolean>(binary_operator_type, lhs, rhs); - } - case INTEGER: { - return create_arithmetic_node_2<Int64>(binary_operator_type, lhs, rhs); - } - case FLOAT: { - return create_arithmetic_node_2<Float>(binary_operator_type, lhs, rhs); - } - case STRING: { - return create_arithmetic_node_2<String>(binary_operator_type, lhs, rhs); - } - } - return nullptr; -} - -// 指定された算術演算子と対応するノードを作成する. -// T: lhs のデータ型. -template <typename T> -CalcNode *CalcImpl::create_arithmetic_node_2( - BinaryOperatorType binary_operator_type, CalcNode *lhs, CalcNode *rhs) { - // 整数同士およびに浮動小数点数同士のみを許す. - switch (rhs->data_type()) { - case BOOLEAN: { - return create_arithmetic_node_3<T, Boolean>( - binary_operator_type, lhs, rhs); - } - case INTEGER: { - return create_arithmetic_node_3<T, Int64>( - binary_operator_type, lhs, rhs); - } - case FLOAT: { - return create_arithmetic_node_3<T, Float>( - binary_operator_type, lhs, rhs); - } - case STRING: { - return create_arithmetic_node_3<T, String>( - binary_operator_type, lhs, rhs); - } - } - return nullptr; -} - -// 指定された算術演算子と対応するノードを作成する. -// T: lhs のデータ型, U: rhs のデータ型. -template <typename T, typename U> -CalcNode *CalcImpl::create_arithmetic_node_3( - BinaryOperatorType binary_operator_type, CalcNode *lhs, CalcNode *rhs) { - switch (binary_operator_type) { - case PLUS_OPERATOR: { - return create_arithmetic_node_4<PlusOperator<T, U>>(lhs, rhs); - } - case MINUS_OPERATOR: { - return create_arithmetic_node_4<MinusOperator<T, U>>(lhs, rhs); - } - case MULTIPLIES_OPERATOR: { - return create_arithmetic_node_4<MultipliesOperator<T, U>>(lhs, rhs); - } - case DIVIDES_OPERATOR: { - return create_arithmetic_node_4<DividesOperator<T, U>>(lhs, rhs); - } - case MODULUS_OPERATOR: { - return create_arithmetic_node_4<ModulusOperator<T, U>>(lhs, rhs); - } - default: { - return nullptr; - } - } -} - -// 指定された算術演算子と対応するノードを作成する. -// T: 算術演算子. -template <typename T> -CalcNode *CalcImpl::create_arithmetic_node_4(CalcNode *lhs, CalcNode *rhs) { -// using Result = typename T::Result; - using Lhs = typename T::Lhs; - using Rhs = typename T::Rhs; - switch (lhs->type()) { - case CONSTANT_NODE: { - switch (rhs->type()) { - case CONSTANT_NODE: { - auto lhs_value = static_cast<ConstantNode<Lhs> *>(lhs)->get(0, 0); - auto rhs_value = static_cast<ConstantNode<Rhs> *>(rhs)->get(0, 0); - return ArithmeticNodeHelper<T>::template create( - lhs_value, rhs_value); - } - case COLUMN_NODE: { - return ArithmeticNodeHelper<T>::template create< - ConstantNode<Lhs>, ColumnNode<Rhs>>(lhs, rhs); - } - case OPERATOR_NODE: { - return ArithmeticNodeHelper<T>::template create< - ConstantNode<Lhs>, OperatorNode<Rhs>>(lhs, rhs); - } - } - break; - } - case COLUMN_NODE: { - switch (rhs->type()) { - case CONSTANT_NODE: { - return ArithmeticNodeHelper<T>::template create< - ColumnNode<Lhs>, ConstantNode<Rhs>>(lhs, rhs); - } - case COLUMN_NODE: { - return ArithmeticNodeHelper<T>::template create< - ColumnNode<Lhs>, ColumnNode<Rhs>>(lhs, rhs); - } - case OPERATOR_NODE: { - return ArithmeticNodeHelper<T>::template create< - ColumnNode<Lhs>, OperatorNode<Rhs>>(lhs, rhs); - } - } - break; - } - case OPERATOR_NODE: { - switch (rhs->type()) { - case CONSTANT_NODE: { - return ArithmeticNodeHelper<T>::template create< - OperatorNode<Lhs>, ConstantNode<Rhs>>(lhs, rhs); - } - case COLUMN_NODE: { - return ArithmeticNodeHelper<T>::template create< - OperatorNode<Lhs>, ColumnNode<Rhs>>(lhs, rhs); - } - case OPERATOR_NODE: { - return ArithmeticNodeHelper<T>::template create< - OperatorNode<Lhs>, OperatorNode<Rhs>>(lhs, rhs); - } - } - } - } - return nullptr; -} - -// 参照演算子と対応するノードを作成する. -CalcNode *CalcImpl::create_reference_node(CalcNode *lhs, CalcNode *rhs) { - // 左の被演算子は参照型のカラムもしくは演算子でなければならない. - // FIXME: 左の被演算子が参照型かどうかは呼び出し側で確認している. - if ((lhs->data_type() != INTEGER) || - ((lhs->type() != COLUMN_NODE) && (lhs->type() != OPERATOR_NODE))) { - return nullptr; - } - // 右の被演算子もカラムでなければならない. - if (rhs->type() != COLUMN_NODE) { - return nullptr; - } - // 右の被演算子の型に応じたノードを作成する. - switch (rhs->data_type()) { - case BOOLEAN: { - if (lhs->type() == COLUMN_NODE) { - return new ReferenceNode<Boolean, ColumnNode<Int64>>(lhs, rhs); - } else { - return new ReferenceNode<Boolean, OperatorNode<Int64>>(lhs, rhs); - } - } - case INTEGER: { - if (lhs->type() == COLUMN_NODE) { - return new ReferenceNode<Int64, ColumnNode<Int64>>(lhs, rhs); - } else { - return new ReferenceNode<Int64, OperatorNode<Int64>>(lhs, rhs); - } - } - case FLOAT: { - if (lhs->type() == COLUMN_NODE) { - return new ReferenceNode<Float, ColumnNode<Int64>>(lhs, rhs); - } else { - return new ReferenceNode<Float, OperatorNode<Int64>>(lhs, rhs); - } - } - case STRING: { - if (lhs->type() == COLUMN_NODE) { - return new ReferenceNode<String, ColumnNode<Int64>>(lhs, rhs); - } else { - return new ReferenceNode<String, OperatorNode<Int64>>(lhs, rhs); - } - } - } - return nullptr; -} - -} // namespace grnxx Deleted: lib/grnxx/calc_impl.hpp (+0 -237) 100644 =================================================================== --- lib/grnxx/calc_impl.hpp 2014-06-25 17:55:59 +0900 (80390ae) +++ /dev/null @@ -1,237 +0,0 @@ -#ifndef GRNXX_CALC_IMPL_HPP -#define GRNXX_CALC_IMPL_HPP - -#include "grnxx/calc.hpp" - -namespace grnxx { - -// 単項演算子の種類. -enum UnaryOperatorType { - LOGICAL_NOT_OPERATOR -}; - -// 二項演算子の種類. -enum BinaryOperatorType { - EQUAL_OPERATOR, - NOT_EQUAL_OPERATOR, - LESS_OPERATOR, - LESS_EQUAL_OPERATOR, - GREATER_OPERATOR, - GREATER_EQUAL_OPERATOR, - LOGICAL_AND_OPERATOR, - LOGICAL_OR_OPERATOR, - PLUS_OPERATOR, - MINUS_OPERATOR, - MULTIPLIES_OPERATOR, - DIVIDES_OPERATOR, - MODULUS_OPERATOR, - REFERENCE_OPERATOR -}; - -// 演算器を構成するノードの種類. -enum CalcNodeType { - CONSTANT_NODE, - COLUMN_NODE, - OPERATOR_NODE -}; - -// 演算器を構成するノード. -class CalcNode { - public: - // 指定された種類のノードとして初期化する. - CalcNode(CalcNodeType type, DataType data_type); - // ノードを破棄する. - virtual ~CalcNode(); - - // ノードの種類を返す. - CalcNodeType type() const { - return type_; - } - // データ型を返す. - DataType data_type() const { - return data_type_; - } - - // 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. - virtual Int64 filter(RowID *row_ids, Int64 num_row_ids); - - // 与えられた行の一覧について演算をおこない,その結果を取得できる状態にする. - virtual void fill(const RowID *row_ids, Int64 num_row_ids); - - protected: - CalcNodeType type_; - DataType data_type_; -}; - -// 括弧の種類. -enum CalcBracketType { - LEFT_BRACKET, - RIGHT_BRACKET -}; - -// クエリを構成するトークンの種類. -enum CalcTokenType { - BRACKET_TOKEN, - NODE_TOKEN, - UNARY_OPERATOR_TOKEN, - BINARY_OPERATOR_TOKEN -}; - -// クエリを構成するトークン. -class CalcToken { - public: - // std::vector<CalcToken>::resize() を使えるようにする. - CalcToken() - : type_(NODE_TOKEN), - node_(nullptr), - priority_(0) {} - // 括弧に対応するトークンを作成する. - explicit CalcToken(CalcBracketType bracket_type) - : type_(BRACKET_TOKEN), - bracket_type_(bracket_type), - priority_(0) {} - // ノードに対応するトークンを作成する. - explicit CalcToken(CalcNode *node) - : type_(NODE_TOKEN), - node_(node), - priority_(0) {} - // 単項演算子に対応するトークンを作成する. - explicit CalcToken(UnaryOperatorType unary_operator_type) - : type_(UNARY_OPERATOR_TOKEN), - unary_operator_type_(unary_operator_type), - priority_(0) {} - // 二項演算子に対応するトークンを作成する. - explicit CalcToken(BinaryOperatorType binary_operator_type) - : type_(BINARY_OPERATOR_TOKEN), - binary_operator_type_(binary_operator_type), - priority_(get_binary_operator_priority(binary_operator_type)) {} - - // トークンの種類を返す. - CalcTokenType type() const { - return type_; - } - // 対応する括弧の種類を返す. - CalcBracketType bracket_type() const { - return bracket_type_; - } - // 対応するノードを返す. - CalcNode *node() const { - return node_; - } - // 対応する単項演算子の種類を返す. - UnaryOperatorType unary_operator_type() const { - return unary_operator_type_; - } - // 対応する二項演算子の種類を返す. - BinaryOperatorType binary_operator_type() const { - return binary_operator_type_; - } - // 対応する二項演算子の優先度を返す. - int priority() const { - return priority_; - } - - private: - CalcTokenType type_; - union { - CalcBracketType bracket_type_; - CalcNode *node_; - UnaryOperatorType unary_operator_type_; - BinaryOperatorType binary_operator_type_; - }; - int priority_; - - // 二項演算子の優先度を返す. - static int get_binary_operator_priority(BinaryOperatorType operator_type); -}; - -class CalcImpl : public Calc { - public: - CalcImpl(); - ~CalcImpl(); - - // 指定された文字列に対応する演算器を作成する. - bool parse(const Table *table, const String &query); - - // 行の一覧を受け取り,演算結果が真になる行のみを残して,残った行の数を返す. - Int64 filter(RowID *row_ids, Int64 num_row_ids); - - // 演算が何も指定されていなければ true を返し,そうでなければ false を返す. - bool empty() const; - - private: - const Table *table_; - std::vector<std::unique_ptr<CalcNode>> nodes_; - - // クエリをトークンに分割する. - bool tokenize_query(const String &query, std::vector<CalcToken> *tokens); - // トークンをひとつずつ解釈する. - bool push_token(const CalcToken &token, std::vector<CalcToken> *stack); - - // 指定されたカラムと対応するノードを作成する. - CalcNode *create_column_node(Column *column); - - // 指定された Boolean の定数と対応するノードを作成する. - CalcNode *create_boolean_node(Boolean value); - // 指定された Int64 の定数と対応するノードを作成する. - CalcNode *create_int64_node(Int64 value); - // 指定された Float の定数と対応するノードを作成する. - CalcNode *create_float_node(Float value); - // 指定された String の定数と対応するノードを作成する. - CalcNode *create_string_node(const String &value); - - // 指定された単項演算子と対応するノードを作成する. - CalcNode *create_unary_operator_node(UnaryOperatorType unary_operator_type, - CalcNode *operand); - - // LOGICAL_NOT と対応するノードを作成する. - CalcNode *create_logical_not_operator_node(CalcNode *operand); - - // 指定された二項演算子と対応するノードを作成する. - CalcNode *create_binary_operator_node( - BinaryOperatorType binary_operator_type, CalcNode *lhs, CalcNode *rhs); - - // 指定された比較演算子と対応するノードを作成する. - CalcNode *create_comparer_node( - BinaryOperatorType binary_operator_type, CalcNode *lhs, CalcNode *rhs); - // 指定された比較演算子と対応するノードを作成する. - // T = 比較に用いる型, U = lhs のデータ型, V = rhs のデータ型. - template <typename T, typename U, typename V> - CalcNode *create_comparer_node_2( - BinaryOperatorType binary_operator_type, CalcNode *lhs, CalcNode *rhs); - // 指定された比較演算子と対応するノードを作成する. - // T = 比較演算子, U = lhs のデータ型, V = rhs のデータ型. - template <typename T, typename U, typename V> - CalcNode *create_comparer_node_3(CalcNode *lhs, CalcNode *rhs); - - // LOGICAL_AND と対応するノードを作成する. - CalcNode *create_logical_and_node(CalcNode *lhs, CalcNode *rhs); - - // LOGICAL_OR と対応するノードを作成する. - CalcNode *create_logical_or_node(CalcNode *lhs, CalcNode *rhs); - - // 指定された算術演算子と対応するノードを作成する. - CalcNode *create_arithmetic_node( - BinaryOperatorType binary_operator_type, CalcNode *lhs, CalcNode *rhs); - // 指定された算術演算子と対応するノードを作成する. - // T: lhs のデータ型. - template <typename T> - CalcNode *create_arithmetic_node_2( - BinaryOperatorType binary_operator_type, CalcNode *lhs, CalcNode *rhs); - // 指定された算術演算子と対応するノードを作成する. - // T: lhs のデータ型, U: rhs のデータ型. - template <typename T, typename U> - CalcNode *create_arithmetic_node_3( - BinaryOperatorType binary_operator_type, CalcNode *lhs, CalcNode *rhs); - // 指定された算術演算子と対応するノードを作成する. - // T: 算術演算子. - template <typename T> - CalcNode *create_arithmetic_node_4(CalcNode *lhs, CalcNode *rhs); - - // 参照演算子と対応するノードを作成する. - CalcNode *create_reference_node(CalcNode *lhs, CalcNode *rhs); -}; - -} // namespace grnxx - -#endif // GRNXX_CALC_IMPL_HPP Modified: lib/grnxx/column.cpp (+182 -108) =================================================================== --- lib/grnxx/column.cpp 2014-06-25 17:55:59 +0900 (bb062b6) +++ lib/grnxx/column.cpp 2014-07-04 12:25:36 +0900 (8bd9ac9) @@ -1,141 +1,215 @@ #include "grnxx/column.hpp" #include "grnxx/column_impl.hpp" - -#include <ostream> +#include "grnxx/cursor.hpp" +#include "grnxx/datum.hpp" +#include "grnxx/error.hpp" +#include "grnxx/table.hpp" namespace grnxx { -// カラムを初期化する. -Column::Column(Table *table, ColumnID id, const String &name, - DataType data_type) - : table_(table), - id_(id), - name_(reinterpret_cast<const char *>(name.data()), name.size()), - data_type_(data_type), - is_unique_(false) {} - -// カラムを破棄する. Column::~Column() {} -// UNIQUE 制約を解除する. -bool Column::unset_unique() { - if (!is_unique_) { +Index *Column::create_index(Error *error, + String name, + IndexType index_type, + const IndexOptions &index_options) { + // TODO: Index is not supported yet. + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not suported yet"); + return nullptr; +} + +bool Column::remove_index(Error *error, String name) { + // TODO: Index is not supported yet. + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not suported yet"); + return false; +} + +bool Column::rename_index(Error *error, + String name, + String new_name) { + // TODO: Index is not supported yet. + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not suported yet"); + return false; +} + +bool Column::reorder_index(Error *error, + String name, + String prev_name) { + // TODO: Index is not supported yet. + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not suported yet"); + return false; +} + +Index *Column::get_index(Error *error, size_t index_id) const { + // TODO: Index is not supported yet. + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not suported yet"); + return nullptr; +} + +Index *Column::find_index(Error *error, String name) const { + // TODO: Index is not supported yet. + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not suported yet"); + return nullptr; +} + +bool Column::set(Error *error, Int row_id, const Datum &datum) { + // TODO: Index is not supported yet. + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not suported yet"); + return false; +} + +bool Column::get(Error *error, Int row_id, Datum *datum) const { + // TODO: Index is not supported yet. + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not suported yet"); + return false; +} + +unique_ptr<Cursor> Column::create_cursor( + Error *error, + const CursorOptions &options) const { + // TODO: Cursor is not supported yet. + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not suported yet"); + return nullptr; +} + +unique_ptr<Column> Column::create(Error *error, + Table *table, + String name, + DataType data_type, + const ColumnOptions &options) { + switch (data_type) { + case BOOL_DATA: { + return BoolColumn::create(error, table, name, options); + } +// case INT_DATA: { +// return ColumnImpl<Int>::create(error, table, name, options); +// } +// case FLOAT_DATA: { +// return ColumnImpl<Float>::create(error, table, name, options); +// } +// case TIME_DATA: { +// return ColumnImpl<Time>::create(error, table, name, options); +// } +// case GEO_POINT_DATA: { +// return ColumnImpl<GeoPoint>::create(error, table, name, options); +// } +// case TEXT_DATA: { +// return ColumnImpl<Text>::create(error, table, name, options); +// } + default: { + // TODO: Other data types are not supported yet. + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not suported yet"); + return nullptr; + } + } +} + +Column::Column() + : table_(nullptr), + name_(), + data_type_(INVALID_DATA), + ref_table_(nullptr), + has_key_attribute_(false) {} + +bool Column::initialize_base(Error *error, + Table *table, + String name, + DataType data_type, + const ColumnOptions &options) { + table_ = table; + if (!name_.assign(error, name)) { return false; } - is_unique_ = false; + data_type_ = data_type; return true; } -// FIXME: 指定された値を検索する. -RowID Column::generic_find(const Datum &datum) const { - switch (data_type()) { - case BOOLEAN: { - auto impl = static_cast<const ColumnImpl<Boolean> *>(this); - return impl->find(static_cast<Boolean>(datum)); - } - case INTEGER: { - auto impl = static_cast<const ColumnImpl<Int64> *>(this); - return impl->find(static_cast<Int64>(datum)); - } - case FLOAT: { - auto impl = static_cast<const ColumnImpl<Float> *>(this); - return impl->find(static_cast<Float>(datum)); - } - case STRING: { - auto impl = static_cast<const ColumnImpl<String> *>(this); - return impl->find(static_cast<std::string>(datum)); - } - } +bool Column::rename(Error *error, String new_name) { + return name_.assign(error, new_name); +} + +bool Column::is_removable() { + // TODO + return true; +} + +bool Column::set_initial_key(Error *error, Int row_id, const Datum &key) { + // TODO + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not suported yet"); return false; } -// FIXME: 指定された ID の値を返す. -Datum Column::generic_get(RowID row_id) const { - switch (data_type()) { - case BOOLEAN: { - return static_cast<const ColumnImpl<Boolean> *>(this)->get(row_id); - } - case INTEGER: { - return static_cast<const ColumnImpl<Int64> *>(this)->get(row_id); - } - case FLOAT: { - return static_cast<const ColumnImpl<Float> *>(this)->get(row_id); - } - case STRING: { - return static_cast<const ColumnImpl<String> *>(this)->get(row_id); - } - } +bool Column::set_default_value(Error *error, Int row_id) { + // TODO + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not suported yet"); return false; } -// FIXME: 指定された ID の値を設定する. -void Column::generic_set(RowID row_id, const Datum &datum) { - switch (data_type()) { - case BOOLEAN: { - static_cast<ColumnImpl<Boolean> *>(this)->set( - row_id, static_cast<Boolean>(datum)); - break; - } - case INTEGER: { - static_cast<ColumnImpl<Int64> *>(this)->set( - row_id, static_cast<Int64>(datum)); - break; - } - case FLOAT: { - static_cast<ColumnImpl<Float> *>(this)->set( - row_id, static_cast<Float>(datum)); - break; - } - case STRING: { - static_cast<ColumnImpl<String> *>(this)->set( - row_id, static_cast<std::string>(datum)); - break; - } +void Column::unset(Int row_id) { +} + +bool BoolColumn::set(Error *error, Int row_id, const Datum &datum) { + if (datum.type() != BOOL_DATA) { + GRNXX_ERROR_SET(error, INVALID_ARGUMENT, "Wrong data type"); + return false; + } + if (!table_->test_row(error, row_id)) { + return false; } + values_[row_id] = static_cast<Bool>(datum); + return true; } -// ストリームに書き出す. -std::ostream &Column::write_to(std::ostream &stream) const { - stream << "{ id = " << id_ - << ", name = \"" << name_ << '"' - << ", data_type = " << data_type_ - << " }"; - return stream; +bool BoolColumn::get(Error *error, Int row_id, Datum *datum) const { + if (!table_->test_row(error, row_id)) { + return false; + } + *datum = values_[row_id]; + return true; } -std::ostream &operator<<(std::ostream &stream, const Column &column) { - return column.write_to(stream); +unique_ptr<BoolColumn> BoolColumn::create(Error *error, + Table *table, + String name, + const ColumnOptions &options) { + unique_ptr<BoolColumn> column(new (nothrow) BoolColumn); + if (!column) { + GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed"); + return nullptr; + } + if (!column->initialize_base(error, table, name, BOOL_DATA, options)) { + return nullptr; + } + try { + column->values_.resize(table->max_row_id() + 1, false); + } catch (...) { + GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed"); + return nullptr; + } + return column; } -// 指定されたカラムを作成して返す. -Column *ColumnHelper::create_column(Table *table, - ColumnID column_id, - const String &column_name, - DataType data_type) { - switch (data_type) { - case BOOLEAN: { - return new ColumnImpl<Boolean>(table, column_id, column_name); - } - case INTEGER: { - return new ColumnImpl<Int64>(table, column_id, column_name); - } - case FLOAT: { - return new ColumnImpl<Float>(table, column_id, column_name); - } - case STRING: { - return new ColumnImpl<String>(table, column_id, column_name); +BoolColumn::~BoolColumn() {} + +bool BoolColumn::set_default_value(Error *error, Int row_id) { + if (static_cast<size_t>(row_id / 64) >= values_.size()) { + try { + values_.resize((row_id / 64) + 1, false); + return true; + } catch (...) { + GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed"); + return false; } } - return nullptr; + values_[row_id] = false; + return true; } -// 指定された参照型のカラムを作成して返す. -Column *ColumnHelper::create_reference_column(Table *table, - ColumnID column_id, - const String &column_name, - Table *dest_table) { - return new ColumnImpl<Int64>(table, column_id, column_name, dest_table); +void BoolColumn::unset(Int row_id) { + values_[row_id] = false; } +BoolColumn::BoolColumn() : Column(), values_() {} + } // namespace grnxx Deleted: lib/grnxx/column.hpp (+0 -85) 100644 =================================================================== --- lib/grnxx/column.hpp 2014-06-25 17:55:59 +0900 (34f0b3d) +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef GRNXX_COLUMN_HPP -#define GRNXX_COLUMN_HPP - -#include "grnxx/datum.hpp" - -namespace grnxx { - -class Column { - public: - // カラムを初期化する. - Column(Table *table, ColumnID id, const String &name, DataType data_type); - // カラムを破棄する. - virtual ~Column(); - - // 所属するテーブルを返す. - Table *table() const { - return table_; - } - // カラム ID を返す. - ColumnID id() const { - return id_; - } - // カラム名を返す. - String name() const { - return name_; - } - // データ型を返す. - DataType data_type() const { - return data_type_; - } - // UNIQUE 制約の有無を返す. - bool is_unique() const { - return is_unique_; - } - - // UNIQUE 制約を設定する. - virtual bool set_unique() = 0; - // UNIQUE 制約を解除する. - virtual bool unset_unique(); - - // 指定された索引との関連付けをおこなう. - virtual bool register_index(Index *index) = 0; - // 指定された索引との関連を削除する. - virtual bool unregister_index(Index *index) = 0; - - // 指定された行 ID が使えるようにサイズを変更する. - virtual void resize(RowID max_row_id) = 0; - - // FIXME: 指定された値を検索する. - virtual RowID generic_find(const Datum &datum) const; - // FIXME: 指定された ID の値を返す. - virtual Datum generic_get(RowID row_id) const; - // FIXME: 指定された ID の値を設定する. - virtual void generic_set(RowID row_id, const Datum &datum); - - // ストリームに書き出す. - virtual std::ostream &write_to(std::ostream &stream) const; - - protected: - Table *table_; - ColumnID id_; - std::string name_; - DataType data_type_; - bool is_unique_; -}; - -std::ostream &operator<<(std::ostream &stream, const Column &column); - -class ColumnHelper { - public: - // 指定されたカラムを作成して返す. - static Column *create_column(Table *table, - ColumnID column_id, - const String &column_name, - DataType data_type); - // 指定された参照型のカラムを作成して返す. - static Column *create_reference_column(Table *table, - ColumnID column_id, - const String &column_name, - Table *dest_table); -}; - -} // namespace grnxx - -#endif // GRNXX_COLUMN_HPP Deleted: lib/grnxx/column_impl.cpp (+0 -548) 100644 =================================================================== --- lib/grnxx/column_impl.cpp 2014-06-25 17:55:59 +0900 (909e625) +++ /dev/null @@ -1,548 +0,0 @@ -#include "grnxx/column_impl.hpp" - -#include "grnxx/index.hpp" -#include "grnxx/table.hpp" - -#include <iostream> // For debugging. - -namespace grnxx { - -// カラムを初期化する. -template <typename T> -ColumnImpl<T>::ColumnImpl(Table *table, ColumnID id, const String &name) - : Column(table, id, name, TypeTraits<T>::data_type()), - data_(MIN_ROW_ID, 0), - indexes_() {} - -// カラムを破棄する. -template <typename T> -ColumnImpl<T>::~ColumnImpl() {} - -// UNIQUE 制約を設定する. -template <typename T> -bool ColumnImpl<T>::set_unique() { - std::set<T> set; - for (RowID row_id = MIN_ROW_ID; row_id <= data_.size(); ++row_id) { - auto it = set.find(data_[row_id]); - if (it != set.end()) { - // 重複があれば失敗する. - return false; - } - set.insert(data_[row_id]); - } - is_unique_ = true; - return true; -} - -// 指定された索引との関連付けをおこなう. -template <typename T> -bool ColumnImpl<T>::register_index(Index *index) { - auto it = std::find(indexes_.begin(), indexes_.end(), index); - if (it != indexes_.end()) { - return false; - } - indexes_.push_back(index); - return true; -} - -// 指定された索引との関連を削除する. -template <typename T> -bool ColumnImpl<T>::unregister_index(Index *index) { - auto it = std::find(indexes_.begin(), indexes_.end(), index); - if (it == indexes_.end()) { - return false; - } - indexes_.erase(it); - return true; -} - -// 指定された行 ID が使えるようにサイズを変更する. -template <typename T> -void ColumnImpl<T>::resize(RowID max_row_id) { - data_.resize(max_row_id + 1, 0); -} - -// 指定された値を検索する. -template <typename T> -RowID ColumnImpl<T>::find(T value) const { - if (indexes_.empty()) { - // 索引がなければ全体を走査する. - for (RowID row_id = MIN_ROW_ID; row_id <= data_.size(); ++row_id) { - if (data_[row_id] == value) { - return row_id; - } - } - } else { - // 索引があれば使う. - auto cursor = indexes_[0]->find_equal(value); - RowID row_id; - if (cursor->get_next(&row_id, 1) != 0) { - return row_id; - } - } - return 0; -} - -// 指定された ID の値を更新する. -template <typename T> -void ColumnImpl<T>::set(RowID row_id, T value) { - data_[row_id] = value; - for (auto index : indexes_) { - index->insert(row_id); - } -} - -template class ColumnImpl<Boolean>; -template class ColumnImpl<Float>; - -#ifdef GRNXX_ENABLE_VARIABLE_INTEGER_TYPE - -// カラムを初期化する. -ColumnImpl<Int64>::ColumnImpl(Table *table, ColumnID id, const String &name, - Table *dest_table) - : Column(table, id, name, INTEGER), - dest_table_(dest_table), - data_8_(MIN_ROW_ID, 0), - data_16_(), - data_32_(), - data_64_(), - internal_data_type_size_(8), - indexes_() {} - -// カラムを破棄する. -ColumnImpl<Int64>::~ColumnImpl() {} - -// UNIQUE 制約を設定する. -bool ColumnImpl<Int64>::set_unique() { - switch (internal_data_type_size_) { - case 8: { - std::set<Int8> set; - for (RowID row_id = MIN_ROW_ID; row_id <= data_8_.size(); ++row_id) { - auto it = set.find(data_8_[row_id]); - if (it != set.end()) { - // 重複があれば失敗する. - return false; - } - set.insert(get(row_id)); - } - break; - } - case 16: { - std::set<Int16> set; - for (RowID row_id = MIN_ROW_ID; row_id <= data_8_.size(); ++row_id) { - auto it = set.find(data_16_[row_id]); - if (it != set.end()) { - // 重複があれば失敗する. - return false; - } - set.insert(get(row_id)); - } - break; - } - case 32: { - std::set<Int32> set; - for (RowID row_id = MIN_ROW_ID; row_id <= data_8_.size(); ++row_id) { - auto it = set.find(data_32_[row_id]); - if (it != set.end()) { - // 重複があれば失敗する. - return false; - } - set.insert(get(row_id)); - } - break; - } - default: { - std::set<Int64> set; - for (RowID row_id = MIN_ROW_ID; row_id <= data_8_.size(); ++row_id) { - auto it = set.find(data_64_[row_id]); - if (it != set.end()) { - // 重複があれば失敗する. - return false; - } - set.insert(get(row_id)); - } - break; - } - } - is_unique_ = true; - return true; -} - -// 指定された索引との関連付けをおこなう. -bool ColumnImpl<Int64>::register_index(Index *index) { - auto it = std::find(indexes_.begin(), indexes_.end(), index); - if (it != indexes_.end()) { - return false; - } - indexes_.push_back(index); - return true; -} - -// 指定された索引との関連を削除する. -bool ColumnImpl<Int64>::unregister_index(Index *index) { - auto it = std::find(indexes_.begin(), indexes_.end(), index); - if (it == indexes_.end()) { - return false; - } - indexes_.erase(it); - return true; -} - -// 指定された行 ID が使えるようにサイズを変更する. -void ColumnImpl<Int64>::resize(RowID max_row_id) { - switch (internal_data_type_size_) { - case 8: { - return data_8_.resize(max_row_id + 1, 0); - } - case 16: { - return data_16_.resize(max_row_id + 1, 0); - } - case 32: { - return data_32_.resize(max_row_id + 1, 0); - } - default: { - return data_64_.resize(max_row_id + 1, 0); - } - } -} - -// 指定された値を検索する. -RowID ColumnImpl<Int64>::find(Int64 value) const { - if (indexes_.empty()) { - // 索引がなければ全体を走査する. - switch (internal_data_type_size_) { - case 8: { - for (RowID row_id = MIN_ROW_ID; row_id <= data_8_.size(); ++row_id) { - if (data_8_[row_id] == value) { - return false; - } - } - break; - } - case 16: { - for (RowID row_id = MIN_ROW_ID; row_id <= data_16_.size(); ++row_id) { - if (data_16_[row_id] == value) { - return false; - } - } - break; - } - case 32: { - for (RowID row_id = MIN_ROW_ID; row_id <= data_32_.size(); ++row_id) { - if (data_32_[row_id] == value) { - return false; - } - } - break; - } - default: { - for (RowID row_id = MIN_ROW_ID; row_id <= data_64_.size(); ++row_id) { - if (data_64_[row_id] == value) { - return false; - } - } - break; - } - } - } else { - // 索引があれば使う. - auto cursor = indexes_[0]->find_equal(value); - RowID row_id; - if (cursor->get_next(&row_id, 1) != 0) { - return row_id; - } - } - return 0; -} - -// 指定された ID の値を更新する. -void ColumnImpl<Int64>::set(RowID row_id, Int64 value) { - if (dest_table_) { - if ((value < dest_table_->min_row_id()) || - (value > dest_table_->max_row_id())) { - throw "invalid reference"; - } - } - switch (internal_data_type_size_) { - case 8: { - if ((value < INT8_MIN) || (value > INT8_MAX)) { - expand_and_set(row_id, value); - } else { - data_8_[row_id] = value; - } - break; - } - case 16: { - if ((value < INT16_MIN) || (value > INT16_MAX)) { - expand_and_set(row_id, value); - } else { - data_16_[row_id] = value; - } - break; - } - case 32: { - if ((value < INT32_MIN) || (value > INT32_MAX)) { - expand_and_set(row_id, value); - } else { - data_32_[row_id] = value; - } - break; - } - default: { - data_64_[row_id] = value; - break; - } - } - for (auto index : indexes_) { - index->insert(row_id); - } -} - -// 渡された値を格納できるように拡張をおこなってから値を格納する. -void ColumnImpl<Int64>::expand_and_set(RowID row_id, Int64 value) { - switch (internal_data_type_size_) { - case 8: { - if ((value < INT32_MIN) || (value > INT32_MAX)) { - // Int8 -> Int64. -// std::cerr << "Log: " << name() << ": Int8 -> Int64" << std::endl; - data_64_.reserve(data_8_.capacity()); - data_64_.assign(data_8_.begin(), data_8_.end()); - data_64_[row_id] = value; - internal_data_type_size_ = 64; - } else if ((value < INT16_MIN) || (value > INT16_MAX)) { - // Int8 -> Int32. -// std::cerr << "Log: " << name() << ": Int8 -> Int32" << std::endl; - data_32_.reserve(data_8_.capacity()); - data_32_.assign(data_8_.begin(), data_8_.end()); - data_32_[row_id] = value; - internal_data_type_size_ = 32; - } else { - // Int8 -> Int16. -// std::cerr << "Log: " << name() << ": Int8 -> Int16" << std::endl; - data_16_.reserve(data_8_.capacity()); - data_16_.assign(data_8_.begin(), data_8_.end()); - data_16_[row_id] = value; - internal_data_type_size_ = 16; - } - std::vector<Int8>().swap(data_8_); - break; - } - case 16: { - if ((value < INT32_MIN) || (value > INT32_MAX)) { - // Int16 -> Int64. -// std::cerr << "Log: " << name() << ": Int16 -> Int64" << std::endl; - data_64_.reserve(data_16_.capacity()); - data_64_.assign(data_16_.begin(), data_16_.end()); - data_64_[row_id] = value; - internal_data_type_size_ = 64; - } else if ((value < INT16_MIN) || (value > INT16_MAX)) { - // Int16 -> Int32. -// std::cerr << "Log: " << name() << ": Int16 -> Int32" << std::endl; - data_32_.reserve(data_16_.capacity()); - data_32_.assign(data_16_.begin(), data_16_.end()); - data_32_[row_id] = value; - internal_data_type_size_ = 32; - } - std::vector<Int16>().swap(data_16_); - break; - } - case 32: { - // Int32 -> Int64. -// std::cerr << "Log: " << name() << ": Int32-> Int64" << std::endl; - data_64_.reserve(data_32_.capacity()); - data_64_.assign(data_32_.begin(), data_32_.end()); - data_64_[row_id] = value; - internal_data_type_size_ = 64; - std::vector<Int32>().swap(data_32_); - break; - } - } -} - -#else // GRNXX_ENABLE_VARIABLE_INTEGER_TYPE - -// カラムを初期化する. -ColumnImpl<Int64>::ColumnImpl(Table *table, ColumnID id, const String &name, - Table *dest_table) - : Column(table, id, name, INTEGER), - dest_table_(dest_table), - data_(MIN_ROW_ID, 0), - indexes_() {} - -// カラムを破棄する. -ColumnImpl<Int64>::~ColumnImpl() {} - -// UNIQUE 制約を設定する. -bool ColumnImpl<Int64>::set_unique() { - std::set<Int64> set; - for (RowID row_id = MIN_ROW_ID; row_id <= data_.size(); ++row_id) { - auto it = set.find(data_[row_id]); - if (it != set.end()) { - // 重複があれば失敗する. - return false; - } - set.insert(data_[row_id]); - } - is_unique_ = true; - return true; -} - -// 指定された索引との関連付けをおこなう. -bool ColumnImpl<Int64>::register_index(Index *index) { - auto it = std::find(indexes_.begin(), indexes_.end(), index); - if (it != indexes_.end()) { - return false; - } - indexes_.push_back(index); - return true; -} - -// 指定された索引との関連を削除する. -bool ColumnImpl<Int64>::unregister_index(Index *index) { - auto it = std::find(indexes_.begin(), indexes_.end(), index); - if (it == indexes_.end()) { - return false; - } - indexes_.erase(it); - return true; -} - -// 指定された行 ID が使えるようにサイズを変更する. -void ColumnImpl<Int64>::resize(RowID max_row_id) { - data_.resize(max_row_id + 1, 0); -} - -// 指定された値を検索する. -RowID ColumnImpl<Int64>::find(Int64 value) const { - if (indexes_.empty()) { - // 索引がなければ全体を走査する. - for (RowID row_id = MIN_ROW_ID; row_id <= data_.size(); ++row_id) { - if (data_[row_id] == value) { - return row_id; - } - } - } else { - // 索引があれば使う. - auto cursor = indexes_[0]->find_equal(value); - RowID row_id; - if (cursor->get_next(&row_id, 1) != 0) { - return row_id; - } - } - return 0; -} - -// 指定された ID の値を更新する. -void ColumnImpl<Int64>::set(RowID row_id, Int64 value) { - if (dest_table_) { - if ((value < dest_table_->min_row_id()) || - (value > dest_table_->max_row_id())) { - throw "invalid reference"; - } - } - data_[row_id] = value; - for (auto index : indexes_) { - index->insert(row_id); - } -} - -#endif // GRNXX_ENABLE_VARIABLE_INTEGER_TYPE - -// カラムを初期化する. -ColumnImpl<String>::ColumnImpl(Table *table, ColumnID id, const String &name) - : Column(table, id, name, TypeTraits<String>::data_type()), - headers_(MIN_ROW_ID, 0), - bodies_(), - indexes_() {} - -// カラムを破棄する. -ColumnImpl<String>::~ColumnImpl() {} - -// UNIQUE 制約を設定する. -bool ColumnImpl<String>::set_unique() { - std::set<String> set; - for (RowID row_id = MIN_ROW_ID; row_id <= headers_.size(); ++row_id) { - auto value = get(row_id); - auto it = set.find(value); - if (it != set.end()) { - // 重複があれば失敗する. - return false; - } - set.insert(value); - } - is_unique_ = true; - return true; -} - -// 指定された索引との関連付けをおこなう. -bool ColumnImpl<String>::register_index(Index *index) { - auto it = std::find(indexes_.begin(), indexes_.end(), index); - if (it != indexes_.end()) { - return false; - } - indexes_.push_back(index); - return true; -} - -// 指定された索引との関連を削除する. -bool ColumnImpl<String>::unregister_index(Index *index) { - auto it = std::find(indexes_.begin(), indexes_.end(), index); - if (it == indexes_.end()) { - return false; - } - indexes_.erase(it); - return true; -} - -// 指定された行 ID が使えるようにサイズを変更する. -void ColumnImpl<String>::resize(RowID max_row_id) { - headers_.resize(max_row_id + 1, 0); -} - -// 指定された値を検索する. -RowID ColumnImpl<String>::find(const String &value) const { - if (indexes_.empty()) { - // 索引がなければ全体を走査する. - for (RowID row_id = MIN_ROW_ID; row_id <= headers_.size(); ++row_id) { - if (get(row_id) == value) { - return row_id; - } - } - } else { - // 索引があれば使う. - auto cursor = indexes_[0]->find_equal(value); - RowID row_id; - if (cursor->get_next(&row_id, 1) != 0) { - return row_id; - } - } - return 0; -} - -// 指定された ID の値を更新する. -void ColumnImpl<String>::set(RowID row_id, const String &value) { - if (value.empty()) { - headers_[row_id] = 0; - return; - } - Int64 offset = bodies_.size(); - if (value.size() < 0xFFFF) { - bodies_.resize(offset + value.size()); - std::memcpy(&bodies_[offset], value.data(), value.size()); - headers_[row_id] = (offset << 16) | value.size(); - } else { - // 長い文字列については offset の位置にサイズを保存する. - if ((offset % sizeof(Int64)) != 0) { - offset += sizeof(Int64) - (offset % sizeof(Int64)); - } - bodies_.resize(offset + sizeof(Int64) + value.size()); - *reinterpret_cast<Int64 *>(&bodies_[offset]) = value.size(); - std::memcpy(&bodies_[offset + sizeof(Int64)], value.data(), value.size()); - headers_[row_id] = (offset << 16) | 0xFFFF; - } - for (auto index : indexes_) { - index->insert(row_id); - } -} - -} // namespace grnxx Modified: lib/grnxx/column_impl.hpp (+30 -202) =================================================================== --- lib/grnxx/column_impl.hpp 2014-06-25 17:55:59 +0900 (2114a5f) +++ lib/grnxx/column_impl.hpp 2014-07-04 12:25:36 +0900 (46cbbe2) @@ -1,221 +1,49 @@ -#ifndef GRNXX_COLUMN_IMPL_HPP -#define GRNXX_COLUMN_IMPL_HPP +#ifndef GRNXX_COLUMN_BASE_HPP +#define GRNXX_COLUMN_BASE_HPP + +#include <vector> -#include "../config.h" #include "grnxx/column.hpp" namespace grnxx { -template <typename T> -class ColumnImpl : public Column { +class BoolColumn : public Column { public: - using Value = T; - - // カラムを初期化する. - ColumnImpl(Table *table, ColumnID id, const String &name); - // カラムを破棄する. - ~ColumnImpl(); + // -- Public API -- - // コピーと代入を禁止する. - ColumnImpl(const ColumnImpl &) = delete; - ColumnImpl &operator=(const ColumnImpl &) = delete; + bool set(Error *error, Int row_id, const Datum &datum); + bool get(Error *error, Int row_id, Datum *datum) const; - // UNIQUE 制約を設定する. - bool set_unique(); + // -- Internal API -- - // 指定された索引との関連付けをおこなう. - bool register_index(Index *index); - // 指定された索引との関連を削除する. - bool unregister_index(Index *index); + // 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<BoolColumn> create(Error *error, + Table *table, + String name, + const ColumnOptions &options); - // 指定された行 ID が使えるようにサイズを変更する. - void resize(RowID max_row_id); + ~BoolColumn(); - // 指定された値を検索する. - RowID find(T value) const; + bool set_default_value(Error *error, Int row_id); + void unset(Int row_id); - // 指定された ID の値を返す. - T get(RowID row_id) const { - return data_[row_id]; + // Return a value identified by "row_id". + // + // Assumes that "row_id" is valid. Otherwise, the result is undefined. + Bool get(Int row_id) const { + return values_[row_id]; } - // 指定された ID の値を更新する. - void set(RowID row_id, T value); - - private: - std::vector<T> data_; - std::vector<Index *> indexes_; -}; - -#ifdef GRNXX_ENABLE_VARIABLE_INTEGER_TYPE -template <> -class ColumnImpl<Int64> : public Column { - public: - using Value = Int64; - - // カラムを初期化する. - ColumnImpl(Table *table, ColumnID id, const String &name, - Table *dest_table = nullptr); - // カラムを破棄する. - ~ColumnImpl(); - - // コピーと代入を禁止する. - ColumnImpl(const ColumnImpl &) = delete; - ColumnImpl &operator=(const ColumnImpl &) = delete; - - // UNIQUE 制約を設定する. - bool set_unique(); - - // 指定された索引との関連付けをおこなう. - bool register_index(Index *index); - // 指定された索引との関連を削除する. - bool unregister_index(Index *index); - - // 指定された行 ID が使えるようにサイズを変更する. - void resize(RowID max_row_id); - - // 参照先のテーブルを返す. - // なければ nullptr を返す. - Table *dest_table() const { - return dest_table_; - } - - // 指定された値を検索する. - RowID find(Int64 value) const; - - // 指定された ID の値を返す. - Int64 get(RowID row_id) const { - switch (internal_data_type_size_) { - case 8: { - return data_8_[row_id]; - } - case 16: { - return data_16_[row_id]; - } - case 32: { - return data_32_[row_id]; - } - default: { - return data_64_[row_id]; - } - } - } - // 指定された ID の値を更新する. - void set(RowID row_id, Int64 value); - - private: - Table *dest_table_; - std::vector<Int8> data_8_; - std::vector<Int16> data_16_; - std::vector<Int32> data_32_; - std::vector<Int64> data_64_; - Int64 internal_data_type_size_; - std::vector<Index *> indexes_; - - // 渡された値を格納できるように拡張をおこなってから値を格納する. - void expand_and_set(RowID row_id, Int64 value); -}; -#else // GRNXX_ENABLE_VARIABLE_INTEGER_TYPE -template <> -class ColumnImpl<Int64> : public Column { - public: - using Value = Int64; - - // カラムを初期化する. - ColumnImpl(Table *table, ColumnID id, const String &name, - Table *dest_table = nullptr); - // カラムを破棄する. - ~ColumnImpl(); - // コピーと代入を禁止する. - ColumnImpl(const ColumnImpl &) = delete; - ColumnImpl &operator=(const ColumnImpl &) = delete; - - // UNIQUE 制約を設定する. - bool set_unique(); - - // 指定された索引との関連付けをおこなう. - bool register_index(Index *index); - // 指定された索引との関連を削除する. - bool unregister_index(Index *index); - - // 指定された行 ID が使えるようにサイズを変更する. - void resize(RowID max_row_id); - - // 参照先のテーブルを返す. - // なければ nullptr を返す. - Table *dest_table() const { - return dest_table_; - } - - // 指定された値を検索する. - RowID find(Int64 value) const; - - // 指定された ID の値を返す. - Int64 get(RowID row_id) const { - return data_[row_id]; - } - // 指定された ID の値を更新する. - void set(RowID row_id, Int64 value); - - private: - Table *dest_table_; - std::vector<Int64> data_; - std::vector<Index *> indexes_; -}; -#endif // GRNXX_ENABLE_VARIABLE_INTEGER_TYPE - -template <> -class ColumnImpl<String> : public Column { - public: - using Value = String; - - // カラムを初期化する. - ColumnImpl(Table *table, ColumnID id, const String &name); - // カラムを破棄する. - ~ColumnImpl(); - - // コピーと代入を禁止する. - ColumnImpl(const ColumnImpl &) = delete; - ColumnImpl &operator=(const ColumnImpl &) = delete; - - // UNIQUE 制約を設定する. - bool set_unique(); - - // 指定された索引との関連付けをおこなう. - bool register_index(Index *index); - // 指定された索引との関連を削除する. - bool unregister_index(Index *index); - - // 指定された行 ID が使えるようにサイズを変更する. - void resize(RowID max_row_id); - - // 指定された値を検索する. - RowID find(const String &value) const; - - // 指定された ID の値を返す. - String get(RowID row_id) const { - Int64 size = headers_[row_id] & 0xFFFF; - if (size == 0) { - return String("", 0); - } - Int64 offset = headers_[row_id] >> 16; - if (size < 0xFFFF) { - return String(&bodies_[offset], size); - } else { - // 長い文字列については offset の位置にサイズが保存してある. - size = *reinterpret_cast<const Int64 *>(&bodies_[offset]); - return String(&bodies_[offset + sizeof(Int64)], size); - } - } - // 指定された ID の値を更新する. - void set(RowID row_id, const String &value); + protected: + std::vector<bool> values_; - private: - std::vector<UInt64> headers_; - std::vector<char> bodies_; - std::vector<Index *> indexes_; + BoolColumn(); }; } // namespace grnxx -#endif // GRNXX_COLUMN_IMPL_HPP +#endif // GRNXX_COLUMN_BASE_HPP Deleted: lib/grnxx/database.cpp (+0 -90) 100644 =================================================================== --- lib/grnxx/database.cpp 2014-06-25 17:55:59 +0900 (2461032) +++ /dev/null @@ -1,90 +0,0 @@ -#include "grnxx/database.hpp" - -#include "grnxx/table.hpp" - -#include <ostream> - -namespace grnxx { - -// データベースを初期化する. -Database::Database() - : tables_(MIN_TABLE_ID), - tables_map_() {} - -// データベースを破棄する. -Database::~Database() {} - -// 指定された名前のテーブルを作成して返す. -// 失敗すると nullptr を返す. -Table *Database::create_table(const String &table_name) { - auto it = tables_map_.find(table_name); - if (it != tables_map_.end()) { - return nullptr; - } - TableID table_id = min_table_id(); - for ( ; table_id <= max_table_id(); ++table_id) { - if (!tables_[table_id]) { - break; - } - } - if (table_id > max_table_id()) { - tables_.resize(table_id + 1); - } - std::unique_ptr<Table> new_table(new Table(this, table_id, table_name)); - tables_map_.insert(it, std::make_pair(new_table->name(), table_id)); - tables_[table_id] = std::move(new_table); - return tables_[table_id].get(); -} - -// 指定された名前のテーブルを破棄する. -// 成功すれば true を返し,失敗すれば false を返す. -bool Database::drop_table(const String &table_name) { - auto it = tables_map_.find(table_name); - if (it == tables_map_.end()) { - return false; - } - tables_[it->second].reset(); - tables_map_.erase(it); - return true; -} - -// 指定された名前のテーブルを返す. -// なければ nullptr を返す. -Table *Database::get_table_by_name(const String &table_name) const { - auto it = tables_map_.find(table_name); - if (it == tables_map_.end()) { - return nullptr; - } - return tables_[it->second].get(); -} - -// テーブルの一覧を tables の末尾に追加し,テーブルの数を返す. -Int64 Database::get_tables(std::vector<Table *> *tables) const { - std::size_t old_size = tables->size(); - for (auto &table : tables_) { - if (table) { - tables->push_back(table.get()); - } - } - return tables->size() - old_size; -} - -// ストリームに書き出す. -std::ostream &Database::write_to(std::ostream &stream) const { - std::vector<Table *> tables; - if (get_tables(&tables) == 0) { - return stream << "{}"; - } - stream << "{ " << *tables[0]; - for (std::size_t i = 1; i < tables.size(); ++i) { - stream << ", " << *tables[i]; - } - stream << " }"; - return stream; -} - -std::ostream &operator<<(std::ostream &stream, const Database &database) { - return database.write_to(stream); -} - -} // namespace grnxx Deleted: lib/grnxx/database.hpp (+0 -62) 100644 =================================================================== --- lib/grnxx/database.hpp 2014-06-25 17:55:59 +0900 (3962c1a) +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef GRNXX_DATABASE_HPP -#define GRNXX_DATABASE_HPP - -#include "grnxx/types.hpp" - -namespace grnxx { - -class Database { - public: - // データベースを初期化する. - Database(); - // データベースを破棄する. - ~Database(); - - // コピーと代入を禁止する. - Database(const Database &) = delete; - Database &operator=(const Database &) = delete; - - // 指定された名前のテーブルを作成して返す. - // 失敗すると nullptr を返す. - Table *create_table(const String &table_name); - // 指定された名前のテーブルを破棄する. - // 成功すれば true を返し,失敗すれば false を返す. - bool drop_table(const String &table_name); - - // テーブル ID の最小値を返す. - TableID min_table_id() const { - return MIN_TABLE_ID; - } - // テーブル ID の最大値を返す. - TableID max_table_id() const { - return tables_.size() - 1; - } - - // 指定された ID のテーブルを返す. - // なければ nullptr を返す. - Table *get_table_by_id(TableID table_id) const { - if (table_id > max_table_id()) { - return nullptr; - } - return tables_[table_id].get(); - } - // 指定された名前のテーブルを返す. - // なければ nullptr を返す. - Table *get_table_by_name(const String &table_name) const; - - // テーブルの一覧を tables の末尾に追加し,テーブルの数を返す. - Int64 get_tables(std::vector<Table *> *tables) const; - - // ストリームに書き出す. - std::ostream &write_to(std::ostream &stream) const; - - private: - std::vector<std::unique_ptr<Table>> tables_; - std::map<String, TableID> tables_map_; -}; - -std::ostream &operator<<(std::ostream &stream, const Database &database); - -} // namespace grnxx - -#endif // GRNXX_DATABASE_HPP Deleted: lib/grnxx/datum.cpp (+0 -37) 100644 =================================================================== --- lib/grnxx/datum.cpp 2014-06-25 17:55:59 +0900 (cb09357) +++ /dev/null @@ -1,37 +0,0 @@ -#include "grnxx/datum.hpp" - -#include <ostream> - -namespace grnxx { - -std::ostream &Datum::write_to(std::ostream &stream) const { - switch (type_) { - case BOOLEAN: { - return stream << (boolean_ ? "TRUE" : "FALSE"); - } - case INTEGER: { - return stream << integer_; - } - case FLOAT: { - if (std::isnan(float_)) { - return stream << "NAN"; - } else if (std::isinf(float_)) { - return stream << (std::signbit(float_) ? "-INF" : "INF"); - } else { - return stream << float_; - } - } - case STRING: { - return stream << '"' << string_ << '"'; - } - default: { - return stream << "N/A"; - } - } -} - -std::ostream &operator<<(std::ostream &stream, const Datum &datum) { - return datum.write_to(stream); -} - -} // namespace grnxx Deleted: lib/grnxx/datum.hpp (+0 -244) 100644 =================================================================== --- lib/grnxx/datum.hpp 2014-06-25 17:55:59 +0900 (a68ff0a) +++ /dev/null @@ -1,244 +0,0 @@ -#ifndef GRNXX_DATUM_HPP -#define GRNXX_DATUM_HPP - -#include "grnxx/string.hpp" -#include "grnxx/types.hpp" - -namespace grnxx { - -// 汎用データ型. -class Datum { - public: - // FALSE として初期化する. - Datum() : type_(BOOLEAN), boolean_(false) {} - // 真偽値として初期化する. - Datum(Boolean value) : type_(BOOLEAN), boolean_(value) {} - // 整数として初期化する. - Datum(Int64 value) : type_(INTEGER), integer_(value) {} - // 浮動小数点数として初期化する. - Datum(Float value) : type_(FLOAT), float_(value) {} - // バイト列・文字列として初期化する. - Datum(const char *value) : type_(STRING), string_(value) {} - Datum(const String &value) - : type_(STRING), - string_(reinterpret_cast<const char *>(value.data()), value.size()) {} - Datum(const std::string &value) : type_(STRING), string_(value) {} - // 破棄する. - ~Datum() { - if (type_ == STRING) { - string_.~basic_string(); - } - } - - // string_ はコピーするとコストが大きくなるので注意. - Datum(const Datum &datum) : type_(BOOLEAN), boolean_(false) { - switch (datum.type_) { - case BOOLEAN: { - boolean_ = datum.boolean_; - type_ = datum.type_; - break; - } - case INTEGER: { - integer_ = datum.integer_; - type_ = datum.type_; - break; - } - case FLOAT: { - float_ = datum.float_; - type_ = datum.type_; - break; - } - case STRING: { - new (&string_) std::string(datum.string_); - type_ = datum.type_; - break; - } - default: { - break; - } - } - } - // string_ はムーブの方がコストを抑えられるはず. - Datum(Datum &&datum) : type_(BOOLEAN), boolean_(false) { - switch (datum.type_) { - case BOOLEAN: { - boolean_ = std::move(datum.boolean_); - type_ = std::move(datum.type_); - break; - } - case INTEGER: { - integer_ = std::move(datum.integer_); - type_ = std::move(datum.type_); - break; - } - case FLOAT: { - float_ = std::move(datum.float_); - type_ = std::move(datum.type_); - break; - } - case STRING: { - new (&string_) std::string(std::move(datum.string_)); - type_ = std::move(datum.type_); - break; - } - default: { - break; - } - } - } - - // データ型を返す. - DataType type() const { - return type_; - } - - // データを代入する. - Datum &operator=(Boolean rhs) { - if (type_ == STRING) { - string_.~basic_string(); - } - type_ = BOOLEAN; - boolean_ = rhs; - return *this; - } - Datum &operator=(Int64 rhs) { - if (type_ == STRING) { - string_.~basic_string(); - } - type_ = INTEGER; - integer_ = static_cast<Int64>(rhs); - return *this; - } - Datum &operator=(Float rhs) { - if (type_ == STRING) { - string_.~basic_string(); - } - type_ = FLOAT; - float_ = static_cast<Float>(rhs); - return *this; - } - Datum &operator=(const char *rhs) { - if (type_ != STRING) { - new (&string_) std::string(rhs); - type_ = STRING; - } else { - string_ = rhs; - } - return *this; - } - Datum &operator=(const String &rhs) { - if (type_ != STRING) { - new (&string_) std::string(reinterpret_cast<const char *>(rhs.data()), - rhs.size()); - type_ = STRING; - } else { - string_.assign(reinterpret_cast<const char *>(rhs.data()), rhs.size()); - } - return *this; - } - Datum &operator=(const std::string &rhs) { - if (type_ != STRING) { - new (&string_) std::string(rhs); - type_ = STRING; - } else { - string_ = rhs; - } - return *this; - } - - // 型変換. - explicit operator Boolean() const { - switch (type_) { - case BOOLEAN: { - return boolean_; - } - case INTEGER: { - return static_cast<Boolean>(integer_); - } - case FLOAT: { - return static_cast<Boolean>(float_); - } - case STRING: { - return string_ == "TRUE"; - } - default: { - return false; - } - } - } - explicit operator Int64() const { - switch (type_) { - case BOOLEAN: { - return static_cast<Int64>(boolean_); - } - case INTEGER: { - return integer_; - } - case FLOAT: { - return static_cast<Int64>(float_); - } - case STRING: { - return static_cast<Int64>(std::stol(string_)); - } - default: { - return 0; - } - } - } - explicit operator Float() const { - switch (type_) { - case BOOLEAN: { - return static_cast<Float>(boolean_); - } - case INTEGER: { - return static_cast<Float>(integer_); - } - case FLOAT: { - return float_; - } - case STRING: { - return static_cast<Float>(std::stod(string_)); - } - default: { - return 0.0; - } - } - } - explicit operator std::string() const { - switch (type_) { - case BOOLEAN: { - return boolean_ ? "TRUE" : "FALSE"; - } - case INTEGER: { - return std::to_string(integer_); - } - case FLOAT: { - return std::to_string(float_); - } - case STRING: { - return string_; - } - default: { - return ""; - } - } - } - - // ストリームに書き出す. - std::ostream &write_to(std::ostream &stream) const; - - private: - DataType type_; - union { - Boolean boolean_; - Int64 integer_; - Float float_; - std::string string_; - }; -}; - -std::ostream &operator<<(std::ostream &stream, const Datum &datum); - -} // namespace grnxx - -#endif // GRNXX_DATUM_HPP Added: lib/grnxx/db.cpp (+164 -0) 100644 =================================================================== --- /dev/null +++ lib/grnxx/db.cpp 2014-07-04 12:25:36 +0900 (d100cd9) @@ -0,0 +1,164 @@ +#include "grnxx/db.hpp" + +#include "grnxx/error.hpp" +#include "grnxx/table.hpp" + +namespace grnxx { + +DB::~DB() {} + +Table *DB::create_table(Error *error, + String name, + const TableOptions &options) { + if (find_table(nullptr, name)) { + GRNXX_ERROR_SET(error, ALREADY_EXISTS, + "Table already exists: name = \"%.*s\"", + static_cast<int>(name.size()), name.data()); + return nullptr; + } + try { + tables_.reserve(tables_.size() + 1); + } catch (...) { + GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed"); + return nullptr; + } + unique_ptr<Table> new_table = + Table::create(error, this, name, options); + if (!new_table) { + return nullptr; + } + tables_.push_back(std::move(new_table)); + return tables_.back().get(); +} + +bool DB::remove_table(Error *error, String name) { + size_t table_id; + if (!find_table_with_id(error, name, &table_id)) { + return false; + } + if (!tables_[table_id]->is_removable()) { + GRNXX_ERROR_SET(error, NOT_REMOVABLE, + "Table is not removable: name = \"%.*s\"", + static_cast<int>(name.size()), name.data()); + return false; + } + tables_.erase(tables_.begin() + table_id); + return true; +} + +bool DB::rename_table(Error *error, + String name, + String new_name) { + size_t table_id; + if (!find_table_with_id(error, name, &table_id)) { + return false; + } + if (name == new_name) { + return true; + } + if (find_table(nullptr, new_name)) { + GRNXX_ERROR_SET(error, ALREADY_EXISTS, + "Table already exists: new_name = \"%.*s\"", + static_cast<int>(new_name.size()), new_name.data()); + return false; + } + return tables_[table_id]->rename(error, new_name); +} + +bool DB::reorder_table(Error *error, + String name, + String prev_name) { + size_t table_id; + if (!find_table_with_id(error, name, &table_id)) { + return false; + } + size_t new_table_id = 0; + if (prev_name.size() != 0) { + size_t prev_table_id; + if (!find_table_with_id(error, prev_name, &prev_table_id)) { + return false; + } + if (table_id <= prev_table_id) { + new_table_id = prev_table_id; + } else { + new_table_id = prev_table_id + 1; + } + } + for ( ; table_id < new_table_id; ++table_id) { + std::swap(tables_[table_id], tables_[table_id + 1]); + } + for ( ; table_id > new_table_id; --table_id) { + std::swap(tables_[table_id], tables_[table_id - 1]); + } + return true; +} + +Table *DB::get_table(Error *error, size_t table_id) const { + if (table_id >= num_tables()) { + GRNXX_ERROR_SET(error, NOT_FOUND, + "Table not found: table_id = %" PRIi64, table_id); + return nullptr; + } + return tables_[table_id].get(); +} + +Table *DB::find_table(Error *error, String name) const { + for (size_t table_id = 0; table_id < num_tables(); ++table_id) { + if (name == tables_[table_id]->name()) { + return tables_[table_id].get(); + } + } + GRNXX_ERROR_SET(error, NOT_FOUND, "Table not found: name = \"%.*s\"", + static_cast<int>(name.size()), name.data()); + return nullptr; +} + +bool DB::save(Error *error, + String path, + const DBOptions &options) const { + // TODO: Named DB is not supported yet. + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet"); + return false; +} + +DB::DB() : tables_() {} + +Table *DB::find_table_with_id(Error *error, + String name, + size_t *table_id) const { + for (size_t i = 0; i < num_tables(); ++i) { + if (name == tables_[i]->name()) { + if (table_id != nullptr) { + *table_id = i; + } + return tables_[i].get(); + } + } + GRNXX_ERROR_SET(error, NOT_FOUND, "Table not found: name = \"%.*s\"", + static_cast<int>(name.size()), name.data()); + return nullptr; +} + +unique_ptr<DB> open_db(Error *error, + String path, + const DBOptions &options) { + if (path.size() != 0) { + // TODO: Named DB is not supported yet. + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet"); + return nullptr; + } + std::unique_ptr<DB> db(new (nothrow) DB); + if (!db) { + GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed"); + return nullptr; + } + return db; +} + +bool remove_db(Error *error, String path, const DBOptions &options) { + // TODO: Named DB is not supported yet. + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supported yet"); + return false; +} + +} // namespace grnxx Added: lib/grnxx/error.cpp (+19 -0) 100644 =================================================================== --- /dev/null +++ lib/grnxx/error.cpp 2014-07-04 12:25:36 +0900 (5536c35) @@ -0,0 +1,19 @@ +#include "grnxx/error.hpp" + +#include <cstdio> +#include <cstdarg> + +namespace grnxx { + +bool Error::set_message(const char *format, ...) { + va_list args; + va_start(args, format); + if (std::vsnprintf(message_, MESSAGE_BUF_SIZE, format, args) < 0) { + message_[0] = '\0'; + return false; + } + va_end(args); + return true; +} + +} // namespace grnxx Modified: lib/grnxx/index.cpp (+25 -557) =================================================================== --- lib/grnxx/index.cpp 2014-06-25 17:55:59 +0900 (01b9cbb) +++ lib/grnxx/index.cpp 2014-07-04 12:25:36 +0900 (e6f760a) @@ -1,573 +1,41 @@ #include "grnxx/index.hpp" -#include "grnxx/column_impl.hpp" - -#include <ostream> +#include "grnxx/cursor.hpp" namespace grnxx { -namespace { - -template <typename T> -class TreeMapIndex : public Index { - public: - using RowIDSet = std::set<RowID>; - using Map = std::map<T, RowIDSet>; - - // 索引を初期化する. - TreeMapIndex(IndexID index_id, - const String &index_name, - Column *column); - - // 指定されたデータを登録する. - void insert(RowID row_id); - // 指定されたデータを削除する. - void remove(RowID row_id); - - // 行の一覧を取得できるカーソルを作成して返す. - RowIDCursor *find_all(bool reverse_order); - // 指定された値が格納された行の一覧を取得できるカーソルを作成して返す. - RowIDCursor *find_equal(const Datum &datum, bool reverse_order); - // 指定された範囲の値が格納された行の一覧を取得できるカーソルを作成して返す. - RowIDCursor *find_less(const Datum &datum, bool less_equal, - bool reverse_order); - // 指定された範囲の値が格納された行の一覧を取得できるカーソルを作成して返す. - RowIDCursor *find_greater(const Datum &datum, bool greater_equal, - bool reverse_order); - // 指定された範囲の値が格納された行の一覧を取得できるカーソルを作成して返す. - RowIDCursor *find_between(const Datum &begin, const Datum &end, - bool greater_equal, bool less_equal, - bool reverse_order); - - class Cursor : public RowIDCursor { - public: - Cursor(typename Map::iterator begin, typename Map::iterator end); - - // 行 ID を最大 size 個取得して buf に格納し,取得した行 ID の数を返す. - // buf が nullptr のときは取得した行 ID をそのまま捨てる. - Int64 get_next(RowID *buf, Int64 size); - - private: - typename Map::iterator map_it_; - typename Map::iterator map_end_; - typename RowIDSet::iterator set_it_; - typename RowIDSet::iterator set_end_; - }; - - class ReverseCursor : public RowIDCursor { - public: - ReverseCursor(typename Map::iterator begin, typename Map::iterator end); - - // 行 ID を最大 size 個取得して buf に格納し,取得した行 ID の数を返す. - // buf が nullptr のときは取得した行 ID をそのまま捨てる. - Int64 get_next(RowID *buf, Int64 size); - - private: - typename Map::reverse_iterator map_it_; - typename Map::reverse_iterator map_end_; - typename RowIDSet::iterator set_it_; - typename RowIDSet::iterator set_end_; - }; - - RowIDCursor *create_cursor(typename Map::iterator begin, - typename Map::iterator end, bool reverse_order); - - private: - Map map_; - ColumnImpl<T> *column_; -}; - -// 索引を初期化する. -template <typename T> -TreeMapIndex<T>::TreeMapIndex(IndexID index_id, - const String &index_name, - Column *column) - : Index(index_id, index_name, column, TREE_MAP), - map_(), - column_(static_cast<ColumnImpl<T> *>(column)) {} - -// 指定されたデータを登録する. -template <typename T> -void TreeMapIndex<T>::insert(RowID row_id) { - map_[column_->get(row_id)].insert(row_id); -} - -// 指定されたデータを削除する. -template <typename T> -void TreeMapIndex<T>::remove(RowID row_id) { - auto map_it = map_.find(column_->get(row_id)); - if (map_it == map_.end()) { - return; - } - auto &set = map_it->second; - auto set_it = set.find(row_id); - if (set_it == set.end()) { - return; - } - set.erase(set_it); - if (set.empty()) { - map_.erase(map_it); - } -} - -// 行の一覧を取得できるカーソルを作成して返す. -template <typename T> -RowIDCursor *TreeMapIndex<T>::find_all(bool reverse_order) { - return create_cursor(map_.begin(), map_.end(), reverse_order); -} - -// 指定された値が格納された行の一覧を取得できるカーソルを作成して返す. -template <typename T> -RowIDCursor *TreeMapIndex<T>::find_equal(const Datum &datum, - bool reverse_order) { - auto range = map_.equal_range(static_cast<T>(datum)); - return create_cursor(range.first, range.second, reverse_order); -} - -// 指定された範囲の値が格納された行の一覧を取得できるカーソルを作成して返す. -template <typename T> -RowIDCursor *TreeMapIndex<T>::find_less(const Datum &datum, - bool less_equal, - bool reverse_order) { - typename Map::iterator map_end; - if (less_equal) { - map_end = map_.upper_bound(static_cast<T>(datum)); - } else { - map_end = map_.lower_bound(static_cast<T>(datum)); - } - return create_cursor(map_.begin(), map_end, reverse_order); -} - -// 指定された範囲の値が格納された行の一覧を取得できるカーソルを作成して返す. -template <typename T> -RowIDCursor *TreeMapIndex<T>::find_greater(const Datum &datum, - bool greater_equal, - bool reverse_order) { - typename Map::iterator map_begin; - if (greater_equal) { - map_begin = map_.lower_bound(static_cast<T>(datum)); - } else { - map_begin = map_.upper_bound(static_cast<T>(datum)); - } - return create_cursor(map_begin, map_.end(), reverse_order); -} - -// 指定された範囲の値が格納された行の一覧を取得できるカーソルを作成して返す. -template <typename T> -RowIDCursor *TreeMapIndex<T>::find_between(const Datum &begin, - const Datum &end, - bool greater_equal, - bool less_equal, - bool reverse_order) { - T begin_key = static_cast<T>(begin); - T end_key = static_cast<T>(end); - typename Map::iterator map_begin; - if (greater_equal) { - map_begin = map_.lower_bound(begin_key); - } else { - map_begin = map_.upper_bound(begin_key); - } - typename Map::iterator map_end; - if (less_equal) { - map_end = map_.upper_bound(end_key); - } else { - map_end = map_.lower_bound(end_key); - } - return create_cursor(map_begin, map_end, reverse_order); -} - -template <typename T> -TreeMapIndex<T>::Cursor::Cursor(typename Map::iterator begin, - typename Map::iterator end) - : RowIDCursor(), - map_it_(begin), - map_end_(end), - set_it_(), - set_end_() { - if (begin != end) { - set_it_ = begin->second.begin(); - set_end_ = begin->second.end(); - } -} - -// 行 ID を最大 size 個取得して buf に格納し,取得した行 ID の数を返す. -// buf が nullptr のときは取得した行 ID をそのまま捨てる. -template <typename T> -Int64 TreeMapIndex<T>::Cursor::get_next(RowID *buf, Int64 size) { - if (map_it_ == map_end_) { - return 0; - } - Int64 count = 0; - while (count < size) { - if (set_it_ == set_end_) { - ++map_it_; - if (map_it_ == map_end_) { - return count; - } - set_it_ = map_it_->second.begin(); - set_end_ = map_it_->second.end(); - } - if (buf) { - buf[count] = *set_it_; - } - ++set_it_; - ++count; - } - return count; -} - -template <typename T> -TreeMapIndex<T>::ReverseCursor::ReverseCursor(typename Map::iterator begin, - typename Map::iterator end) - : RowIDCursor(), - map_it_(end), - map_end_(begin), - set_it_(), - set_end_() { - if (map_it_ != map_end_) { - set_it_ = map_it_->second.begin(); - set_end_ = map_it_->second.end(); - } -} - -// 行 ID を最大 size 個取得して buf に格納し,取得した行 ID の数を返す. -// buf が nullptr のときは取得した行 ID をそのまま捨てる. -template <typename T> -Int64 TreeMapIndex<T>::ReverseCursor::get_next(RowID *buf, Int64 size) { - if (map_it_ == map_end_) { - return 0; - } - Int64 count = 0; - while (count < size) { - if (set_it_ == set_end_) { - ++map_it_; - if (map_it_ == map_end_) { - return count; - } - set_it_ = map_it_->second.begin(); - set_end_ = map_it_->second.end(); - } - if (buf) { - buf[count] = *set_it_; - } - ++set_it_; - ++count; - } - return count; -} - -template <typename T> -RowIDCursor *TreeMapIndex<T>::create_cursor(typename Map::iterator begin, - typename Map::iterator end, - bool reverse_order) { - if (reverse_order) { - return new ReverseCursor(begin, end); - } else { - return new Cursor(begin, end); - } -} - -// String では更新時に本体のアドレスが移動してしまい, -// std::map では対応できないので, std::string を使っている. -template <> -class TreeMapIndex<String> : public Index { - public: - using RowIDSet = std::set<RowID>; - using Map = std::map<std::string, RowIDSet>; - - // 索引を初期化する. - TreeMapIndex(IndexID index_id, - const String &index_name, - Column *column); - // 指定されたデータを登録する. - void insert(RowID row_id); - // 指定されたデータを削除する. - void remove(RowID row_id); - - - // 行の一覧を取得できるカーソルを作成して返す. - RowIDCursor *find_all(bool reverse_order); - // 指定された値が格納された行の一覧を取得できるカーソルを作成して返す. - RowIDCursor *find_equal(const Datum &datum, bool reverse_order); - // 指定された範囲の値が格納された行の一覧を取得できるカーソルを作成して返す. - RowIDCursor *find_less(const Datum &datum, bool less_equal, - bool reverse_order); - // 指定された範囲の値が格納された行の一覧を取得できるカーソルを作成して返す. - RowIDCursor *find_greater(const Datum &datum, bool greater_equal, - bool reverse_order); - // 指定された範囲の値が格納された行の一覧を取得できるカーソルを作成して返す. - RowIDCursor *find_between(const Datum &begin, const Datum &end, - bool greater_equal, bool less_equal, - bool reverse_order); - - class Cursor : public RowIDCursor { - public: - Cursor(Map::iterator begin, Map::iterator end); - - // 行 ID を最大 size 個取得して buf に格納し,取得した行 ID の数を返す. - // buf が nullptr のときは取得した行 ID をそのまま捨てる. - Int64 get_next(RowID *buf, Int64 size); - - private: - Map::iterator map_it_; - Map::iterator map_end_; - RowIDSet::iterator set_it_; - RowIDSet::iterator set_end_; - }; - - class ReverseCursor : public RowIDCursor { - public: - ReverseCursor(Map::iterator begin, Map::iterator end); - - // 行 ID を最大 size 個取得して buf に格納し,取得した行 ID の数を返す. - // buf が nullptr のときは取得した行 ID をそのまま捨てる. - Int64 get_next(RowID *buf, Int64 size); - - private: - Map::reverse_iterator map_it_; - Map::reverse_iterator map_end_; - RowIDSet::iterator set_it_; - RowIDSet::iterator set_end_; - }; - - RowIDCursor *create_cursor(typename Map::iterator begin, - typename Map::iterator end, bool reverse_order); - - private: - Map map_; - ColumnImpl<String> *column_; -}; - -// 索引を初期化する. -TreeMapIndex<String>::TreeMapIndex(IndexID index_id, - const String &index_name, - Column *column) - : Index(index_id, index_name, column, TREE_MAP), - map_(), - column_(static_cast<ColumnImpl<String> *>(column)) {} - -// 指定されたデータを登録する. -void TreeMapIndex<String>::insert(RowID row_id) { - String datum = column_->get(row_id); - std::string key(reinterpret_cast<const char *>(datum.data()), datum.size()); - map_[key].insert(row_id); -} - -// 指定されたデータを削除する. -void TreeMapIndex<String>::remove(RowID row_id) { - String datum = column_->get(row_id); - std::string key(reinterpret_cast<const char *>(datum.data()), datum.size()); - auto map_it = map_.find(key); - if (map_it == map_.end()) { - return; - } - auto &set = map_it->second; - auto set_it = set.find(row_id); - if (set_it == set.end()) { - return; - } - set.erase(set_it); - if (set.empty()) { - map_.erase(map_it); - } -} - -// 行の一覧を取得できるカーソルを作成して返す. -RowIDCursor *TreeMapIndex<String>::find_all(bool reverse_order) { - return create_cursor(map_.begin(), map_.end(), reverse_order); -} - -// 指定された値が格納された行の一覧を取得できるカーソルを作成して返す. -RowIDCursor *TreeMapIndex<String>::find_equal(const Datum &datum, - bool reverse_order) { - auto range = map_.equal_range(static_cast<std::string>(datum)); - return create_cursor(range.first, range.second, reverse_order); -} - -// 指定された範囲の値が格納された行の一覧を取得できるカーソルを作成して返す. -RowIDCursor *TreeMapIndex<String>::find_less(const Datum &datum, - bool less_equal, - bool reverse_order) { - typename Map::iterator map_end; - if (less_equal) { - map_end = map_.upper_bound(static_cast<std::string>(datum)); - } else { - map_end = map_.lower_bound(static_cast<std::string>(datum)); - } - return create_cursor(map_.begin(), map_end, reverse_order); -} - -// 指定された範囲の値が格納された行の一覧を取得できるカーソルを作成して返す. -RowIDCursor *TreeMapIndex<String>::find_greater(const Datum &datum, - bool greater_equal, - bool reverse_order) { - typename Map::iterator map_begin; - if (greater_equal) { - map_begin = map_.lower_bound(static_cast<std::string>(datum)); - } else { - map_begin = map_.upper_bound(static_cast<std::string>(datum)); - } - return create_cursor(map_begin, map_.end(), reverse_order); -} - -// 指定された範囲の値が格納された行の一覧を取得できるカーソルを作成して返す. -RowIDCursor *TreeMapIndex<String>::find_between(const Datum &begin, - const Datum &end, - bool greater_equal, - bool less_equal, - bool reverse_order) { - std::string begin_key = static_cast<std::string>(begin); - std::string end_key = static_cast<std::string>(end); - Map::iterator map_begin; - if (greater_equal) { - map_begin = map_.lower_bound(begin_key); - } else { - map_begin = map_.upper_bound(begin_key); - } - Map::iterator map_end; - if (less_equal) { - map_end = map_.upper_bound(end_key); - } else { - map_end = map_.lower_bound(end_key); - } - return create_cursor(map_begin, map_end, reverse_order); -} - -TreeMapIndex<String>::Cursor::Cursor(Map::iterator begin, Map::iterator end) - : RowIDCursor(), - map_it_(begin), - map_end_(end), - set_it_(), - set_end_() { - if (begin != end) { - set_it_ = begin->second.begin(); - set_end_ = begin->second.end(); - } -} - -// 行 ID を最大 size 個取得して buf に格納し,取得した行 ID の数を返す. -// buf が nullptr のときは取得した行 ID をそのまま捨てる. -Int64 TreeMapIndex<String>::Cursor::get_next(RowID *buf, Int64 size) { - if (map_it_ == map_end_) { - return 0; - } - Int64 count = 0; - while (count < size) { - if (set_it_ == set_end_) { - ++map_it_; - if (map_it_ == map_end_) { - return count; - } - set_it_ = map_it_->second.begin(); - set_end_ = map_it_->second.end(); - } - if (buf) { - buf[count] = *set_it_; - } - ++set_it_; - ++count; - } - return count; -} +Index::~Index() {} -TreeMapIndex<String>::ReverseCursor::ReverseCursor(Map::iterator begin, - Map::iterator end) - : RowIDCursor(), - map_it_(end), - map_end_(begin), - set_it_(), - set_end_() { - if (map_it_ != map_end_) { - set_it_ = map_it_->second.begin(); - set_end_ = map_it_->second.end(); - } +unique_ptr<Cursor> Index::create_cursor( + Error *error, + const CursorOptions &options) const { + // TODO + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supoprted yet"); + return nullptr; } -// 行 ID を最大 size 個取得して buf に格納し,取得した行 ID の数を返す. -// buf が nullptr のときは取得した行 ID をそのまま捨てる. -Int64 TreeMapIndex<String>::ReverseCursor::get_next(RowID *buf, Int64 size) { - if (map_it_ == map_end_) { - return 0; - } - Int64 count = 0; - while (count < size) { - if (set_it_ == set_end_) { - ++map_it_; - if (map_it_ == map_end_) { - return count; - } - set_it_ = map_it_->second.begin(); - set_end_ = map_it_->second.end(); - } - if (buf) { - buf[count] = *set_it_; - } - ++set_it_; - ++count; - } - return count; +unique_ptr<Index> Index::create(Error *error, + Column *column, + String name, + IndexType type, + const IndexOptions &options) { + // TODO + GRNXX_ERROR_SET(error, NOT_SUPPORTED_YET, "Not supoprted yet"); + return nullptr; } -RowIDCursor *TreeMapIndex<String>::create_cursor(typename Map::iterator begin, - typename Map::iterator end, - bool reverse_order) { - if (reverse_order) { - return new ReverseCursor(begin, end); - } else { - return new Cursor(begin, end); - } +bool Index::rename(Error *error, String new_name) { + return name_.assign(error, new_name); } -class TreeMapIndexHelper { - public: - static Index *create(IndexID index_id, - const String &index_name, - Column *column); -}; - -Index *TreeMapIndexHelper::create(IndexID index_id, - const String &index_name, - Column *column) { - switch (column->data_type()) { - case BOOLEAN: { - return new TreeMapIndex<Boolean>(index_id, index_name, column); - } - case INTEGER: { - return new TreeMapIndex<Int64>(index_id, index_name, column); - } - case FLOAT: { - return new TreeMapIndex<Float>(index_id, index_name, column); - } - case STRING: { - return new TreeMapIndex<String>(index_id, index_name, column); - } - } - return nullptr; +bool Index::is_removable() { + // TODO + return true; } -} // namespace - -// 索引を初期化する. -Index::Index(IndexID id, const String &name, Column *column, IndexType type) - : id_(id), - name_(reinterpret_cast<const char *>(name.data()), name.size()), - column_(column), - type_(type) {} - -// 索引を破棄する. -Index::~Index() {} - -// 指定された ID, 名前,対処カラム,種類の索引を作成して返す. -Index *IndexHelper::create(IndexID index_id, - const String &index_name, - Column *column, - IndexType index_type) { - switch (index_type) { - case TREE_MAP: { - return TreeMapIndexHelper::create(index_id, index_name, column); - } - } - return nullptr; -} +Index::Index() + : column_(nullptr), + name_(), + type_() {} } // namespace grnxx Deleted: lib/grnxx/index.hpp (+0 -75) 100644 =================================================================== --- lib/grnxx/index.hpp 2014-06-25 17:55:59 +0900 (5f36e61) +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef GRNXX_INDEX_HPP -#define GRNXX_INDEX_HPP - -#include "grnxx/datum.hpp" - -namespace grnxx { - -class Index { - public: - // 索引を初期化する. - Index(IndexID id, const String &name, Column *column, IndexType type); - // 索引を破棄する. - virtual ~Index(); - - // 索引の ID を返す. - IndexID id() const { - return id_; - } - // 索引の名前を返す. - String name() const { - return name_; - } - // 対象カラムを返す. - Column *column() const { - return column_; - } - // 索引の種類を返す. - IndexType type() const { - return type_; - } - - // 指定されたデータを登録する. - virtual void insert(RowID row_id) = 0; - // 指定されたデータを削除する. - virtual void remove(RowID row_id) = 0; - - // 行の一覧を取得できるカーソルを作成して返す. - virtual RowIDCursor *find_all(bool reverse_order = false) = 0; - // 指定された値が格納された行の一覧を取得できるカーソルを作成して返す. - virtual RowIDCursor *find_equal(const Datum &datum, - bool reverse_order = false) = 0; - // 指定された範囲の値が格納された行の一覧を取得できるカーソルを作成して返す. - virtual RowIDCursor *find_less(const Datum &datum, - bool less_equal = false, - bool reverse_order = false) = 0; - // 指定された範囲の値が格納された行の一覧を取得できるカーソルを作成して返す. - virtual RowIDCursor *find_greater(const Datum &datum, - bool greater_equal = false, - bool reverse_order = false) = 0; - // 指定された範囲の値が格納された行の一覧を取得できるカーソルを作成して返す. - virtual RowIDCursor *find_between(const Datum &begin, const Datum &end, - bool greater_equal = false, - bool less_equal = false, - bool reverse_order = false) = 0; - - protected: - IndexID id_; - std::string name_; - Column *column_; - IndexType type_; -}; - -class IndexHelper { - public: - // 指定された ID, 名前,対処カラム,種類の索引を作成して返す. - static Index *create(IndexID index_id, - const String &index_name, - Column *column, - IndexType index_type); -}; - -} // namespace grnxx - -#endif // GRNXX_INDEX_HPP - Modified: lib/grnxx/library.cpp (+1 -12) =================================================================== --- lib/grnxx/library.cpp 2014-06-25 17:55:59 +0900 (ed12791) +++ lib/grnxx/library.cpp 2014-07-04 12:25:36 +0900 (dee6b81) @@ -5,23 +5,12 @@ namespace grnxx { -// ライブラリの名前を返す. -const char *Library::name() { +const char *Library::package() { return PACKAGE; } -// ライブラリのバージョンを返す. const char *Library::version() { return GRNXX_VERSION; } -// 可変長整数型が有効であれば true を返し,そうでなければ false を返す. -bool Library::enable_varint() { -#ifdef GRNXX_ENABLE_VARIABLE_INTEGER_TYPE - return true; -#else // GRNXX_ENABLE_VARIABLE_INTEGER_TYPE - return false; -#endif // GRNXX_ENABLE_VARIABLE_INTEGER_TYPE -} - } // namespace grnxx Deleted: lib/grnxx/library.hpp (+0 -21) 100644 =================================================================== --- lib/grnxx/library.hpp 2014-06-25 17:55:59 +0900 (6ea31e9) +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef GRNXX_LIBRARY_HPP -#define GRNXX_LIBRARY_HPP - -namespace grnxx { - -// ライブラリ. -class Library { - public: - // ライブラリの名前を返す. - static const char *name(); - - // ライブラリのバージョンを返す. - static const char *version(); - - // 可変長整数型が有効であれば true を返し,そうでなければ false を返す. - static bool enable_varint(); -}; - -} // namespace grnxx - -#endif // GRNXX_LIBRARY_HPP Added: lib/grnxx/name.cpp (+64 -0) 100644 =================================================================== --- /dev/null +++ lib/grnxx/name.cpp 2014-07-04 12:25:36 +0900 (80fab4a) @@ -0,0 +1,64 @@ +#include "grnxx/name.hpp" + +#include "grnxx/error.hpp" + +namespace grnxx { +namespace { + +bool is_alpha(int c) { + return ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')); +} + +bool is_digit(int c) { + return (c >= '0') && (c <= '9'); +} + +bool is_allowed_symbol(int c) { + return c == '_'; +} + +} // namespace + +bool Name::assign(Error *error, String name) { + if (!test(error, name)) { + return false; + } + + std::unique_ptr<char[]> new_data(new (std::nothrow) char[name.size() + 1]); + if (!new_data) { + GRNXX_ERROR_SET(error, NO_MEMORY, + "Memory allocation failed: size = %" PRIi64, name.size()); + return false; + } + std::memcpy(new_data.get(), name.data(), name.size()); + new_data[name.size()] = '\0'; + + data_ = std::move(new_data); + size_ = name.size(); + return true; +} + +bool Name::test(Error *error, String name) { + if ((name.size() < MIN_SIZE) || (name.size() > MAX_SIZE)) { + GRNXX_ERROR_SET(error, INVALID_NAME, + "Invalid name size: size = %" PRIi64, name.size()); + return false; + } + if (!is_alpha(name[0]) && !is_digit(name[0])) { + GRNXX_ERROR_SET(error, INVALID_NAME, + "Name must start with an alphanumeric character"); + return false; + } + for (Int i = 1; i < name.size(); ++i) { + if (!is_alpha(name[i]) && + !is_digit(name[i]) && + !is_allowed_symbol(name[i])) { + GRNXX_ERROR_SET(error, INVALID_NAME, + "Name contains invalid characters"); + return false; + } + } + return true; +} + +} // namespace grnxx Added: lib/grnxx/name.hpp (+61 -0) 100644 =================================================================== --- /dev/null +++ lib/grnxx/name.hpp 2014-07-04 12:25:36 +0900 (85920e1) @@ -0,0 +1,61 @@ +#ifndef GRNXX_NAME_HPP +#define GRNXX_NAME_HPP + +#include "grnxx/error.hpp" +#include "grnxx/types.hpp" + +namespace grnxx { + +class Name { + public: + Name() : data_(nullptr), size_(0) {} + + Name(const Name &) = delete; + Name &operator=(const Name &) = delete; + + const char &operator[](Int i) const { + return data_[i]; + } + + const char *data() const { + return data_.get(); + } + Int size() const { + return size_; + } + + // Return a reference to the name string. + String ref() const { + return String(data_.get(), size_); + } + + // Assign a new name. + // Allocates memory and copies the given name to it. + // Then, terminates the copied name with a null character '\0'. + // + // A name string can contain alphabets (A-Za-z), digits (0-9), and + // underscores (_). + // + // Returns true on success. + // On failure, returns false and stores error information into "*error" if + // "error" != nullptr. + bool assign(Error *error, String name); + + private: + std::unique_ptr<char[]> data_; + Int size_; + + static constexpr Int MIN_SIZE = 1; + static constexpr Int MAX_SIZE = 1023; + + // Check if "name" is valid as an object name or not. + // + // Returns true if "name" is valid. + // Otherwise, returns false and stores error information into "*error" if + // "error" != nullptr. + static bool test(Error *error, String name); +}; + +} // namespace grnxx + +#endif // GRNXX_NAME_HPP Deleted: lib/grnxx/sorter.cpp (+0 -16) 100644 =================================================================== --- lib/grnxx/sorter.cpp 2014-06-25 17:55:59 +0900 (4e9f0a1) +++ /dev/null @@ -1,16 +0,0 @@ -#include "grnxx/sorter.hpp" - -#include "grnxx/sorter_impl.hpp" - -namespace grnxx { - -// 演算器を作成する. -Sorter *SorterHelper::create(const Table *table, const String &query) { - std::unique_ptr<SorterImpl> sorter(new SorterImpl); - if (!sorter->parse(table, query)) { - return nullptr; - } - return sorter.release(); -} - -} // namespace grnxx Deleted: lib/grnxx/sorter.hpp (+0 -29) 100644 =================================================================== --- lib/grnxx/sorter.hpp 2014-06-25 17:55:59 +0900 (392719a) +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef GRNXX_SORTER_HPP -#define GRNXX_SORTER_HPP - -#include "grnxx/types.hpp" - -namespace grnxx { - -class Sorter { - public: - // 整列器を初期化する. - Sorter() {} - // 整列器を破棄する. - virtual ~Sorter() {} - - // 与えられた行の一覧を整列する. - // 整列の結果が保証されるのは [offset, offset + limit) の範囲に限定される. - virtual void sort(RowID *row_ids, Int64 num_row_ids, - Int64 offset = 0, Int64 limit = INT64_MAX) = 0; -}; - -class SorterHelper { - public: - // 演算器を作成する. - static Sorter *create(const Table *table, const String &query); -}; - -} // namespace grnxx - -#endif // GRNXX_SORTER_HPP Deleted: lib/grnxx/sorter_impl.cpp (+0 -481) 100644 =================================================================== --- lib/grnxx/sorter_impl.cpp 2014-06-25 17:55:59 +0900 (4fe59a7) +++ /dev/null @@ -1,481 +0,0 @@ -#include "grnxx/sorter_impl.hpp" - -#include "grnxx/column_impl.hpp" -#include "grnxx/table.hpp" - -#include <ostream> - -namespace grnxx { -namespace { - -// 整列順序. -enum SortOrder { - ASCENDING, - DESCENDING -}; - -template <typename T> -class GenericSorterNode : public SorterNode { - public: - // ノードを初期化する. - GenericSorterNode(Column *column, SortOrder sort_order) - : column_(static_cast<ColumnImpl<T> *>(column)), - sort_order_(sort_order), - data_() {} - - // 与えられた行の一覧を整列する. - // 整列の結果が保証されるのは [begin, end) の範囲に限定される. - void sort(RowID *row_ids, Int64 num_row_ids, - Int64 begin, Int64 end); - - private: - ColumnImpl<T> *column_; - SortOrder sort_order_; - std::vector<T> data_; - - // 第一引数の優先度が第二引数より高ければ true を返す. - struct AscendingPriorTo { - bool operator()(const T &lhs, const T &rhs) const { - return lhs < rhs; - } - }; - struct DescendingPriorTo { - bool operator()(const T &lhs, const T &rhs) const { - return rhs < lhs; - } - }; - - // 三分岐のクイックソートをおこなう. - template <typename U> - void quick_sort(RowID *row_ids, T *values, Int64 size, - Int64 begin, Int64 end, U prior_to); - // 挿入ソートをおこなう. - template <typename U> - void insertion_sort(RowID *row_ids, T *values, Int64 size, U prior_to); - - // 枢軸となる要素を見つけ,その要素を先頭に移動する. - template <typename U> - void move_pivot_first(RowID *row_ids, T *values, Int64 size, U prior_to); -}; - -// 与えられた行の一覧を整列する. -// 整列の結果が保証されるのは [begin, end) の範囲に限定される. -template <typename T> -void GenericSorterNode<T>::sort(RowID *row_ids, Int64 num_row_ids, - Int64 begin, Int64 end) { - // 整列に使うデータを取ってくる. - data_.resize(num_row_ids); - for (Int64 i = 0; i < num_row_ids; ++i) { - data_[i] = column_->get(row_ids[i]); - } - if (sort_order_ == ASCENDING) { - quick_sort(row_ids, &*data_.begin(), num_row_ids, - begin, end, AscendingPriorTo()); - } else { - quick_sort(row_ids, &*data_.begin(), num_row_ids, - begin, end, DescendingPriorTo()); - } -} - -// 三分岐のクイックソートをおこなう. -template <typename T> -template <typename U> -void GenericSorterNode<T>::quick_sort(RowID *row_ids, T *values, Int64 size, - Int64 begin, Int64 end, U prior_to) { - while (size >= 16) { - move_pivot_first(row_ids, values, size, prior_to); - const auto &pivot = values[0]; - Int64 left = 1; - Int64 right = size; - Int64 pivot_left = 1; - Int64 pivot_right = size; - for ( ; ; ) { - // 枢軸を基準として左右へと分ける. - // 枢軸と等しい要素は左右端へと移動する. - while (left < right) { - if (prior_to(pivot, values[left])) { - break; - } else if (pivot == values[left]) { - std::swap(values[left], values[pivot_left]); - std::swap(row_ids[left], row_ids[pivot_left]); - ++pivot_left; - } - ++left; - } - while (left < right) { - --right; - if (prior_to(values[right], pivot)) { - break; - } else if (values[right] == pivot) { - --pivot_right; - std::swap(values[right], values[pivot_right]); - std::swap(row_ids[right], row_ids[pivot_right]); - } - } - if (left >= right) { - break; - } - std::swap(values[left], values[right]); - std::swap(row_ids[left], row_ids[right]); - ++left; - } - - // 左端の値を境界の左に移動する. - while (pivot_left > 0) { - --pivot_left; - --left; - std::swap(values[pivot_left], values[left]); - std::swap(row_ids[pivot_left], row_ids[left]); - } - // 右端の値を境界の右に移動する. - while (pivot_right < size) { - std::swap(values[pivot_right], values[right]); - std::swap(row_ids[pivot_right], row_ids[right]); - ++pivot_right; - ++right; - } - - // 枢軸と同値の範囲 [left, right) には次の検索条件を適用する. - if (next()) { - if (((right - left) >= 2) && (begin < right) && (end > left)) { - Int64 next_begin = (begin < left) ? 0 : (begin - left); - Int64 next_end = ((end > right) ? right : end) - left; - next()->sort(row_ids + left, right - left, next_begin, next_end); - } - } - - // 再帰呼び出しの深さを抑えるため,左側 [0, left) と右側 [right, size) の - // 小さい方を再帰呼び出しで処理し,大きい方を次のループで処理する. - if (left < (size - right)) { - if ((begin < left) && (left >= 2)) { - Int64 next_end = (end < left) ? end : left; - quick_sort(row_ids, values, left, begin, next_end, prior_to); - } - if (end <= right) { - return; - } - row_ids += right; - values += right; - size -= right; - begin -= right; - if (begin < 0) { - begin = 0; - } - end -= right; - } else { - if ((end > right) && ((size - right) >= 2)) { - Int64 next_begin = (begin < right) ? 0 : (begin - right); - Int64 next_end = end - right; - quick_sort(row_ids + right, values + right, size - right, - next_begin, next_end, prior_to); - } - if (begin >= left) { - return; - } - size = left; - if (end > left) { - end = left; - } - } - } - - if (size >= 2) { - insertion_sort(row_ids, values, size, prior_to); - } -} - -// 挿入ソートをおこなう. -template <typename T> -template <typename U> -void GenericSorterNode<T>::insertion_sort(RowID *row_ids, T *values, - Int64 size, U prior_to) { - // まずは挿入ソートをおこなう. - for (Int64 i = 1; i < size; ++i) { - for (Int64 j = i; j > 0; --j) { - if (prior_to(values[j], values[j - 1])) { - std::swap(row_ids[j], row_ids[j - 1]); - std::swap(values[j], values[j - 1]); - } else { - break; - } - } - } - - // 同値の範囲には次の検索条件を適用する. - if (next()) { - Int64 begin = 0; - for (Int64 i = 1; i < size; ++i) { - if (values[i] != values[begin]) { - if ((i - begin) >= 2) { - next()->sort(row_ids + begin, i - begin, 0, i - begin); - } - begin = i; - } - } - if ((size - begin) >= 2) { - next()->sort(row_ids + begin, size - begin, 0, size - begin); - } - } -} - -// 枢軸となる要素を見つけ,その要素を先頭に移動する. -template <typename T> -template <typename U> -void GenericSorterNode<T>::move_pivot_first(RowID *row_ids, T *values, - Int64 size, U prior_to) { - RowID first = 1; - RowID middle = size / 2; - RowID last = size - 2; - if (prior_to(values[first], values[middle])) { - // first < middle. - if (prior_to(values[middle], values[last])) { - // first < middle < last. - std::swap(values[0], values[middle]); - std::swap(row_ids[0], row_ids[middle]); - } else if (prior_to(values[first], values[last])) { - // first < last < middle. - std::swap(values[0], values[last]); - std::swap(row_ids[0], row_ids[last]); - } else { - // last < first < middle. - std::swap(values[0], values[first]); - std::swap(row_ids[0], row_ids[first]); - } - } else if (prior_to(values[last], values[middle])) { - // last < middle < first. - std::swap(values[0], values[middle]); - std::swap(row_ids[0], row_ids[middle]); - } else if (prior_to(values[last], values[first])) { - // middle < last < first. - std::swap(values[0], values[last]); - std::swap(row_ids[0], row_ids[last]); - } else { - // middle < first < last. - std::swap(values[0], values[first]); - std::swap(row_ids[0], row_ids[first]); - } -} - -class BooleanSorterNode : public SorterNode { - public: - // ノードを初期化する. - BooleanSorterNode(Column *column, SortOrder sort_order) - : SorterNode(), - column_(static_cast<ColumnImpl<Boolean> *>(column)), - sort_order_(sort_order) {} - - // 与えられた行の一覧を整列する. - // 整列の結果が保証されるのは [begin, end) の範囲に限定される. - void sort(RowID *row_ids, Int64 num_row_ids, - Int64 begin, Int64 end); - - private: - ColumnImpl<Boolean> *column_; - SortOrder sort_order_; - - // 昇順のときは TRUE を後ろに移動する. - struct AscendingIsPrior { - bool operator()(Boolean value) const { - return !value; - } - }; - // 降順のときは FALSE を後ろに移動する. - struct DescendingIsPrior { - bool operator()(Boolean value) const { - return value; - } - }; - - // 行 ID の一覧を全域ソートする. - template <typename T> - void entire_sort(RowID *row_ids, Int64 num_row_ids, - Int64 begin, Int64 end, T is_prior); -}; - -// 与えられた行の一覧を整列する. -// 整列の結果が保証されるのは [begin, end) の範囲に限定される. -void BooleanSorterNode::sort(RowID *row_ids, Int64 num_row_ids, - Int64 begin, Int64 end) { - if (sort_order_ == ASCENDING) { - entire_sort(row_ids, num_row_ids, begin, end, AscendingIsPrior()); - } else { - entire_sort(row_ids, num_row_ids, begin, end, DescendingIsPrior()); - } -} - -// 行 ID の一覧を全域ソートする. -template <typename T> -void BooleanSorterNode::entire_sort(RowID *row_ids, Int64 num_row_ids, - Int64 begin, Int64 end, T is_prior) { - // 優先する値を左に,そうでない値を右に移動する. - Int64 left = 0; - Int64 right = num_row_ids; - while (left < right) { - auto value = column_->get(row_ids[left]); - if (is_prior(value)) { - ++left; - } else { - --right; - std::swap(row_ids[left], row_ids[right]); - } - } - // この時点で left, right は等しく,左右の境界になっている. - if (next()) { - // 左右に 2 つ以上の行があり,整列範囲と重なっていれば,次の整列条件を適用する. - if ((left >= 2) && (begin < left)) { - next()->sort(row_ids, left, begin, (end < left) ? end : left); - } - if (((num_row_ids - left) >= 2) && (end > left)) { - if (begin < left) { - begin = 0; - } else { - begin -= left; - } - end -= left; - next()->sort(row_ids + left, num_row_ids - left, begin, end); - } - } -} - -// 行 ID の大小関係にしたがって整列する. -class RowIDSorterNode : public SorterNode { - public: - // ノードを初期化する. - explicit RowIDSorterNode(SortOrder sort_order) : sort_order_(sort_order) {} - - // 与えられた行の一覧を整列する. - // 整列の結果が保証されるのは [begin, end) の範囲に限定される. - void sort(RowID *row_ids, Int64 num_row_ids, - Int64 begin, Int64 end); - - private: - SortOrder sort_order_; -}; - -// 与えられた行の一覧を整列する. -// 整列の結果が保証されるのは [begin, end) の範囲に限定される. -void RowIDSorterNode::sort(RowID *row_ids, Int64 num_row_ids, - Int64 begin, Int64 end) { - // 整列対象が多くて整列範囲が狭いときは部分ソートを使い,それ以外のときは全域を整列する. - // FIXME: 適当に設定したので,将来的には最適な設定を求めるべきである. - // FIXME: 行 ID に重複がないという前提になっているため,次の整列条件があっても関係ない. - if ((num_row_ids >= 1000) && (end < 100)) { - if (sort_order_ == ASCENDING) { - std::partial_sort(row_ids, row_ids + end, - row_ids + num_row_ids, std::less<RowID>()); - } else { - std::partial_sort(row_ids, row_ids + end, - row_ids + num_row_ids, std::greater<RowID>()); - } - } else { - if (sort_order_ == ASCENDING) { - std::sort(row_ids, row_ids + num_row_ids, std::less<RowID>()); - } else { - std::sort(row_ids, row_ids + num_row_ids, std::greater<RowID>()); - } - } -} - -} // namespace - -// 整列器を初期化する. -SorterImpl::SorterImpl() - : Sorter(), - table_(nullptr), - nodes_() {} - -// 整列器を破棄する. -SorterImpl::~SorterImpl() {} - -// 指定された文字列に対応する整列器を作成する. -// 文字列には,コンマ区切りでカラムを指定することができる. -// カラム名の先頭に '-' を付けると降順になる. -bool SorterImpl::parse(const Table *table, String query) { - table_ = table; - nodes_.clear(); - while (!query.empty()) { - Int64 delim_pos = query.find_first_of(','); - String column_name; - if (delim_pos == query.npos) { - column_name = query; - query = ""; - } else { - column_name = query.prefix(delim_pos); - query = query.except_prefix(delim_pos + 1); - } - if (!append_column(column_name)) { - return false; - } - } - return true; -} - -// 与えられた行の一覧を整列する. -// 整列の結果が保証されるのは [offset, offset + limit) の範囲に限定される. -void SorterImpl::sort(RowID *row_ids, Int64 num_row_ids, - Int64 offset, Int64 limit) { - // 整列条件が存在しなければ何もしない. - if (nodes_.empty()) { - return; - } - // 整列対象が存在しなければ何もしない. - if ((num_row_ids <= 1) || (offset >= num_row_ids) || (limit <= 0)) { - return; - } - // 整列範囲を補正する. - if (offset < 0) { - offset = 0; - } - if (limit > (num_row_ids - offset)) { - limit = num_row_ids - offset; - } - nodes_.front()->sort(row_ids, num_row_ids, offset, offset + limit); -} - -// 指定された名前のカラムを整列条件に加える. -// 先頭に '-' を付けると降順になる. -// 成功すれば true を返し,失敗すれば false を返す. -bool SorterImpl::append_column(String column_name) { - Column *column = nullptr; - SortOrder sort_order = ASCENDING; - if (column_name.starts_with("-")) { - column_name = column_name.except_prefix(1); - sort_order = DESCENDING; - } - if (column_name != "_id") { - column = table_->get_column_by_name(column_name); - if (!column) { - return false; - } - } - std::unique_ptr<SorterNode> node; - if (column) { - switch (column->data_type()) { - case BOOLEAN: { - node.reset(new BooleanSorterNode(column, sort_order)); - break; - } - case INTEGER: { - node.reset(new GenericSorterNode<Int64>(column, sort_order)); - break; - } - case FLOAT: { - node.reset(new GenericSorterNode<Float>(column, sort_order)); - break; - } - case STRING: { - node.reset(new GenericSorterNode<String>(column, sort_order)); - break; - } - } - } else { - node.reset(new RowIDSorterNode(sort_order)); - } - if (!nodes_.empty()) { - nodes_.back()->set_next(node.get()); - } - nodes_.push_back(std::move(node)); - return true; -} - -} // namespace grnxx Deleted: lib/grnxx/sorter_impl.hpp (+0 -66) 100644 =================================================================== --- lib/grnxx/sorter_impl.hpp 2014-06-25 17:55:59 +0900 (aa8ca3a) +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef GRNXX_SORTER_IMPL_HPP -#define GRNXX_SORTER_IMPL_HPP - -#include "grnxx/sorter.hpp" -#include "grnxx/string.hpp" - -namespace grnxx { - -// 整列器を構成するノード. -// 整列条件のひとつと対応する. -class SorterNode { - public: - // ノードを初期化する. - SorterNode() : next_(nullptr) {} - // ノードを破棄する. - virtual ~SorterNode() {} - - // 次のノードを返す. - SorterNode *next() { - return next_; - } - // 次のノードを設定する. - void set_next(SorterNode *next) { - next_ = next; - } - - // 与えられた行の一覧を整列する. - // 整列の結果が保証されるのは [begin, end) の範囲に限定される. - virtual void sort(RowID *row_ids, Int64 num_row_ids, - Int64 begin, Int64 end) = 0; - - private: - SorterNode *next_; -}; - -// 整列器. -class SorterImpl : public Sorter { - public: - // 整列器を初期化する. - SorterImpl(); - // 整列器を破棄する. - ~SorterImpl(); - - // 指定された文字列に対応する整列器を作成する. - // 文字列には,コンマ区切りでカラムを指定することができる. - // カラム名の先頭に '-' を付けると降順になる. - bool parse(const Table *table, String query); - - // 与えられた行の一覧を整列する. - // 整列の結果が保証されるのは [offset, offset + limit) の範囲に限定される. - void sort(RowID *row_ids, Int64 num_row_ids, - Int64 offset = 0, Int64 limit = -1); - - protected: - const Table *table_; - std::vector<std::unique_ptr<SorterNode>> nodes_; - - // 指定された名前のカラムを整列条件に加える. - // 先頭に '-' を付けると降順になる. - // 成功すれば true を返し,失敗すれば false を返す. - bool append_column(String column_name); -}; - -} // namespace grnxx - -#endif // GRNXX_SORTER_IMPL_HPP Deleted: lib/grnxx/string.cpp (+0 -12) 100644 =================================================================== --- lib/grnxx/string.cpp 2014-06-25 17:55:59 +0900 (d916ba3) +++ /dev/null @@ -1,12 +0,0 @@ -#include "grnxx/string.hpp" - -#include <ostream> - -namespace grnxx { - -std::ostream &operator<<(std::ostream &stream, const String &string) { - stream.write(reinterpret_cast<const char *>(string.data()), string.size()); - return stream; -} - -} // namespace grnxx Deleted: lib/grnxx/string.hpp (+0 -181) 100644 =================================================================== --- lib/grnxx/string.hpp 2014-06-25 17:55:59 +0900 (2fc9597) +++ /dev/null @@ -1,181 +0,0 @@ -#ifndef GRNXX_STRING_HPP -#define GRNXX_STRING_HPP - -#include "grnxx/types.hpp" - -namespace grnxx { - -// 文字列. -class String { - public: - // 明示的な初期化はしない. - String() = default; - // 空文字列として初期化する. - String(std::nullptr_t) : data_(nullptr), size_(0) {} - // NULL 文字を終端とする文字列を参照するように初期化する. - String(const char *str) - : data_(reinterpret_cast<const UInt8 *>(str)), - size_(std::strlen(str)) {} - // 文字列を参照するように初期化する. - String(const std::string &str) - : data_(reinterpret_cast<const UInt8 *>(str.data())), - size_(str.size()) {} - // 指定されたバイト列を参照するように初期化する. - String(const void *data, Int64 size) - : data_(static_cast<const UInt8 *>(data)), - size_(size) {} - - // 先頭の n bytes に対するオブジェクトを作成する. - String prefix(Int64 n) const { - return String(data_, n); - } - // 末尾の n bytes に対するオブジェクトを作成する. - String suffix(Int64 n) const { - return String(data_ + size_ - n, n); - } - - // 先頭の n bytes を取り除いた部分列に対するオブジェクトを作成する. - String except_prefix(Int64 n) const { - return String(data_ + n, size_ - n); - } - // 末尾の n bytes を取り除いた部分列に対するオブジェクトを作成する. - String except_suffix(Int64 n) const { - return String(data_, size_ - n); - } - - // 先頭の n bytes を飛ばした後,残りの先頭 m bytes に対するオブジェクトを作成する. - String extract(Int64 n, Int64 m) const { - return String(data_ + n, m); - } - // 先頭の n bytes と末尾の n bytes を取り除いた部分列に対するオブジェクトを作成する. - String trim(Int64 n, Int64 m) const { - return String(data_ + n, size_ - n - m); - } - - // 文字列同士を比較する. - bool operator==(const String &rhs) const { - return (size_ == rhs.size_) && - (std::memcmp(data_, rhs.data_, size_) == 0); - } - bool operator!=(const String &rhs) const { - return !(*this == rhs); - } - bool operator<(const String &rhs) const { - const Int64 min_size = (size_ < rhs.size_) ? size_ : rhs.size_; - int result = std::memcmp(data_, rhs.data_, min_size); - return (result < 0) || ((result == 0) && (size_ < rhs.size_)); - } - bool operator>(const String &rhs) const { - return rhs < *this; - } - bool operator<=(const String &rhs) const { - return !(*this > rhs); - } - bool operator>=(const String &rhs) const { - return !(*this < rhs); - } - - // 指定された文字列と比較して,小さければ負の値を返し,等しければ 0 を返し, - // 大きければ正の値を返す. - int compare(const String &bytes) const { - const Int64 min_size = (size_ < bytes.size_) ? size_ : bytes.size_; - int result = std::memcmp(data_, bytes.data_, min_size); - if (result != 0) { - return result; - } - return (size_ < bytes.size_) ? -1 : (size_ > bytes.size_); - } - - // 指定された文字列で始まっていれば true を返し,そうでなければ false を返す. - bool starts_with(const String &bytes) const { - return (size_ >= bytes.size_) && (prefix(bytes.size_) == bytes); - } - // 指定された文字列で終わっていれば true を返し,そうでなければ false を返す. - bool ends_with(const String &bytes) const { - return (size_ >= bytes.size_) && (suffix(bytes.size_) == bytes); - } - - // 指定された文字が最初に出現する位置を返す. - // 先頭の offset bytes は無視する. - // 出現しないときは npos を返す. - Int64 find_first_of(UInt8 byte, Int64 offset = 0) const { - for (Int64 i = offset; i < size_; ++i) { - if (data_[i] == byte) { - return i; - } - } - return npos; - } - // 指定された文字列に含まれる文字が最初に出現する位置を返す. - // 先頭の offset bytes は無視する. - // 出現しないときは npos を返す. - Int64 find_first_of(const String &str, Int64 offset = 0) const { - for (Int64 i = offset; i < size_; ++i) { - if (str.find_first_of(data_[i]) != npos) { - return i; - } - } - return npos; - } - // 指定された文字が最後に出現する位置を返す. - // 先頭の offset bytes は無視する. - // 出現しないときは npos を返す. - Int64 find_last_of(UInt8 byte, Int64 offset = 0) const { - for (Int64 i = size_; i > offset; ) { - if (data_[--i] == byte) { - return i; - } - } - return npos; - } - - // 指定された文字列に含まれない文字が最初に出現する位置を返す. - // 出現しないときは npos を返す. - Int64 find_first_not_of(const String &str, Int64 offset = 0) const { - for (Int64 i = offset; i < size_; ++i) { - if (str.find_first_of(data_[i]) == npos) { - return i; - } - } - return npos; - } - // 指定された文字列に含まれない文字が最後に出現する位置を返す. - // 出現しないときは npos を返す. - Int64 find_last_not_of(const String &str, Int64 offset = 0) const { - for (Int64 i = size_; i > offset; ) { - if (str.find_first_of(data_[--i]) == npos) { - return i; - } - } - return npos; - } - - // 文字(バイト)を返す. - UInt8 operator[](Int64 i) const { - return data_[i]; - } - // 開始アドレスを返す. - const UInt8 *data() const { - return data_; - } - // 長さをバイト単位で返す. - Int64 size() const { - return size_; - } - // 長さが 0 であれば true を返し,そうでなければ false を返す. - bool empty() const { - return size_ == 0; - } - - static constexpr Int64 npos = -1; - - private: - const UInt8 *data_; - Int64 size_; -}; - -std::ostream &operator<<(std::ostream &stream, const String &string); - -} // namespace grnxx - -#endif // GRNXX_STRING_HPP Modified: lib/grnxx/table.cpp (+222 -482) =================================================================== --- lib/grnxx/table.cpp 2014-06-25 17:55:59 +0900 (d230fb2) +++ lib/grnxx/table.cpp 2014-07-04 12:25:36 +0900 (16e26f2) @@ -1,568 +1,308 @@ #include "grnxx/table.hpp" -#include "grnxx/calc.hpp" -#include "grnxx/column_impl.hpp" -#include "grnxx/database.hpp" -#include "grnxx/index.hpp" -#include "grnxx/sorter.hpp" - -#include <ostream> - -#include <iostream> // For debugging. +#include "grnxx/column.hpp" +#include "grnxx/cursor.hpp" +#include "grnxx/db.hpp" +#include "grnxx/error.hpp" namespace grnxx { -namespace { -// カンマ区切りの文字列をばらす. -std::vector<String> split_column_names( - const String &comma_separated_column_names) { - std::vector<String> column_names; - if (comma_separated_column_names.empty()) { - return column_names; - } - Int64 begin = 0; - while (begin < comma_separated_column_names.size()) { - auto end = comma_separated_column_names.find_first_of(',', begin); - if (end == comma_separated_column_names.npos) { - end = comma_separated_column_names.size(); - } - column_names.push_back( - comma_separated_column_names.extract(begin, end - begin)); - begin = end + 1; - } - return column_names; -} - -// FIXME: これのためだけに column_impl.hpp を #include している状態なので, -// ColumnImpl に持たせるべきなのかもしれない. -// 基準となるカラムの値が変化する位置を求める. -template <typename T> -void find_boundaries(const RowID *row_ids, Int64 num_row_ids, - const Column *column, - const std::vector<Int64> &boundaries, - std::vector<Int64> *new_boundaries) { - // あらかじめ十分なサイズの仮想領域を確保することで,拡張にかかるコストをなくす. - new_boundaries->reserve(num_row_ids); - auto data = static_cast<const ColumnImpl<T> *>(column); - Int64 begin = 0; - for (auto end : boundaries) { - for (auto i = begin + 1; i < end; ++i) { - if (data->get(row_ids[i - 1]) != data->get(row_ids[i])) { - new_boundaries->push_back(i); - } - } - new_boundaries->push_back(end); - begin = end; - } -} - -} // namespace - -// テーブルを初期化する. -Table::Table(Database *database, TableID id, const String &name) - : database_(database), - id_(id), - name_(reinterpret_cast<const char *>(name.data()), name.size()), - max_row_id_(MIN_ROW_ID - 1), - primary_key_column_(nullptr), - columns_(MIN_COLUMN_ID), - columns_map_(), - indexes_(MIN_INDEX_ID), - indexes_map_() {} - -// テーブルを破棄する. Table::~Table() {} -// 指定された名前とデータ型のカラムを作成して返す. -// 失敗すると nullptr を返す. -Column *Table::create_column(const String &column_name, DataType data_type) { - auto it = columns_map_.find(column_name); - if (it != columns_map_.end()) { +Column *Table::create_column(Error *error, + String name, + DataType data_type, + const ColumnOptions &options) { + if (find_column(nullptr, name)) { + GRNXX_ERROR_SET(error, ALREADY_EXISTS, + "Column already exists: name = \"%.*s\"", + static_cast<int>(name.size()), name.data()); return nullptr; } - ColumnID column_id = min_column_id(); - for ( ; column_id <= max_column_id(); ++column_id) { - if (!columns_[column_id]) { - break; - } - } - if (column_id > max_column_id()) { - columns_.resize(column_id + 1); - } - std::unique_ptr<Column> new_column( - ColumnHelper::create_column(this, column_id, column_name, data_type)); - new_column->resize(max_row_id()); - columns_map_.insert(it, std::make_pair(new_column->name(), column_id)); - columns_[column_id] = std::move(new_column); - return columns_[column_id].get(); -} - -// 参照型のカラムを作成して返す. -// 失敗すると nullptr を返す. -Column *Table::create_reference_column(const String &column_name, - const String &table_name) { - Table *dest_table = database_->get_table_by_name(table_name); - if (!dest_table) { + try { + columns_.reserve(columns_.size() + 1); + } catch (...) { + GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed"); return nullptr; } - auto it = columns_map_.find(column_name); - if (it != columns_map_.end()) { + unique_ptr<Column> new_column = + Column::create(error, this, name, data_type, options); + if (!new_column) { return nullptr; } - ColumnID column_id = min_column_id(); - for ( ; column_id <= max_column_id(); ++column_id) { - if (!columns_[column_id]) { - break; - } - } - if (column_id > max_column_id()) { - columns_.resize(column_id + 1); - } - std::unique_ptr<Column> new_column( - ColumnHelper::create_reference_column(this, column_id, column_name, - dest_table)); - new_column->resize(max_row_id()); - columns_map_.insert(it, std::make_pair(new_column->name(), column_id)); - columns_[column_id] = std::move(new_column); - return columns_[column_id].get(); + columns_.push_back(std::move(new_column)); + return columns_.back().get(); } -// 指定された名前のカラムを破棄する. -// 成功すれば true を返し,失敗すれば false を返す. -bool Table::drop_column(const String &column_name) { - auto it = columns_map_.find(column_name); - if (it == columns_map_.end()) { +bool Table::remove_column(Error *error, String name) { + size_t column_id; + if (!find_column_with_id(error, name, &column_id)) { return false; } - auto &column = columns_[it->second]; - for (auto &index : indexes_) { - if (index) { - if (index->column() == column.get()) { - drop_index(index->name()); - } - } + if (!columns_[column_id]->is_removable()) { + GRNXX_ERROR_SET(error, NOT_REMOVABLE, + "Column is not removable: name = \"%.*s\"", + static_cast<int>(name.size()), name.data()); + return false; } - column.reset(); - columns_map_.erase(it); + columns_.erase(columns_.begin() + column_id); return true; } -// 指定されたカラムに主キー属性を与える. -// 成功すれば true を返し,失敗すれば false を返す. -bool Table::set_primary_key(const String &column_name) { - if (primary_key_column_) { +bool Table::rename_column(Error *error, + String name, + String new_name) { + size_t column_id; + if (!find_column_with_id(error, name, &column_id)) { return false; } - auto it = columns_map_.find(column_name); - if (it == columns_map_.end()) { - return false; + if (name == new_name) { + return true; } - auto column = columns_[it->second].get(); - if (!column->set_unique()) { + if (find_column(nullptr, new_name)) { + GRNXX_ERROR_SET(error, ALREADY_EXISTS, + "Column already exists: new_name = \"%.*s\"", + static_cast<int>(new_name.size()), new_name.data()); return false; } - primary_key_column_ = column; - return true; + return columns_[column_id]->rename(error, new_name); } -// 主キー属性を解除する. -// 成功すれば true を返し,失敗すれば false を返す. -bool Table::unset_primary_key() { - if (!primary_key_column_) { +bool Table::reorder_column(Error *error, + String name, + String prev_name) { + size_t column_id; + if (!find_column_with_id(error, name, &column_id)) { return false; } - if (!primary_key_column_->unset_unique()) { - return false; + size_t new_column_id = 0; + if (prev_name.size() != 0) { + size_t prev_column_id; + if (!find_column_with_id(error, prev_name, &prev_column_id)) { + return false; + } + if (column_id <= prev_column_id) { + new_column_id = prev_column_id; + } else { + new_column_id = prev_column_id + 1; + } + } + for ( ; column_id < new_column_id; ++column_id) { + std::swap(columns_[column_id], columns_[column_id + 1]); + } + for ( ; column_id > new_column_id; --column_id) { + std::swap(columns_[column_id], columns_[column_id - 1]); } - primary_key_column_ = nullptr; return true; } -// 指定された 名前 のカラムを返す. -// なければ nullptr を返す. -Column *Table::get_column_by_name(const String &column_name) const { - auto it = columns_map_.find(column_name); - if (it == columns_map_.end()) { +Column *Table::get_column(Error *error, size_t column_id) const { + if (column_id >= num_columns()) { + GRNXX_ERROR_SET(error, NOT_FOUND, + "Column not found: column_id = %" PRIi64, column_id); return nullptr; } - return columns_[it->second].get(); + return columns_[column_id].get(); } -// カラムの一覧を columns の末尾に追加し,カラムの数を返す. -Int64 Table::get_columns(std::vector<Column *> *columns) const { - std::size_t old_size = columns->size(); - for (auto &column : columns_) { - if (column) { - columns->push_back(column.get()); +Column *Table::find_column(Error *error, String name) const { + for (size_t column_id = 0; column_id < num_columns(); ++column_id) { + if (name == columns_[column_id]->name()) { + return columns_[column_id].get(); } } - return columns->size() - old_size; + GRNXX_ERROR_SET(error, NOT_FOUND, "Column not found: name = \"%.*s\"", + static_cast<int>(name.size()), name.data()); + return nullptr; } -// 指定された名前,対象カラム,種類の索引を作成して返す. -// 失敗すると nullptr を返す. -Index *Table::create_index(const String &index_name, const String &column_name, - IndexType index_type) { - Column *column = get_column_by_name(column_name); - if (!column) { - return nullptr; +bool Table::set_key_column(Error *error, String name) { + if (key_column_) { + GRNXX_ERROR_SET(error, ALREADY_EXISTS, "Key column already exists"); + return false; } - auto it = indexes_map_.find(index_name); - if (it != indexes_map_.end()) { - return nullptr; + // TODO: Key column is not supported yet. + return false; +} + +bool Table::unset_key_column(Error *error) { + if (!key_column_) { + GRNXX_ERROR_SET(error, NOT_FOUND, "Key column not found"); + return false; } - IndexID index_id = min_index_id(); - for ( ; index_id <= max_index_id(); ++index_id) { - if (!indexes_[index_id]) { - break; + // TODO: Key column is not supported yet. + return false; +} + +bool Table::insert_row(Error *error, + Int request_row_id, + const Datum &key, + Int *result_row_id) { + *result_row_id = NULL_ROW_ID; + Int next_row_id; + if (request_row_id != NULL_ROW_ID) { + if (request_row_id > (max_row_id() + 1)) { + GRNXX_ERROR_SET(error, INVALID_ARGUMENT, + "Invalid argument: request_row_id = %" PRIi64, + request_row_id); + return false; + } + if (get_bit(request_row_id)) { + *result_row_id = request_row_id; + GRNXX_ERROR_SET(error, ALREADY_EXISTS, + "Row already exists: request_row_id = %" PRIi64, + request_row_id); + return false; } + next_row_id = request_row_id; + } else { + // TODO: Removed rows should be reused. + next_row_id = max_row_id() + 1; } - if (index_id > max_index_id()) { - indexes_.resize(index_id + 1); + if (key_column()) { + *result_row_id = find_row(nullptr, key); + if (*result_row_id != NULL_ROW_ID) { + GRNXX_ERROR_SET(error, ALREADY_EXISTS, "Key already exists"); + return false; + } } - std::unique_ptr<Index> new_index( - IndexHelper::create(index_id, index_name, column, index_type)); - if (!column->register_index(new_index.get())) { - return nullptr; + if (!reserve_bit(error, next_row_id)) { + return false; } - for (RowID row_id = min_row_id(); row_id <= max_row_id(); ++row_id) { - new_index->insert(row_id); + if (key_column()) { + if (!key_column_->set_initial_key(error, next_row_id, key)) { + return false; + } + } + // TODO: Fill other column values with the default values. + if (next_row_id > max_row_id()) { + for (size_t column_id = 0; column_id < num_columns(); ++column_id) { + if (columns_[column_id].get() != key_column()) { + if (!columns_[column_id]->set_default_value(error, next_row_id)) { + // Rollback the insertion. + if (key_column()) { + key_column_->unset(next_row_id); + } + for (size_t i = 0; i < column_id; ++i) { + if (columns_[i].get() != key_column()) { + columns_[i]->unset(next_row_id); + } + } + return false; + } + } + } + max_row_id_ = next_row_id; } - indexes_map_.insert(it, std::make_pair(new_index->name(), index_id)); - indexes_[index_id] = std::move(new_index); - return indexes_[index_id].get(); + set_bit(next_row_id); + *result_row_id = next_row_id; + return true; } -// 指定された名前の索引を破棄する. -// 成功すれば true を返し,失敗すれば false を返す. -bool Table::drop_index(const String &index_name) { - auto it = indexes_map_.find(index_name); - if (it == indexes_map_.end()) { +bool Table::remove_row(Error *error, Int row_id) { + if (!test_row(error, row_id)) { return false; } - auto &index = indexes_[it->second]; - if (!index->column()->unregister_index(index.get())) { - return false; + // TODO: Check removability and unset column values. + for (size_t column_id = 0; column_id < num_columns(); ++column_id) { + columns_[column_id]->unset(row_id); } - index.reset(); - indexes_map_.erase(it); + unset_bit(row_id); return true; } -// 指定された名前の索引を返す. -// なければ nullptr を返す. -Index *Table::get_index_by_name(const String &index_name) const { - auto it = indexes_map_.find(index_name); - if (it == indexes_map_.end()) { - return nullptr; +bool Table::test_row(Error *error, Int row_id) const { + if ((row_id < MIN_ROW_ID) || (row_id > max_row_id())) { + GRNXX_ERROR_SET(error, INVALID_ARGUMENT, "Invalid row ID"); + return false; } - return indexes_[it->second].get(); -} - -// 索引の一覧を indexes の末尾に追加し,索引の数を返す. -Int64 Table::get_indexes(std::vector<Index *> *indexes) const { - std::size_t old_size = indexes->size(); - for (auto &index : indexes_) { - if (index) { - indexes->push_back(index.get()); - } + if (!get_bit(row_id)) { + GRNXX_ERROR_SET(error, NOT_FOUND, "Removed row"); + return false; } - return indexes->size() - old_size; + return true; } -// 行を追加して,その行 ID を返す. -RowID Table::insert_row() { - RowID row_id = max_row_id() + 1; - for (auto &column : columns_) { - if (column) { - column->resize(row_id); - } +Int Table::find_row(Error *error, const Datum &key) const { + if (!key_column_) { + GRNXX_ERROR_SET(error, NO_KEY_COLUMN, "No key column"); + return NULL_ROW_ID; } - max_row_id_ = row_id; - return row_id; + // TODO: Key column is not supported yet. + return NULL_ROW_ID; } -// 指定されたテーブルの範囲内にある行 ID を取得するようにカーソルを初期化する. -Table::Cursor::Cursor(RowID range_min, RowID range_max) - : RowIDCursor(), - row_id_(range_min), - max_row_id_(range_max) {} - -// 行 ID を最大 size 個取得して buf に格納し,取得した行 ID の数を返す. -// buf が nullptr のときは取得した行 ID をそのまま捨てる. -Int64 Table::Cursor::get_next(RowID *buf, Int64 size) { - if (buf) { - Int64 count = 0; - while ((count < size) && (row_id_ <= max_row_id_)) { - buf[count] = row_id_; - ++row_id_; - ++count; - } - return count; - } else { - Int64 count; - if (size > (max_row_id_ - row_id_ + 1)) { - count = max_row_id_ - row_id_ + 1; - } else { - count = size; - } - row_id_ += count; - return count; - } +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; } -// 指定された範囲の行 ID を取得するカーソルを作成して返す. -// 範囲が省略された,もしくは 0 を指定されたときは, -// 行 ID の最小値・最大値を範囲として使用する. -Table::Cursor *Table::create_cursor(RowID range_min, RowID range_max) const { - if (range_min < min_row_id()) { - range_min = min_row_id(); +unique_ptr<Table> Table::create(Error *error, + DB *db, + String name, + const TableOptions &options) { + std::unique_ptr<Table> table(new Table); + table->db_ = db; + if (!table->name_.assign(error, name)) { + return nullptr; } - if (range_max > max_row_id()) { - range_max = max_row_id(); + try { + // Disable NULL_ROW_ID. + table->bitmap_.push_back(~uint64_t(0) ^ 1); + } catch (...) { + GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed"); + return nullptr; } - return new Cursor(range_min, range_max); -} - -// FIXME: 指定された主キーを持つ行を検索する. -RowID Table::find_row(const Datum &datum) { - return primary_key_column_->generic_find(datum); -} - -// 演算器を作成する. -Calc *Table::create_calc(const String &query) const { - return CalcHelper::create(this, query); -} - -// 整列器を作成する. -Sorter *Table::create_sorter(const String &query) const { - return SorterHelper::create(this, query); + // TODO: Apply options. + return table; } -// 整列済みの行一覧を受け取り,グループの境界を求める. -bool Table::group_by(const RowID *row_ids, Int64 num_row_ids, - const String &comma_separated_column_names, - std::vector<Int64> *boundaries) { - boundaries->clear(); - if (num_row_ids == 0) { - return true; - } - boundaries->push_back(num_row_ids); - auto column_names = split_column_names(comma_separated_column_names); - for (const String &column_name : column_names) { - // 一カラムずつ走査する. - auto column = get_column_by_name(column_name); - if (!column) { +bool Table::reserve_bit(Error *error, Int row_id) { + if (static_cast<size_t>(row_id / 64) >= bitmap_.size()) { + try { + bitmap_.push_back(~uint64_t(0)); + } catch (...) { + GRNXX_ERROR_SET(error, NO_MEMORY, "Memory allocation failed"); return false; } - std::vector<Int64> new_boundaries; - switch (column->data_type()) { - case BOOLEAN: { - find_boundaries<Boolean>(row_ids, num_row_ids, column, - *boundaries, &new_boundaries); - break; - } - case INTEGER: { - find_boundaries<Int64>(row_ids, num_row_ids, column, - *boundaries, &new_boundaries); - break; - } - case FLOAT: { - find_boundaries<Float>(row_ids, num_row_ids, column, - *boundaries, &new_boundaries); - break; - } - case STRING: { - find_boundaries<String>(row_ids, num_row_ids, column, - *boundaries, &new_boundaries); - break; - } - default: { - return false; - } - } - boundaries->swap(new_boundaries); } return true; } -// ストリームに書き出す. -std::ostream &Table::write_to(std::ostream &stream) const { - stream << "{ id = " << id_ - << ", name = \"" << name_ << '"' - << ", columns = "; - std::vector<Column *> columns; - if (get_columns(&columns) == 0) { - stream << "{}"; - } else { - stream << "{ " << *columns[0]; - for (std::size_t i = 1; i < columns.size(); ++i) { - stream << ", " << *columns[i]; - } - stream << " }"; - } - stream << " }"; - return stream; +bool Table::rename(Error *error, String new_name) { + return name_.assign(error, new_name); } -// 行の一覧を文字列化する. -std::ostream &Table::write_to(std::ostream &stream, - const RowID *row_ids, Int64 num_row_ids, - const String &comma_separated_column_names) { - std::vector<String> column_names; - std::vector<Column *> columns; - if (comma_separated_column_names.empty() || - (comma_separated_column_names == "*")) { - // 指定なし,およびに "*" のときはすべてのカラムに展開する. - get_columns(&columns); - column_names.push_back("_id"); - for (auto column : columns) { - column_names.push_back(column->name()); - } - columns.insert(columns.begin(), nullptr); - } else { - column_names = split_column_names(comma_separated_column_names); - for (const String &column_name : column_names) { - auto column = get_column_by_name(column_name); - if (!column) { - // "_id"という名前が指定されて,しかも該当するカラムが存在しないときは行IDを使う. - if (column_name != "_id") { -// std::cerr << "unknown column: \"" << column_name << "\""; - return stream; - } - } - columns.push_back(column); - } - } - stream << "{ column_names = {"; - for (std::size_t i = 0; i < column_names.size(); ++i) { - if (i == 0) { - stream << ' '; - } - stream << '"' << column_names[i] << '"'; - if (i != (column_names.size() - 1)) { - stream << ','; - } - stream << ' '; - } - stream << "}, rows = {"; - - for (Int64 i = 0; i < num_row_ids; ++i) { - if (i == 0) { - stream << ' '; - } - stream << '{'; - for (std::size_t j = 0; j < columns.size(); ++j) { - if (j == 0) { - stream << ' '; - } - if (columns[j]) { - stream << columns[j]->generic_get(row_ids[i]); - } else { - stream << row_ids[i]; - } - if (j != (columns.size() - 1)) { - stream << ','; - } - stream << ' '; - } - stream << '}'; - if (i != (num_row_ids - 1)) { - stream << ','; - } - stream << ' '; - } - stream << "} }"; - return stream; +bool Table::is_removable() { + // TODO: Referenced table (except self-reference) is not removable. + return true; } -// 行の一覧をグループ毎に文字列化する. -std::ostream &Table::write_to(std::ostream &stream, - const RowID *row_ids, Int64 num_row_ids, - const std::vector<Int64> &boundaries, - const String &comma_separated_column_names) { - std::vector<String> column_names; - std::vector<Column *> columns; - if (comma_separated_column_names.empty() || - (comma_separated_column_names == "*")) { - // 指定なし,およびに "*" のときはすべてのカラムに展開する. - get_columns(&columns); - column_names.push_back("_id"); - for (auto column : columns) { - column_names.push_back(column->name()); - } - columns.insert(columns.begin(), nullptr); - } else { - column_names = split_column_names(comma_separated_column_names); - for (const String &column_name : column_names) { - auto column = get_column_by_name(column_name); - if (!column) { - // "_id"という名前が指定されて,しかも該当するカラムが存在しないときは行IDを使う. - if (column_name != "_id") { -// std::cerr << "unknown column: \"" << column_name << "\""; - return stream; - } - } - columns.push_back(column); - } - } - stream << "{ column_names = {"; - for (std::size_t i = 0; i < column_names.size(); ++i) { - if (i == 0) { - stream << ' '; - } - stream << '"' << column_names[i] << '"'; - if (i != (column_names.size() - 1)) { - stream << ','; - } - stream << ' '; - } - stream << "}, groups = {"; - - Int64 begin = 0; - for (auto end : boundaries) { - if (end == boundaries.front()) { - stream << ' '; - } - stream << '{'; - for (Int64 i = begin; i < end; ++i) { - if (i == begin) { - stream << ' '; - } - stream << '{'; - for (std::size_t j = 0; j < columns.size(); ++j) { - if (j == 0) { - stream << ' '; - } - if (columns[j]) { - stream << columns[j]->generic_get(row_ids[i]); - } else { - stream << row_ids[i]; - } - if (j != (columns.size() - 1)) { - stream << ','; - } - stream << ' '; - } - stream << '}'; - if (i != (end - 1)) { - stream << ','; +Table::Table() + : db_(nullptr), + name_(), + columns_(), + key_column_(nullptr), + max_row_id_(MIN_ROW_ID - 1), + bitmap_() {} + +Column *Table::find_column_with_id(Error *error, + String name, + size_t *column_id) const { + for (Int i = 0; i < num_columns(); ++i) { + if (name == columns_[i]->name()) { + if (column_id != nullptr) { + *column_id = i; } - stream << ' '; - } - stream << '}'; - if (end != boundaries.back()) { - stream << ','; + return columns_[i].get(); } - stream << ' '; - begin = end; } - stream << "} }"; - return stream; -} - -std::ostream &operator<<(std::ostream &stream, const Table &table) { - return table.write_to(stream); + GRNXX_ERROR_SET(error, NOT_FOUND, "Column not found: name = \"%.*s\"", + static_cast<int>(name.size()), name.data()); + return nullptr; } } // namespace grnxx Deleted: lib/grnxx/table.hpp (+0 -184) 100644 =================================================================== --- lib/grnxx/table.hpp 2014-06-25 17:55:59 +0900 (acb4a49) +++ /dev/null @@ -1,184 +0,0 @@ -#ifndef GRNXX_TABLE_HPP -#define GRNXX_TABLE_HPP - -#include "grnxx/datum.hpp" - -namespace grnxx { - -class Table { - public: - // テーブルを初期化する. - Table(Database *database, TableID id, const String &name); - // テーブルを破棄する. - ~Table(); - - // コピーと代入を禁止する. - Table(const Table &) = delete; - Table &operator=(const Table &) = delete; - - // 所属するデータベースを返す. - Database *database() const { - return database_; - } - // テーブル ID を返す. - TableID id() const { - return id_; - } - // テーブル名を返す. - String name() const { - return name_; - } - - // 指定された名前とデータ型のカラムを作成して返す. - // 失敗すると nullptr を返す. - Column *create_column(const String &column_name, DataType data_type); - // 参照型のカラムを作成して返す. - // 失敗すると nullptr を返す. - Column *create_reference_column(const String &column_name, - const String &table_name); - // 指定された名前のカラムを破棄する. - // 成功すれば true を返し,失敗すれば false を返す. - bool drop_column(const String &column_name); - - // 指定されたカラムに主キー属性を与える. - // 成功すれば true を返し,失敗すれば false を返す. - bool set_primary_key(const String &column_name); - // 主キー属性を解除する. - // 成功すれば true を返し,失敗すれば false を返す. - bool unset_primary_key(); - - // カラム ID の最小値を返す. - ColumnID min_column_id() const { - return MIN_COLUMN_ID; - } - // カラム ID の最大値を返す. - ColumnID max_column_id() const { - return columns_.size() - 1; - } - - // 指定された ID のカラムを返す. - // なければ nullptr を返す. - Column *get_column_by_id(ColumnID column_id) const { - if (column_id > max_column_id()) { - return nullptr; - } - return columns_[column_id].get(); - } - // 指定された名前のカラムを返す. - // なければ nullptr を返す. - Column *get_column_by_name(const String &column_name) const; - - // カラムの一覧を columns の末尾に追加し,カラムの数を返す. - Int64 get_columns(std::vector<Column *> *columns) const; - - // 指定された名前,対象カラム,種類の索引を作成して返す. - // 失敗すると nullptr を返す. - Index *create_index(const String &index_name, const String &column_name, - IndexType index_type); - // 指定された名前の索引を破棄する. - // 成功すれば true を返し,失敗すれば false を返す. - bool drop_index(const String &index_name); - - // 索引 ID の最小値を返す. - IndexID min_index_id() const { - return MIN_INDEX_ID; - } - // 索引 ID の最大値を返す. - IndexID max_index_id() const { - return indexes_.size() - 1; - } - // 指定された ID の索引を返す. - // なければ nullptr を返す. - Index *get_index_by_id(IndexID index_id) const { - if (index_id > max_index_id()) { - return nullptr; - } - return indexes_[index_id].get(); - } - // 指定された名前の索引を返す. - // なければ nullptr を返す. - Index *get_index_by_name(const String &index_name) const; - - // 索引の一覧を indexes の末尾に追加し,索引の数を返す. - Int64 get_indexes(std::vector<Index *> *indexes) const; - - // 行を追加して,その行 ID を返す. - RowID insert_row(); - - // 行 ID の最小値を返す. - RowID min_row_id() const { - return MIN_ROW_ID; - } - // 行 ID の最大値を返す. - RowID max_row_id() const { - return max_row_id_; - } - // 主キーカラムを返す. - Column *primary_key_column() const { - return primary_key_column_; - } - - // 行 ID を取得するカーソル. - class Cursor : public RowIDCursor { - public: - // 指定されたテーブルの範囲内にある行 ID を取得するようにカーソルを初期化する. - Cursor(RowID range_min, RowID range_max); - - // 行 ID を最大 size 個取得して buf に格納し,取得した行 ID の数を返す. - // buf が nullptr のときは取得した行 ID をそのまま捨てる. - Int64 get_next(RowID *buf, Int64 size); - - private: - RowID row_id_; - RowID max_row_id_; - }; - - // 指定された範囲の行 ID を取得するカーソルを作成して返す. - // 範囲が省略された,もしくは 0 を指定されたときは, - // 行 ID の最小値・最大値を範囲として使用する. - Cursor *create_cursor(RowID range_min = MIN_ROW_ID, - RowID range_max = INT64_MAX) const; - - // FIXME: 指定された主キーを持つ行を検索する. - RowID find_row(const Datum &datum); - - // 演算器を作成する. - Calc *create_calc(const String &query) const; - // 整列器を作成する. - Sorter *create_sorter(const String &query) const; - - // 整列済みの行一覧を受け取り,グループの境界を求める. - bool group_by(const RowID *row_ids, Int64 num_row_ids, - const String &comma_separated_column_names, - std::vector<Int64> *boundaries); - - // ストリームに書き出す. - std::ostream &write_to(std::ostream &stream) const; - - // 行の一覧を文字列化する. - std::ostream &write_to(std::ostream &stream, - const RowID *row_ids, Int64 num_row_ids, - const String &comma_separated_column_names); - // 行の一覧をグループ毎に文字列化する. - std::ostream &write_to(std::ostream &stream, - const RowID *row_ids, Int64 num_row_ids, - const std::vector<Int64> &boundaries, - const String &comma_separated_column_names); - - private: - Database *database_; - TableID id_; - std::string name_; - RowID max_row_id_; - Column *primary_key_column_; - std::vector<std::unique_ptr<Column>> columns_; - std::map<String, ColumnID> columns_map_; - std::vector<std::unique_ptr<Index>> indexes_; - std::map<String, IndexID> indexes_map_; -}; - -std::ostream &operator<<(std::ostream &stream, const Table &table); - -} // namespace grnxx - -#endif // GRNXX_TABLE_HPP Deleted: lib/grnxx/timer.cpp (+0 -32) 100644 =================================================================== --- lib/grnxx/timer.cpp 2014-06-25 17:55:59 +0900 (a4194bf) +++ /dev/null @@ -1,32 +0,0 @@ -#include "grnxx/timer.hpp" - -#include <time.h> - -namespace grnxx { -namespace { - -// 現在の時刻を秒単位で返す. -double get_current_time() { - // CLOCK_MONOTONIC_RAW は使える環境が限られるため, - // とりあえずは CLOCK_MONOTONIC を使うことにする. - struct timespec current_time; - ::clock_gettime(CLOCK_MONOTONIC, ¤t_time); - return current_time.tv_sec + (current_time.tv_nsec * 0.000000001); -} - -} // namespace - -// タイマを初期化する. -Timer::Timer() : start_time_(get_current_time()) {} - -// タイマが初期化されてからの経過時間を秒単位で返す. -double Timer::elapsed() const { - return get_current_time() - start_time_; -} - -// タイマを初期化する. -void Timer::reset() { - start_time_ = get_current_time(); -} - -} // namespace grnxx Deleted: lib/grnxx/timer.hpp (+0 -24) 100644 =================================================================== --- lib/grnxx/timer.hpp 2014-06-25 17:55:59 +0900 (4927889) +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef GRNXX_TIMER_HPP -#define GRNXX_TIMER_HPP - -namespace grnxx { - -// FIXME: 将来的にはマイクロ秒単位を標準にする. -class Timer { - public: - // タイマを初期化する. - Timer(); - - // タイマが初期化されてからの経過時間を秒単位で返す. - double elapsed() const; - - // タイマを初期化する. - void reset(); - - private: - double start_time_; -}; - -} // namespace grnxx - -#endif // GRNXX_TIMER_HPP Deleted: lib/grnxx/types.cpp (+0 -34) 100644 =================================================================== --- lib/grnxx/types.cpp 2014-06-25 17:55:59 +0900 (915186b) +++ /dev/null @@ -1,34 +0,0 @@ -#include "grnxx/types.hpp" - -#include <ostream> - -namespace grnxx { - -std::ostream &operator<<(std::ostream &stream, DataType data_type) { - switch (data_type) { - case BOOLEAN: { - return stream << "BOOLEAN"; - } - case INTEGER: { - return stream << "INTEGER"; - } - case FLOAT: { - return stream << "FLOAT"; - } - case STRING: { - return stream << "STRING"; - } - } - return stream << "N/A"; -} - -std::ostream &operator<<(std::ostream &stream, IndexType index_type) { - switch (index_type) { - case TREE_MAP: { - return stream << "TREE_MAP"; - } - } - return stream << "N/A"; -} - -} // namespace grnxx Deleted: lib/grnxx/types.hpp (+0 -112) 100644 =================================================================== --- lib/grnxx/types.hpp 2014-06-25 17:55:59 +0900 (3069069) +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef GRNXX_TYPES_HPP -#define GRNXX_TYPES_HPP - -#include <algorithm> -#include <cctype> -#include <cstdint> -#include <cstring> -#include <functional> -#include <iosfwd> -#include <map> -#include <memory> -#include <set> -#include <string> -#include <vector> - -namespace grnxx { - -// 真偽値. -using Boolean = bool; - -// 符号付き整数. -using Int8 = std::int8_t; -using Int16 = std::int16_t; -using Int32 = std::int32_t; -using Int64 = std::int64_t; - -// 符号なし整数. -using UInt8 = std::uint8_t; -using UInt16 = std::uint16_t; -using UInt32 = std::uint32_t; -using UInt64 = std::uint64_t; - -// 浮動小数点数. -using Float = double; - -// 文字列. -class String; - -// 前方宣言. -class Database; -class Table; -class Column; -class Index; -class Calc; -class Sorter; - -// 各種 ID. -using TableID = Int64; -using ColumnID = Int64; -using IndexID = Int64; -using RowID = Int64; - -// 各種 ID の最小値. -constexpr TableID MIN_TABLE_ID = 1; -constexpr ColumnID MIN_COLUMN_ID = 1; -constexpr IndexID MIN_INDEX_ID = 1; -constexpr RowID MIN_ROW_ID = 1; - -// 行 ID を取得するカーソルのインタフェース. -class RowIDCursor { - public: - // カーソルを初期化する. - explicit RowIDCursor() {} - // カーソルを破棄する. - virtual ~RowIDCursor() {} - - // 行 ID を最大 size 個取得して buf に格納し,取得した行 ID の数を返す. - // buf が nullptr のときは取得した行 ID をそのまま捨てる. - virtual Int64 get_next(RowID *buf, Int64 size) = 0; -}; - -// データ型の種類. -enum DataType { - BOOLEAN, // 真偽値. - INTEGER, // 整数. - FLOAT, // 浮動小数点数. - STRING // 文字列. -}; - -// データ型の情報. -template <typename T> struct TypeTraits; -template <> struct TypeTraits<Boolean> { - static DataType data_type() { - return BOOLEAN; - } -}; -template <> struct TypeTraits<Int64> { - static DataType data_type() { - return INTEGER; - } -}; -template <> struct TypeTraits<Float> { - static DataType data_type() { - return FLOAT; - } -}; -template <> struct TypeTraits<String> { - static DataType data_type() { - return STRING; - } -}; - -// 索引の種類. -enum IndexType { - TREE_MAP -}; - -std::ostream &operator<<(std::ostream &stream, IndexType index_type); - -} // namespace grnxx - -#endif // GRNXX_TYPES_HPP Modified: src/grnxx.cpp (+8 -1002) =================================================================== --- src/grnxx.cpp 2014-06-25 17:55:59 +0900 (b6356f6) +++ src/grnxx.cpp 2014-07-04 12:25:36 +0900 (17d2a06) @@ -1,1013 +1,25 @@ #include <getopt.h> #include <unistd.h> -#include <cctype> #include <iostream> -#include <sstream> -#include "grnxx/calc.hpp" -#include "grnxx/column.hpp" -#include "grnxx/column_impl.hpp" -#include "grnxx/database.hpp" -#include "grnxx/index.hpp" #include "grnxx/library.hpp" -#include "grnxx/sorter.hpp" -#include "grnxx/table.hpp" -#include "grnxx/timer.hpp" namespace { -bool verbose_mode = false; - -// 入力された行をコマンドと引数に切り分ける. -bool extract_command(const std::string &line, - grnxx::String *command, grnxx::String *params) { - // 先頭の空白をスキップする. - std::size_t begin = line.find_first_not_of(" \t\n"); - if ((begin == line.npos) || (line[begin] == '#')) { - // 空行および'#'で始まる行(コメント)は無視する. - return false; - } - // 次の空白までをコマンドとして抜き出す. - std::size_t end = line.find_first_of(" \t\n", begin); - if (end == line.npos) { - end = line.size(); - } - *command = grnxx::String(line.data() + begin, end - begin); - // コマンドの後ろにある空白をスキップする. - begin = line.find_first_not_of(" \t\n", end); - if (begin == line.npos) { - begin = line.size(); - } - end = line.find_last_not_of(" \t\n"); - if (end == line.npos) { - end = line.size(); - } - *params = grnxx::String(line.data() + begin, end - begin + 1); - return true; -} - -// table_create コマンド. -bool run_table_create(grnxx::Database *database, - const grnxx::String ¶ms) { - auto delim_pos = params.find_first_of(" \t\n"); - if (delim_pos == params.npos) { - delim_pos = params.size(); - } - grnxx::String table_name = params.prefix(delim_pos); - if (!database->create_table(table_name)) { - std::cerr << "Error: grnxx::Database::create_table() failed: " - << "table_name = " << table_name << std::endl; - return false; - } - std::cout << "OK\n"; - return true; -} - -// table_remove コマンド. -bool run_table_remove(grnxx::Database *database, - const grnxx::String ¶ms) { - auto delim_pos = params.find_first_of(" \t\n"); - if (delim_pos == params.npos) { - delim_pos = params.size(); - } - grnxx::String table_name = params.prefix(delim_pos); - if (!database->drop_table(table_name)) { - std::cerr << "Error: grnxx::Database::drop_table() failed: " - << "table_name = " << table_name << std::endl; - return false; - } - std::cout << "OK\n"; - return true; -} - -// table_list コマンド. -bool run_table_list(grnxx::Database *database, - const grnxx::String ¶ms) { - std::vector<grnxx::Table *> tables; - database->get_tables(&tables); - for (auto table : tables) { - std::cout << "id = " << table->id() - << ", name = " << table->name() << '\n'; - } - std::cout << "OK\n"; - return true; -} - -// column_create コマンド. -bool run_column_create(grnxx::Database *database, - const grnxx::String ¶ms) { - auto end = params.find_first_of(" \t\n"); - if (end == params.npos) { - std::cerr << "Error: too few arguments" << std::endl; - return false; - } - grnxx::String table_name = params.prefix(end); - auto table = database->get_table_by_name(table_name); - if (!table) { - std::cerr << "Error: table not found: " - << "table_name = " << table_name << std::endl; - return false; - } - auto begin = params.find_first_not_of(" \t\n", end); - end = params.find_first_of(" \t\n", begin); - if (end == params.npos) { - std::cerr << "Error: too few arguments" << std::endl; - return false; - } - grnxx::String column_name = params.extract(begin, end - begin); - begin = params.find_first_not_of(" \t\n", end); - end = params.find_first_of(" \t\n", begin); - if (end == params.npos) { - end = params.size(); - } - grnxx::String data_type_name = params.extract(begin, end - begin); - grnxx::DataType data_type; - if (data_type_name == "BOOLEAN") { - data_type = grnxx::BOOLEAN; - } else if ((data_type_name == "INT8") || - (data_type_name == "INT16") || - (data_type_name == "INT32") || - (data_type_name == "INT64")) { - data_type = grnxx::INTEGER; - } else if (data_type_name == "FLOAT") { - data_type = grnxx::FLOAT; - } else if (data_type_name == "STRING") { - data_type = grnxx::STRING; - } else { - if (!database->get_table_by_name(data_type_name)) { - std::cerr << "Error: Unknown data type: " - << "data_type = " << data_type_name << std::endl; - return false; - } - if (!table->create_reference_column(column_name, data_type_name)) { - std::cerr << "Error: grnxx::Table::create_reference_column() failed: " - << "table_name = \"" << table_name << '"' - << ", column_name = \"" << column_name << '"' - << ", dest_table_name = " << data_type_name << std::endl; - return false; - } - std::cout << "OK\n"; - return true; - } - if (!table->create_column(column_name, data_type)) { - std::cerr << "Error: grnxx::Table::create_column() failed: " - << "table_name = \"" << table_name << '"' - << ", column_name = \"" << column_name << '"' - << ", data_type = " << data_type << std::endl; - return false; - } - std::cout << "OK\n"; - return true; -} - -// column_remove コマンド. -bool run_column_remove(grnxx::Database *database, - const grnxx::String ¶ms) { - auto end = params.find_first_of(" \t\n"); - if (end == params.npos) { - std::cerr << "Error: too few arguments" << std::endl; - return false; - } - grnxx::String table_name = params.prefix(end); - auto table = database->get_table_by_name(table_name); - if (!table) { - std::cerr << "Error: table not found: " - << "table_name = " << table_name << std::endl; - return false; - } - auto begin = params.find_first_not_of(" \t\n", end); - end = params.find_first_of(" \t\n", begin); - if (end == params.npos) { - end = params.size(); - } - grnxx::String column_name = params.extract(begin, end - begin); - if (!table->drop_column(column_name)) { - std::cerr << "Error: grnxx::Table::drop_column() failed: " - << "table_name = " << table_name - << ", column_name = " << column_name << std::endl; - return false; - } - std::cout << "OK\n"; - return true; -} - -// column_list コマンド. -bool run_column_list(grnxx::Database *database, - const grnxx::String ¶ms) { - auto delim_pos = params.find_first_of(" \t\n"); - if (delim_pos == params.npos) { - delim_pos = params.size(); - } - grnxx::String table_name = params.prefix(delim_pos); - auto table = database->get_table_by_name(table_name); - if (!table) { - std::cerr << "Error: table not found: table_name = " - << table_name << std::endl; - return false; - } - std::vector<grnxx::Column *> columns; - table->get_columns(&columns); - for (auto column : columns) { - std::cout << "id = " << column->id() - << ", name = \"" << column->name() << '"' - << ", type = " << column->data_type() << '\n'; - } - std::cout << "OK\n"; - return true; -} - -// index_create コマンド. -bool run_index_create(grnxx::Database *database, - const grnxx::String ¶ms) { - auto end = params.find_first_of(" \t\n"); - if (end == params.npos) { - std::cerr << "Error: too few arguments" << std::endl; - return false; - } - grnxx::String table_name = params.prefix(end); - auto table = database->get_table_by_name(table_name); - if (!table) { - std::cerr << "Error: table not found: " - << "table_name = " << table_name << std::endl; - return false; - } - auto begin = params.find_first_not_of(" \t\n", end); - end = params.find_first_of(" \t\n", begin); - if (end == params.npos) { - std::cerr << "Error: too few arguments" << std::endl; - return false; - } - grnxx::String index_name = params.extract(begin, end - begin); - begin = params.find_first_not_of(" \t\n", end); - end = params.find_first_of(" \t\n", begin); - if (end == params.npos) { - end = params.size(); - } - grnxx::String column_name = params.extract(begin, end - begin); - if (end == params.size()) { - begin = end; - } else { - begin = params.find_first_not_of(" \t\n", end); - end = params.find_first_of(" \t\n", begin); - if (end == params.npos) { - end = params.size(); - } - } - grnxx::String index_type_name = params.extract(begin, end - begin); - grnxx::IndexType index_type = grnxx::TREE_MAP; - if (index_type_name.empty()) { - index_type = grnxx::TREE_MAP; - } else if (index_type_name == "TREE_MAP") { - index_type = grnxx::TREE_MAP; - } else { - std::cerr << "Error: Unknown index type: " - << "index_type = " << index_type_name << std::endl; - return false; - } - if (!table->create_index(index_name, column_name, index_type)) { - std::cerr << "Error: grnxx::Table::create_index() failed: " - << "table_name = " << table_name - << ", index_name = " << index_name - << ", column_name = " << column_name - << ", index_type = " << index_type << std::endl; - return false; - } - std::cout << "OK\n"; - return true; -} - -// index_remove コマンド. -bool run_index_remove(grnxx::Database *database, - const grnxx::String ¶ms) { - auto end = params.find_first_of(" \t\n"); - if (end == params.npos) { - std::cerr << "Error: too few arguments" << std::endl; - return false; - } - grnxx::String table_name = params.prefix(end); - auto table = database->get_table_by_name(table_name); - if (!table) { - std::cerr << "Error: table not found: " - << "table_name = " << table_name << std::endl; - return false; - } - auto begin = params.find_first_not_of(" \t\n", end); - end = params.find_first_of(" \t\n", begin); - if (end == params.npos) { - end = params.size(); - } - grnxx::String index_name = params.extract(begin, end - begin); - if (!table->drop_index(index_name)) { - std::cerr << "Error: grnxx::Table::drop_index() failed: " - << "table_name = " << table_name - << ", index_name = " << index_name << std::endl; - return false; - } - std::cout << "OK\n"; - return true; -} - -// index_list コマンド. -bool run_index_list(grnxx::Database *database, - const grnxx::String ¶ms) { - auto delim_pos = params.find_first_of(" \t\n"); - if (delim_pos == params.npos) { - delim_pos = params.size(); - } - grnxx::String table_name = params.prefix(delim_pos); - auto table = database->get_table_by_name(table_name); - if (!table) { - std::cerr << "Error: table not found: table_name = " - << table_name << std::endl; - return false; - } - std::vector<grnxx::Index *> indexes; - table->get_indexes(&indexes); - for (auto index : indexes) { - std::cout << "id = " << index->id() - << ", name = \"" << index->name() << '"' - << ", column = \"" << index->column()->name() << '"' - << ", type = " << index->type() << '\n'; - } - std::cout << "OK\n"; - return true; -} - -// load コマンド. -bool run_load(grnxx::Database *database, - const grnxx::String ¶ms) { - auto end = params.find_first_of(" \t\n"); - if (end == params.npos) { - end = params.size(); - } - grnxx::String table_name = params.prefix(end); - auto table = database->get_table_by_name(table_name); - if (!table) { - std::cerr << "Error: table not found: table_name = " - << table_name << std::endl; - return false; - } - auto begin = params.find_first_not_of(" \t\n", end); - if (begin == params.npos) { - // 入力がひとつもない. - std::cout << "OK: 0 rows\n"; - return true; - } - grnxx::Int64 count = 0; - grnxx::Datum datum; - do { - ++count; - grnxx::RowID row_id = table->insert_row(); - if (!row_id) { - std::cerr << "Error: Table::add_row() failed" << std::endl; - return false; - } - for (grnxx::ColumnID column_id = table->min_column_id(); - column_id <= table->max_column_id(); ++column_id) { - auto column = table->get_column_by_id(column_id); - if (!column) { - continue; - } - grnxx::Table *dest_table = nullptr; - if (params[begin] == '&') { - // 参照型. - if (column->data_type() != grnxx::INTEGER) { - return false; - } - auto impl = static_cast<grnxx::ColumnImpl<grnxx::Int64> *>(column); - dest_table = impl->dest_table(); - if (!dest_table || !dest_table->primary_key_column()) { - return false; - } - ++begin; - } - if (params[begin] == '"') { - // 文字列. - end = params.find_first_of('"', ++begin); - if (end == params.npos) { - end = params.size(); - } - datum = params.extract(begin, end - begin); - if (end != params.size()) { - ++end; - } - } else { - // そのほか. - end = params.find_first_of(" \t\n", begin); - if (end == params.npos) { - end = params.size(); - } - datum = params.extract(begin, end - begin); - } - if (dest_table) { - // 参照の解決. - datum = dest_table->find_row(datum); - } - if (column->is_unique()) { - // UNIQUE 制約の確認. - if (column->generic_find(datum) != 0) { - return false; - } - } - column->generic_set(table->max_row_id(), datum); - begin = params.find_first_not_of(" \t\n", end); - if (begin == params.npos) { - break; - } - } - } while (begin != params.npos); - std::cout << "OK: " << count << " rows\n"; - return true; -} - -// set_primary_key コマンド. -bool run_set_primary_key(grnxx::Database *database, - const grnxx::String ¶ms) { - auto end = params.find_first_of(" \t\n"); - if (end == params.npos) { - std::cerr << "Error: too few arguments" << std::endl; - return false; - } - grnxx::String table_name = params.prefix(end); - auto table = database->get_table_by_name(table_name); - if (!table) { - std::cerr << "Error: table not found: " - << "table_name = " << table_name << std::endl; - return false; - } - auto begin = params.find_first_not_of(" \t\n", end); - end = params.find_first_of(" \t\n", begin); - if (end == params.npos) { - end = params.size(); - } - grnxx::String column_name = params.extract(begin, end - begin); - if (!table->set_primary_key(column_name)) { - std::cerr << "Error: grnxx::Table::set_primary_key() failed: " - << "table_name = " << table_name - << ", column_name = " << column_name << std::endl; - return false; - } - std::cout << "OK\n"; - return true; -} - -struct SelectQuery { - grnxx::String table_name; - grnxx::String output_column_names; - grnxx::String index_query; - grnxx::String calc_query; - grnxx::Int64 offset; - grnxx::Int64 limit; - grnxx::String column_names_for_sort_by; - grnxx::String column_names_for_group_by; -}; - -bool parse_select_params(grnxx::String params, SelectQuery *query) { - // テーブルの名前を切り出す. - auto boundary = params.find_first_of(" \t\n"); - if (boundary == params.npos) { - boundary = params.size(); - } - query->table_name = params.prefix(boundary); - params = params.except_prefix(boundary); - boundary = params.find_first_not_of(" \t\n"); - if (boundary == params.npos) { - boundary = params.size(); - } - params = params.except_prefix(boundary); - - // 出力カラム名を切り出す. - boundary = params.find_first_of(" \t\n"); - if (boundary == params.npos) { - boundary = params.size(); - } - query->output_column_names = params.prefix(boundary); - params = params.except_prefix(boundary); - boundary = params.find_first_not_of(" \t\n"); - if (boundary == params.npos) { - boundary = params.size(); - } - params = params.except_prefix(boundary); - - // 索引を使うクエリを切り出す. - query->index_query = ""; - if (!params.empty() && (params[0] == '`')) { - boundary = params.find_first_of('`', 1); - if (boundary == params.npos) { - std::cerr << "Error: closing back quote does not exist" << std::endl; - return false; - } - query->index_query = params.extract(1, boundary - 1); - params = params.except_prefix(boundary + 1); - } - boundary = params.find_first_not_of(" \t\n"); - if (boundary == params.npos) { - boundary = params.size(); - } - params = params.except_prefix(boundary); - - // フィルタのクエリを切り出す. - query->calc_query = ""; - if (!params.empty() && (params[0] == '\'')) { - boundary = params.find_first_of('\'', 1); - if (boundary == params.npos) { - std::cerr << "Error: closing single quote does not exist" << std::endl; - return false; - } - query->calc_query = params.extract(1, boundary - 1); - params = params.except_prefix(boundary + 1); - } - boundary = params.find_first_not_of(" \t\n"); - if (boundary == params.npos) { - boundary = params.size(); - } - params = params.except_prefix(boundary); - - // オフセットを切り出す. - query->offset = 0; - if (!params.empty() && std::isdigit(params[0])) { - boundary = params.find_first_of(" \t\n"); - if (boundary == params.npos) { - boundary = params.size(); - } - query->offset = std::strtol(reinterpret_cast<const char *>(params.data()), - nullptr, 10); - params = params.except_prefix(boundary); - } - boundary = params.find_first_not_of(" \t\n"); - if (boundary == params.npos) { - boundary = params.size(); - } - params = params.except_prefix(boundary); - - // 上限数を切り出す. - query->limit = INT64_MAX; - if (!params.empty() && std::isdigit(params[0])) { - boundary = params.find_first_of(" \t\n"); - if (boundary == params.npos) { - boundary = params.size(); - } - query->limit = std::strtol(reinterpret_cast<const char *>(params.data()), - nullptr, 10); - params = params.except_prefix(boundary); - } - boundary = params.find_first_not_of(" \t\n"); - if (boundary == params.npos) { - boundary = params.size(); - } - params = params.except_prefix(boundary); - - // 整列条件を切り出す. - boundary = params.find_first_of(" \t\n"); - if (boundary == params.npos) { - boundary = params.size(); - } - query->column_names_for_sort_by = params.prefix(boundary); - params = params.except_prefix(boundary); - boundary = params.find_first_not_of(" \t\n"); - if (boundary == params.npos) { - boundary = params.size(); - } - params = params.except_prefix(boundary); - - // グループ化の条件を切り出す. - boundary = params.find_first_of(" \t\n"); - if (boundary == params.npos) { - boundary = params.size(); - } - query->column_names_for_group_by = params.prefix(boundary); - params = params.except_prefix(boundary); - boundary = params.find_first_not_of(" \t\n"); - if (boundary == params.npos) { - boundary = params.size(); - } - params = params.except_prefix(boundary); - - if (verbose_mode) { - std::cout << "table_name = " << query->table_name << '\n' - << "output = " << query->output_column_names << '\n' - << "index_query = " << query->index_query << '\n' - << "calc_query = " << query->calc_query << '\n' - << "offset = " << query->offset << '\n' - << "limit = " << query->limit << '\n' - << "sort_by = " << query->column_names_for_sort_by << '\n' - << "group_by = " << query->column_names_for_group_by << '\n'; - } - - return true; -} - -grnxx::RowIDCursor *create_index_cursor(grnxx::Table *table, - grnxx::String query) { - // 索引の名前を切り出す. - auto boundary = query.find_first_of(" \t\n"); - if (boundary == query.npos) { - boundary = query.size(); - } - grnxx::String index_name = query.prefix(boundary); - bool reverse_order = false; - if (index_name.starts_with("-")) { - index_name = index_name.except_prefix(1); - reverse_order = true; - } - grnxx::Index *index = table->get_index_by_name(index_name); - if (!index) { - std::cerr << "Error: index not found: index_name = " - << index_name << std::endl; - return nullptr; - } - query = query.except_prefix(boundary); - boundary = query.find_first_not_of(" \t\n"); - if (boundary == query.npos) { - boundary = query.size(); - } - query = query.except_prefix(boundary); - - // 特に指示がなければすべての行を取得できるカーソルを作成する. - if (query.empty()) { - return index->find_all(reverse_order); - } - - // 指定された範囲に対応するカーソルを作成する. - if (!query.empty() && ((query[0] == '[') || (query[0] == '('))) { - // 角括弧なら「以上」と「以下」で,丸括弧なら「より大きい」と「より小さい」になる. - grnxx::UInt8 last_byte = query[query.size() - 1]; - if ((last_byte != ']') && (last_byte != ')')) { - std::cerr << "Error: closing bracket not found" << std::endl; - return nullptr; - } - bool greater_equal = (query[0] == '['); - bool less_equal = (last_byte == ']'); - query = query.trim(1, 1); - - // 括弧内両端の空白を取り除く. - boundary = query.find_first_not_of(" \t\n"); - if (boundary == query.npos) { - std::cerr << "Error: empty brackets" << std::endl; - return nullptr; - } - query = query.except_prefix(boundary); - query = query.prefix(query.find_last_not_of(" \t\n") + 1); - - // 一つ目の値を切り出す. - bool has_begin = false; - grnxx::Datum begin; - if (query[0] == '"') { - boundary = query.find_first_of('"', 1); - if (boundary == query.npos) { - std::cerr << "Error: closing double quote not found" << std::endl; - return nullptr; - } - has_begin = true; - begin = query.extract(1, boundary - 1); - query = query.except_prefix(boundary + 1); - } else { - boundary = query.find_first_of(" \t\n,"); - if (boundary == query.npos) { - std::cerr << "Error: delimiter not found" << std::endl; - return nullptr; - } else if (boundary != 0) { - has_begin = true; - begin = query.prefix(boundary); - query = query.except_prefix(boundary); - } - } - - // 空白,区切り文字,空白の順でスキップする. - boundary = query.find_first_not_of(" \t\n"); - if (boundary == query.npos) { - std::cerr << "Error: delimiter not found" << std::endl; - return nullptr; - } - query = query.except_prefix(boundary); - if (query[0] != ',') { - std::cerr << "Error: delimiter not found" << std::endl; - return nullptr; - } - query = query.except_prefix(1); - boundary = query.find_first_not_of(" \t\n"); - if (boundary == query.npos) { - boundary = query.size(); - } - query = query.except_prefix(boundary); - - // 二つ目の値を切り出す. - bool has_end = false; - grnxx::Datum end; - if (query[0] == '"') { - boundary = query.find_first_of('"', 1); - if (boundary == query.npos) { - std::cerr << "Error: closing double quote not found" << std::endl; - return nullptr; - } - has_end = true; - end = query.extract(1, boundary - 1); - query = query.except_prefix(boundary + 1); - } else { - boundary = query.find_first_of(" \t\n"); - if (boundary == query.npos) { - boundary = query.size(); - } - if (boundary != 0) { - has_end = true; - end = query.prefix(boundary); - query = query.except_prefix(boundary); - } - } - - if (!query.empty()) { - std::cerr << "Error: invalid format" << std::endl; - return nullptr; - } - - if (verbose_mode) { - std::cout << "index_name = " << index_name << '\n' - << "begin = " << (has_begin ? begin : "N/A") << '\n' - << "end = " << (has_end ? end : "N/A") << '\n' - << "greater_equal = " << greater_equal << '\n' - << "less_equal = " << less_equal << '\n'; - } - - if (has_begin) { - if (has_end) { - return index->find_between(begin, end, greater_equal, less_equal, - reverse_order); - } else { - return index->find_greater(begin, greater_equal, reverse_order); - } - } else if (has_end) { - return index->find_less(end, less_equal, reverse_order); - } else { - return index->find_all(reverse_order); - } - return nullptr; - } - - // 指定された値に対応するカーソルを作成する. - grnxx::Datum datum; - if (query[0] == '"') { - boundary = query.find_first_of('"', 1); - if (boundary == query.npos) { - std::cerr << "Error: closing double quote not found" << std::endl; - return nullptr; - } - datum = query.extract(1, boundary - 1); - query = query.except_prefix(boundary + 1); - } else { - datum = query; - } - - if (verbose_mode) { - std::cout << "index_name = " << index_name << '\n' - << "datum = " << datum << '\n'; - } - - return index->find_equal(datum); -} - -// select コマンド. -bool run_select(grnxx::Database *database, const grnxx::String ¶ms, - std::ostream &stream) { - // 引数を解釈する. - SelectQuery query; - if (!parse_select_params(params, &query)) { - return false; - } - - // 指定されたテーブルを探す. - grnxx::Table *table = database->get_table_by_name(query.table_name); - if (!table) { - std::cerr << "Error: table not found: table_name = " - << query.table_name << std::endl; - return false; - } - - // 行 ID の一覧を取得するためのカーソルを用意する. - std::unique_ptr<grnxx::RowIDCursor> cursor; - if (query.index_query.empty()) { - // テーブル由来のカーソルを使う. - cursor.reset(table->create_cursor()); - if (!cursor) { - std::cerr << "Error: Table::create_cursor failed" << std::endl; - return false; - } - } else { - // 索引由来のカーソルを使う. - cursor.reset(create_index_cursor(table, query.index_query)); - if (!cursor) { - return false; - } - } - - // 絞り込みに用いるフィルタを用意する. - std::unique_ptr<grnxx::Calc> calc(table->create_calc(query.calc_query)); - if (!calc) { - std::cerr << "Error: Table::create_calc() failed: query = " - << query.calc_query << std::endl; - return false; - } - - // 検索をおこなう. - constexpr grnxx::Int64 BLOCK_SIZE = 1024; - grnxx::Int64 num_filtered_rows = 0; - std::vector<grnxx::RowID> row_ids; - if (query.column_names_for_sort_by.empty()) { - // 整列条件がなければ offset, limit を考慮する. - if (calc->empty()) { - // 検索条件がなければ,先頭の offset 件をスキップした後, - // 最大 limit 件を取り出して終了する. - cursor->get_next(nullptr, query.offset); - grnxx::Int64 num_rows = 0; - while (num_rows < query.limit) { - grnxx::Int64 limit_left = query.limit - num_rows; - grnxx::Int64 block_size = - (limit_left < BLOCK_SIZE) ? limit_left : BLOCK_SIZE; - row_ids.resize(num_rows + block_size); - grnxx::Int64 num_new_rows = - cursor->get_next(&row_ids[num_rows], block_size); - num_rows += num_new_rows; - if (num_new_rows < block_size) { - break; - } - } - row_ids.resize(num_rows); - } else { - // 検索条件があればブロック単位でフィルタにかける. - grnxx::Int64 num_rows = 0; - grnxx::Int64 offset_left = query.offset; - for ( ; ; ) { - row_ids.resize(num_rows + BLOCK_SIZE); - grnxx::Int64 num_new_rows = - cursor->get_next(&row_ids[num_rows], BLOCK_SIZE); - if (num_new_rows == 0) { - break; - } - num_filtered_rows += num_new_rows; - num_new_rows = calc->filter(&row_ids[num_rows], num_new_rows); - if (offset_left != 0) { - if (num_new_rows > offset_left) { - for (grnxx::Int64 i = 0; i < (num_new_rows - offset_left); ++i) { - row_ids[i] = row_ids[offset_left + i]; - } - num_new_rows -= offset_left; - offset_left = 0; - } else { - offset_left -= num_new_rows; - num_new_rows = 0; - } - } - num_rows += num_new_rows; - if (num_rows >= query.limit) { - num_rows = query.limit; - break; - } - } - row_ids.resize(num_rows); - } - } else { - // 整列条件が指定されているときは,後で offset, limit を適用する. - grnxx::Int64 num_rows = 0; - for ( ; ; ) { - row_ids.resize(num_rows + BLOCK_SIZE); - grnxx::Int64 num_new_rows = - cursor->get_next(&row_ids[num_rows], BLOCK_SIZE); - num_rows += num_new_rows; - if (num_new_rows < BLOCK_SIZE) { - break; - } - } - row_ids.resize(num_rows); - } - - if (verbose_mode) { - std::cout << "num_filtered_rows = " << num_filtered_rows << '\n'; - } - - // 整列をおこなう. - if (!query.column_names_for_sort_by.empty()) { - std::unique_ptr<grnxx::Sorter> sorter( - table->create_sorter(query.column_names_for_sort_by)); - if (!sorter) { - std::cerr << "Error: Table::create_sorter() failed: query = " - << query.column_names_for_sort_by << std::endl; - return false; - } - sorter->sort(&*row_ids.begin(), row_ids.size(), query.offset, query.limit); - if (query.offset >= row_ids.size()) { - // 先頭の offset 個を取り除くと空になる. - row_ids.clear(); - } else if (query.offset > 0) { - // 先頭の offset 個を取り除く. - grnxx::Int64 num_rows = row_ids.size() - query.offset; - if (num_rows > query.limit) { - num_rows = query.limit; - } - for (grnxx::Int64 i = 0; i < num_rows; ++i) { - row_ids[i] = row_ids[query.offset + i]; - } - row_ids.resize(num_rows); - } else if (query.limit < row_ids.size()) { - // 先頭の limit 個までを残す. - row_ids.resize(query.limit); - } - } - - // グループ化をおこなう. - std::vector<grnxx::Int64> boundaries; - if (!query.column_names_for_group_by.empty()) { - if (!table->group_by(&*row_ids.begin(), row_ids.size(), - query.column_names_for_group_by, &boundaries)) { - std::cerr << "Error: Table::group_by() failed: column_names = " - << query.column_names_for_group_by << std::endl; - return false; - } - } - - // 結果を出力する. - table->write_to(stream << "result = ", &*row_ids.begin(), row_ids.size(), - query.output_column_names) << '\n'; - if (!boundaries.empty()) { - table->write_to(stream, &*row_ids.begin(), row_ids.size(), - boundaries, query.output_column_names) << '\n'; - } - std::cout << "OK: " << row_ids.size() << " rows\n"; - - return false; -} - -// 対話モード. -void run_terminal() { - grnxx::Database database; - std::string line; - grnxx::String command; - grnxx::String params; - while (std::getline(std::cin, line)) { - if (extract_command(line, &command, ¶ms)) { - if (verbose_mode) { - std::cout << "command = " << command - << ", params = " << params << '\n'; - } - if (command == "table_create") { - run_table_create(&database, params); - } else if (command == "table_remove") { - run_table_remove(&database, params); - } else if (command == "table_list") { - run_table_list(&database, params); - } else if (command == "column_create") { - run_column_create(&database, params); - } else if (command == "column_remove") { - run_column_remove(&database, params); - } else if (command == "column_list") { - run_column_list(&database, params); - } else if (command == "index_create") { - run_index_create(&database, params); - } else if (command == "index_remove") { - run_index_remove(&database, params); - } else if (command == "index_list") { - run_index_list(&database, params); - } else if (command == "load") { - run_load(&database, params); - } else if (command == "set_primary_key") { - run_set_primary_key(&database, params); - } else if (command == "select") { - grnxx::Timer timer; - run_select(&database, params, std::cout); - std::cerr << "select: " << timer.elapsed() - << " [s] elapsed" << std::endl; - } else if (command == "count") { - grnxx::Timer timer; - std::stringstream stream; - run_select(&database, params, stream); - std::cerr << "count: " << timer.elapsed() - << " [s] elapsed" << std::endl; - } else if (command == "quit") { - break; - } else { - std::cerr << "Error: unknown command: " - << "command= " << command << std::endl; - } - } - } -} - void print_version() { - std::cout << grnxx::Library::name() << ' ' - << grnxx::Library::version() << "\n\n"; - - std::cout << "options:"; - if (grnxx::Library::enable_varint()) { - std::cout << " varint"; - } + std::cout << grnxx::Library::package() << ' ' + << grnxx::Library::version(); + // TODO: Enumerate valid options. +// std::cout << "\n\noptions:"; std::cout << std::endl; } void print_usage(int argc, char *argv[]) { std::cout << "Usage: " << argv[0] << " [OPTION]...\n\n" "Options:\n" - " -v, --verbose: enable verbose mode\n" " -h, --help: print this help\n" - " -V, --version: print grnxx version\n" + " -v, --version: print grnxx version\n" << std::flush; } @@ -1015,24 +27,19 @@ void print_usage(int argc, char *argv[]) { int main(int argc, char *argv[]) { const struct option long_options[] = { - { "verbose", 0, nullptr, 'v' }, { "help", 0, nullptr, 'h' }, - { "version", 0, nullptr, 'V' }, + { "version", 0, nullptr, 'v' }, { nullptr, 0, nullptr, 0 } }; int value; - while ((value = ::getopt_long(argc, argv, "vhV", + while ((value = ::getopt_long(argc, argv, "hv", long_options, nullptr)) != -1) { switch (value) { - case 'v': { - verbose_mode = true; - break; - } case 'h': { print_usage(argc, argv); return 0; } - case 'V': { + case 'v': { print_version(); return 0; } @@ -1042,6 +49,5 @@ int main(int argc, char *argv[]) { } } } - run_terminal(); return 0; } Modified: test/benchmark_grnxx.cpp (+7 -1) =================================================================== --- test/benchmark_grnxx.cpp 2014-06-25 17:55:59 +0900 (f807017) +++ test/benchmark_grnxx.cpp 2014-07-04 12:25:36 +0900 (e2f53cf) @@ -17,8 +17,14 @@ */ #include <cassert> -#include "grnxx/library.hpp" +namespace { + +// TODO + +} // namespace int main() { + // TODO + return 0; } Modified: test/test_grnxx.cpp (+123 -1497) =================================================================== --- test/test_grnxx.cpp 2014-06-25 17:55:59 +0900 (134b660) +++ test/test_grnxx.cpp 2014-07-04 12:25:36 +0900 (cde8282) @@ -17,1530 +17,156 @@ */ #include <cassert> -#include "grnxx/library.hpp" -#include <cassert> -#include <iostream> -#include <random> -#include <vector> - -#include "grnxx/calc.hpp" #include "grnxx/column.hpp" -#include "grnxx/column_impl.hpp" -#include "grnxx/database.hpp" -#include "grnxx/index.hpp" -#include "grnxx/sorter.hpp" +#include "grnxx/datum.hpp" +#include "grnxx/db.hpp" +#include "grnxx/error.hpp" #include "grnxx/table.hpp" namespace { -void test_database() { - grnxx::Database database; - - assert(database.min_table_id() == grnxx::MIN_TABLE_ID); - assert(database.max_table_id() == (grnxx::MIN_TABLE_ID - 1)); - - grnxx::Table *table = database.create_table("Table_1"); - assert(table); - - grnxx::TableID table_id = table->id(); - assert(table_id == grnxx::MIN_TABLE_ID); - grnxx::String table_name = table->name(); - assert(table_name == "Table_1"); - - table = database.get_table_by_id(table_id); - assert(table->id() == table_id); - assert(table->name() == table_name); - - table = database.get_table_by_name(table_name); - assert(table->id() == table_id); - assert(table->name() == table_name); - - assert(!database.create_table("Table_1")); - - assert(database.create_table("Table_2")); - assert(database.create_table("Table_3")); - assert(database.drop_table("Table_2")); - - std::vector<grnxx::Table *> tables; - assert(database.get_tables(&tables) == 2); - - assert(tables[0]->name() == "Table_1"); - assert(tables[1]->name() == "Table_3"); - - assert(database.min_table_id() == tables[0]->id()); - assert(database.max_table_id() == tables[1]->id()); -} - -void test_table() { - grnxx::Database database; - - grnxx::Table *table = database.create_table("Table"); - assert(table); - - assert(table->min_column_id() == grnxx::MIN_COLUMN_ID); - assert(table->max_column_id() == (grnxx::MIN_COLUMN_ID - 1)); - - grnxx::Column *column = table->create_column("Column_1", grnxx::INTEGER); - assert(column); - - grnxx::ColumnID column_id = column->id(); - assert(column_id == grnxx::MIN_COLUMN_ID); - grnxx::String column_name = column->name(); - assert(column_name == "Column_1"); - - column = table->get_column_by_id(column_id); - assert(column->id() == column_id); - assert(column->name() == column_name); - - column = table->get_column_by_name(column_name); - assert(column->id() == column_id); - assert(column->name() == column_name); - - grnxx::Index *index = - table->create_index("Index_1", "Column_1", grnxx::TREE_MAP); - assert(index); - - grnxx::IndexID index_id = index->id(); - assert(index_id == grnxx::MIN_INDEX_ID); - grnxx::String index_name = index->name(); - assert(index_name == "Index_1"); - - index = table->get_index_by_id(index_id); - assert(index->id() == index_id); - assert(index->name() == index_name); - - index = table->get_index_by_name(index_name); - assert(index->id() == index_id); - assert(index->name() == index_name); - - assert(!table->create_index("Index_1", "Column_1", grnxx::TREE_MAP)); - - assert(table->create_column("Column_2", grnxx::FLOAT)); - assert(table->create_column("Column_3", grnxx::STRING)); - assert(table->create_index("Index_2", "Column_2", grnxx::TREE_MAP)); - assert(table->create_index("Index_3", "Column_3", grnxx::TREE_MAP)); - assert(table->drop_column("Column_2")); - assert(table->drop_index("Index_3")); - - std::vector<grnxx::Column *> columns; - assert(table->get_columns(&columns) == 2); - - assert(columns[0]->name() == "Column_1"); - assert(columns[1]->name() == "Column_3"); - - assert(table->min_column_id() == columns[0]->id()); - assert(table->max_column_id() == columns[1]->id()); - - std::vector<grnxx::Index *> indexes; - assert(table->get_indexes(&indexes) == 1); - - assert(indexes[0]->name() == "Index_1"); - - for (grnxx::Int64 i = 0; i < 100; ++i) { - assert(table->insert_row() == (grnxx::MIN_ROW_ID + i)); - assert(table->min_row_id() == grnxx::MIN_ROW_ID); - assert(table->max_row_id() == (grnxx::MIN_ROW_ID + i)); - } - - std::unique_ptr<grnxx::RowIDCursor> cursor(table->create_cursor()); - assert(cursor); - - std::vector<grnxx::RowID> row_ids(110); - assert(cursor->get_next(&row_ids[0], 10) == 10); - assert(cursor->get_next(&row_ids[10], 100) == 90); - for (grnxx::Int64 i = 0; i < 100; ++i) { - assert(row_ids[i] == (grnxx::MIN_ROW_ID + i)); - } -} - -void test_column() { - grnxx::Database database; - - grnxx::Table *table = database.create_table("Table"); - assert(table); - - auto boolean_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Boolean> *>( - table->create_column("Boolean", grnxx::BOOLEAN)); - assert(boolean_column); - assert(boolean_column->name() == "Boolean"); - assert(boolean_column->data_type() == grnxx::BOOLEAN); - - auto integer_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Int64> *>( - table->create_column("Integer", grnxx::INTEGER)); - assert(integer_column); - assert(integer_column->name() == "Integer"); - assert(integer_column->data_type() == grnxx::INTEGER); - - auto float_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Float> *>( - table->create_column("Float", grnxx::FLOAT)); - assert(float_column); - assert(float_column->name() == "Float"); - assert(float_column->data_type() == grnxx::FLOAT); - - auto string_column = dynamic_cast<grnxx::ColumnImpl<grnxx::String> *>( - table->create_column("String", grnxx::STRING)); - assert(string_column); - assert(string_column->name() == "String"); - assert(string_column->data_type() == grnxx::STRING); - - for (grnxx::Int64 row_id = grnxx::MIN_ROW_ID; row_id <= 1000; ++row_id) { - assert(table->insert_row() == row_id); - boolean_column->set(row_id, row_id & 1); - integer_column->set(row_id, row_id); - float_column->set(row_id, 1.0 / row_id); - std::string str = std::to_string(row_id); - string_column->set(row_id, str); - } - - for (grnxx::Int64 row_id = table->min_row_id(); - row_id <= table->max_row_id(); ++row_id) { - assert(boolean_column->get(row_id) == grnxx::Boolean(row_id & 1)); - assert(integer_column->get(row_id) == grnxx::Int64(row_id)); - assert(float_column->get(row_id) == grnxx::Float(1.0 / row_id)); - std::string str = std::to_string(row_id); - assert(string_column->get(row_id) == str); - } - - std::vector<grnxx::RowID> row_ids = { 1, 5, 10, 50, 100, 500 }; - table->write_to(std::cout, &*row_ids.begin(), row_ids.size(), - "_id,Integer,Float,String") << std::endl; - table->write_to(std::cout, &*row_ids.begin(), row_ids.size(), - "*") << std::endl; - - std::vector<grnxx::Int64> boundaries = { 2, 4, 6 }; - table->write_to(std::cout, &*row_ids.begin(), row_ids.size(), boundaries, - "*") << std::endl; -} - -void test_calc() { - grnxx::Database database; - - grnxx::Table *table = database.create_table("Table"); - assert(table); - - auto boolean_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Boolean> *>( - table->create_column("Boolean", grnxx::BOOLEAN)); - assert(boolean_column); - auto integer_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Int64> *>( - table->create_column("Integer", grnxx::INTEGER)); - assert(integer_column); - auto float_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Float> *>( - table->create_column("Float", grnxx::FLOAT)); - assert(float_column); - auto string_column = dynamic_cast<grnxx::ColumnImpl<grnxx::String> *>( - table->create_column("String", grnxx::STRING)); - assert(string_column); - - std::mt19937_64 random; - std::vector<grnxx::Boolean> boolean_data; - std::vector<grnxx::Int64> integer_data; - std::vector<grnxx::Float> float_data; - std::vector<std::string> string_data; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - boolean_data.push_back(random() & 1); - integer_data.push_back(random() % 100); - float_data.push_back(1.0 * random() / random.max()); - std::string str(1 + (random() % 10), 'A'); - for (std::size_t i = 0; i < str.size(); ++i) { - str[i] += random() % 26; - } - string_data.push_back(str); - } - - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = table->insert_row(); - boolean_column->set(row_id, boolean_data[i]); - integer_column->set(row_id, integer_data[i]); - float_column->set(row_id, float_data[i]); - string_column->set(row_id, string_data[i]); - } - - std::vector<grnxx::RowID> all_row_ids(1000); - std::unique_ptr<grnxx::RowIDCursor> cursor(table->create_cursor()); - assert(cursor->get_next(&all_row_ids[0], 1000) == 1000); - - // 何もしない. - { - std::unique_ptr<grnxx::Calc> calc(table->create_calc("")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids == 1000); - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - assert(row_ids[i] == row_id); - } - } - - // 定数ですべて破棄する. - { - std::unique_ptr<grnxx::Calc> calc(table->create_calc("!TRUE")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids == 0); - } - - // Boolean で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc(table->create_calc("Boolean")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if (boolean_data[i]) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // Boolean で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc(table->create_calc("!Boolean")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if (!boolean_data[i]) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // Integer の範囲で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc(table->create_calc("Integer < 50")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if (integer_data[i] < 50) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // Integer の範囲で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc(table->create_calc("!(Integer < 50)")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if (!(integer_data[i] < 50)) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // Boolean と Integer と Float の範囲で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc( - table->create_calc("Boolean && Integer < 50 && Float < 0.5")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if (boolean_data[i] && (integer_data[i] < 50) && (float_data[i] < 0.5)) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // Boolean と Integer と String の範囲で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc( - table->create_calc("(Boolean && Integer >= 50) || (String <= \"A\")")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if ((boolean_data[i] && (integer_data[i] >= 50)) || - (string_data[i] <= "A")) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // Integer の演算結果で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc( - table->create_calc("(Integer * 2) > 100")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if ((integer_data[i] * 2) > 100) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // Integer の演算結果で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc( - table->create_calc("(Integer == 0) || (100 / Integer) > 10")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if ((integer_data[i] == 0) || ((100 / integer_data[i]) > 10)) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // Float の演算結果で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc( - table->create_calc("(Float + 1.0) < 1.5")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if ((float_data[i] + 1.0) < 1.5) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // ゼロによる除算を起こす. - { - std::unique_ptr<grnxx::Calc> calc( - table->create_calc("Integer / Integer != 0")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids == 0); - } - - // ゼロによる除算を回避する. - { - std::unique_ptr<grnxx::Calc> calc( - table->create_calc("!((Integer != 0) && ((100 / Integer) == 0))")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if ((integer_data[i] == 0) || ((100 / integer_data[i]) != 0)) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - } - - // ゼロによる除算を回避する. - { - std::unique_ptr<grnxx::Calc> calc( - table->create_calc("((Integer == 0) || ((100 / Integer) != 0)) == TRUE")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if ((integer_data[i] == 0) || ((100 / integer_data[i]) != 0)) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - } - - // オーバーフローを起こす. - { - std::unique_ptr<grnxx::Calc> calc( - table->create_calc("9223372036854775807 + 9223372036854775807 != 0")); - assert(!calc); - } - - // || の左がすべて真になる. - { - std::unique_ptr<grnxx::Calc> calc( - table->create_calc("Integer <= 100 || Float < 0.5")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if ((integer_data[i] <= 100) || (float_data[i] < 0.5)) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // || の左がすべて偽になる. - { - std::unique_ptr<grnxx::Calc> calc( - table->create_calc("Integer < 0 || Float < 0.5")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if ((integer_data[i] < 0) || (float_data[i] < 0.5)) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // || の右がすべて真になる. - { - std::unique_ptr<grnxx::Calc> calc( - table->create_calc("Integer < 50 || Float >= 0.0")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if ((integer_data[i] < 50) || (float_data[i] >= 0.0)) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // || の右がすべて偽になる. - { - std::unique_ptr<grnxx::Calc> calc( - table->create_calc("Integer < 50 || Float < 0.0")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if ((integer_data[i] < 50) || (float_data[i] < 0.0)) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } -} - -void test_reference() { - grnxx::Database database; - - grnxx::Table *src_table = database.create_table("Src"); - assert(src_table); - grnxx::Table *dest_table = database.create_table("Dest"); - assert(dest_table); - - auto reference_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Int64> *>( - src_table->create_reference_column("Reference", "Dest")); - assert(reference_column); - - auto boolean_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Boolean> *>( - dest_table->create_column("Boolean", grnxx::BOOLEAN)); - assert(boolean_column); - auto integer_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Int64> *>( - dest_table->create_column("Integer", grnxx::INTEGER)); - assert(integer_column); - auto float_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Float> *>( - dest_table->create_column("Float", grnxx::FLOAT)); - assert(float_column); - - std::mt19937_64 random; - - std::vector<grnxx::Int64> boolean_data; - std::vector<grnxx::Int64> integer_data; - std::vector<grnxx::Int64> float_data; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - boolean_data.push_back(random() & 1); - integer_data.push_back(random() % 100); - float_data.push_back(1.0 * random() / random.max()); - grnxx::RowID row_id = dest_table->insert_row(); - boolean_column->set(row_id, boolean_data[i]); - integer_column->set(row_id, integer_data[i]); - float_column->set(row_id, float_data[i]); - } - - std::vector<grnxx::Int64> reference_data; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = src_table->insert_row(); - reference_data.push_back(random() % 1000); - reference_column->set(row_id, reference_data[i] + 1); - } - - std::vector<grnxx::RowID> all_row_ids(1000); - std::unique_ptr<grnxx::RowIDCursor> cursor(src_table->create_cursor()); - assert(cursor->get_next(&all_row_ids[0], 1000) == 1000); - - // Boolean で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc( - src_table->create_calc("Reference.Boolean")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if (boolean_data[reference_data[i]]) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // Boolean で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc( - src_table->create_calc("!Reference.Boolean")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if (!boolean_data[reference_data[i]]) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // Integer で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc( - src_table->create_calc("Reference.Integer < 50")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if (integer_data[reference_data[i]] < 50) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // Float で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc( - src_table->create_calc("Reference.Float <= 0.5")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if (float_data[reference_data[i]] <= 0.5) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - // Boolean と Integer と Float の範囲で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc(src_table->create_calc( - "Reference.Boolean && Reference.Integer < 50 && Reference.Float < 0.5")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if (boolean_data[reference_data[i]] && - (integer_data[reference_data[i]] < 50) && - (float_data[reference_data[i]] < 0.5)) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } -} - -void test_deep_reference() { - grnxx::Database database; - - grnxx::Table *first_table = database.create_table("First"); - assert(first_table); - grnxx::Table *second_table = database.create_table("Second"); - assert(second_table); - grnxx::Table *third_table = database.create_table("Third"); - assert(third_table); - - auto third_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Float> *>( - third_table->create_column("Third", grnxx::FLOAT)); - assert(third_column); - auto second_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Int64> *>( - second_table->create_reference_column("Second", "Third")); - assert(second_column); - auto first_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Int64> *>( - first_table->create_reference_column("First", "Second")); - assert(first_column); - - std::mt19937_64 random; - - std::vector<grnxx::Float> third_data; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = third_table->insert_row(); - third_data.push_back(1.0 * random() / random.max()); - third_column->set(row_id, third_data[i]); - } +void test_db() { + grnxx::Error error; - std::vector<grnxx::Int64> second_data; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = second_table->insert_row(); - second_data.push_back(random() % 1000); - second_column->set(row_id, second_data[i] + 1); - } - - std::vector<grnxx::Int64> first_data; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = first_table->insert_row(); - first_data.push_back(random() % 1000); - first_column->set(row_id, first_data[i] + 1); - } - - std::vector<grnxx::RowID> all_row_ids(1000); - std::unique_ptr<grnxx::RowIDCursor> cursor(first_table->create_cursor()); - assert(cursor->get_next(&all_row_ids[0], 1000) == 1000); - - // Third で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc(third_table->create_calc("Third > 0.5")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if (third_data[i] > 0.5) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - cursor.reset(second_table->create_cursor()); - assert(cursor->get_next(&all_row_ids[0], 1000) == 1000); - - // Second.Third で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc( - second_table->create_calc("Second.Third > 0.5")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if (third_data[second_data[i]] > 0.5) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - cursor.reset(third_table->create_cursor()); - assert(cursor->get_next(&all_row_ids[0], 1000) == 1000); - - // First.Second.Third で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc( - first_table->create_calc("First.Second.Third > 0.5")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if (third_data[second_data[first_data[i]]] > 0.5) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } -} - -void test_primary_key() { - grnxx::Database database; - - grnxx::Table *src_table = database.create_table("Src"); - assert(src_table); - grnxx::Table *dest_table = database.create_table("Dest"); - assert(dest_table); - - auto reference_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Int64> *>( - src_table->create_reference_column("Reference", "Dest")); - assert(reference_column); - - auto key_column = dynamic_cast<grnxx::ColumnImpl<grnxx::String> *>( - dest_table->create_column("Key", grnxx::STRING)); - assert(key_column); - assert(dest_table->set_primary_key("Key")); - - std::mt19937_64 random; - - std::vector<std::string> key_data; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - std::string str(8, 'A'); - for (std::size_t i = 0; i < str.size(); ++i) { - str[i] += random() % 26; - } - key_data.push_back(str); - grnxx::RowID row_id = dest_table->insert_row(); - key_column->set(row_id, key_data[i]); - } - - std::vector<grnxx::Int64> reference_data; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - reference_data.push_back(random() % 1000); - grnxx::RowID row_id = src_table->insert_row(); - reference_column->set(row_id, reference_data[i] + 1); - } - - std::vector<grnxx::RowID> all_row_ids(1000); - std::unique_ptr<grnxx::RowIDCursor> cursor(src_table->create_cursor()); - assert(cursor->get_next(&all_row_ids[0], 1000) == 1000); - - // Reference.Key で絞り込む. - { - std::unique_ptr<grnxx::Calc> calc( - src_table->create_calc("Reference.Key > \"N\"")); - assert(calc); - std::vector<grnxx::RowID> row_ids(all_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - assert(num_row_ids != 0); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = grnxx::MIN_ROW_ID + i; - if (key_data[reference_data[i]] > "N") { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } - - for (grnxx::Int64 i = 0; i < 1000; ++i) { - assert(dest_table->find_row(key_data[i]) == (i + 1)); - } - for (grnxx::Int64 i = 0; i < 1000; ++i) { - std::string str(8, 'a'); - for (std::size_t i = 0; i < str.size(); ++i) { - str[i] += random() % 26; - } - assert(dest_table->find_row(str) == 0); - } -} + auto db = grnxx::open_db(&error, "", grnxx::DBOptions()); + assert(db); + assert(db->num_tables() == 0); -void test_sorter() { - constexpr grnxx::Int64 DATA_SIZE = 1000; - - grnxx::Database database; - - grnxx::Table *table = database.create_table("Table"); + auto table = db->create_table(&error, "Table_1", grnxx::TableOptions()); assert(table); + assert(table->name() == "Table_1"); + assert(db->num_tables() == 1); - auto boolean_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Boolean> *>( - table->create_column("Boolean", grnxx::BOOLEAN)); - assert(boolean_column); - auto integer_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Int64> *>( - table->create_column("Integer", grnxx::INTEGER)); - assert(integer_column); - auto float_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Float> *>( - table->create_column("Float", grnxx::FLOAT)); - assert(float_column); - auto string_column = dynamic_cast<grnxx::ColumnImpl<grnxx::String> *>( - table->create_column("String", grnxx::STRING)); - assert(string_column); - - std::mt19937_64 random; - for (grnxx::Int64 i = 0; i < DATA_SIZE; ++i) { - grnxx::RowID row_id = table->insert_row(); - boolean_column->set(row_id, random() & 1); - integer_column->set(row_id, random() % 100); - float_column->set(row_id, 1.0 * random() / random.max()); - std::string str(1 + (random() % 10), 'A'); - for (std::size_t i = 0; i < str.size(); ++i) { - str[i] += random() % 26; - } - string_column->set(row_id, str); - } - - std::vector<grnxx::RowID> all_row_ids(DATA_SIZE); - std::unique_ptr<grnxx::RowIDCursor> cursor(table->create_cursor()); - assert(cursor->get_next(&all_row_ids[0], DATA_SIZE) == DATA_SIZE); - - // Boolean を基準に整列する. - { - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter(table->create_sorter("Boolean")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size()); - for (grnxx::Int64 i = 1; i < DATA_SIZE; ++i) { - assert(boolean_column->get(row_ids[i - 1]) <= - boolean_column->get(row_ids[i])); - } - } - - // Integer を基準に整列する. - { - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter(table->create_sorter("Integer")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size()); - for (grnxx::Int64 i = 1; i < DATA_SIZE; ++i) { - assert(integer_column->get(row_ids[i - 1]) <= - integer_column->get(row_ids[i])); - } - } - - // Float を基準に整列する. - { - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter(table->create_sorter("Float")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size()); - for (grnxx::Int64 i = 1; i < DATA_SIZE; ++i) { - assert(float_column->get(row_ids[i - 1]) <= - float_column->get(row_ids[i])); - } - } - - // String を基準に整列する. - { - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter(table->create_sorter("String")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size()); - for (grnxx::Int64 i = 1; i < DATA_SIZE; ++i) { - assert(string_column->get(row_ids[i - 1]) <= - string_column->get(row_ids[i])); - } - } - - // Boolean を基準に整列する. - { - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter(table->create_sorter("-Boolean")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size()); - for (grnxx::Int64 i = 1; i < DATA_SIZE; ++i) { - assert(boolean_column->get(row_ids[i - 1]) >= - boolean_column->get(row_ids[i])); - } - } - - // Integer を基準に整列する. - { - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter(table->create_sorter("-Integer")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size()); - for (grnxx::Int64 i = 1; i < DATA_SIZE; ++i) { - assert(integer_column->get(row_ids[i - 1]) >= - integer_column->get(row_ids[i])); - } - } + assert(db->get_table(&error, 0) == table); + assert(db->find_table(&error, "Table_1") == table); - // Float を基準に整列する. - { - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter(table->create_sorter("-Float")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size()); - for (grnxx::Int64 i = 1; i < DATA_SIZE; ++i) { - assert(float_column->get(row_ids[i - 1]) >= - float_column->get(row_ids[i])); - } - } + assert(!db->create_table(&error, "Table_1", grnxx::TableOptions())); - // String を基準に整列する. - { - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter(table->create_sorter("-String")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size()); - for (grnxx::Int64 i = 1; i < DATA_SIZE; ++i) { - assert(string_column->get(row_ids[i - 1]) >= - string_column->get(row_ids[i])); - } - } + assert(db->create_table(&error, "Table_2", grnxx::TableOptions())); + assert(db->create_table(&error, "Table_3", grnxx::TableOptions())); + assert(db->num_tables() == 3); - // Integer を基準に整列して 100 件目まで取得する. - { - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter(table->create_sorter("Integer")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size(), 0, 100); - for (grnxx::Int64 i = 1; i < 100; ++i) { - assert(integer_column->get(row_ids[i - 1]) <= - integer_column->get(row_ids[i])); - } - } + assert(db->remove_table(&error, "Table_2")); + assert(db->num_tables() == 2); - // Integer を基準に整列して,先頭の 100 件をスキップしてから 200 件目まで取得する. - { - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter(table->create_sorter("Integer")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size(), 100, 200); - for (grnxx::Int64 i = 101; i < 300; ++i) { - assert(integer_column->get(row_ids[i - 1]) <= - integer_column->get(row_ids[i])); - } - } + assert(db->get_table(&error, 0)->name() == "Table_1"); + assert(db->get_table(&error, 1)->name() == "Table_3"); - // Boolean, Integer, Float を基準に整列する. - { - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter( - table->create_sorter("Boolean,Integer,-Float")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size()); - for (grnxx::Int64 i = 1; i < DATA_SIZE; ++i) { - assert(boolean_column->get(row_ids[i - 1]) <= - boolean_column->get(row_ids[i])); - if (boolean_column->get(row_ids[i - 1]) == - boolean_column->get(row_ids[i])) { - assert(integer_column->get(row_ids[i - 1]) <= - integer_column->get(row_ids[i])); - if (integer_column->get(row_ids[i - 1]) == - integer_column->get(row_ids[i])) { - assert(float_column->get(row_ids[i - 1]) >= - float_column->get(row_ids[i])); - } - } - } - } + assert(db->create_table(&error, "Table_2", grnxx::TableOptions())); - // Boolean, Integer, String を基準に整列して 500 件目までを取得する. - { - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter( - table->create_sorter("Boolean,Integer,-String")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size(), 0, 500); - for (grnxx::Int64 i = 1; i < 500; ++i) { - assert(boolean_column->get(row_ids[i - 1]) <= - boolean_column->get(row_ids[i])); - if (boolean_column->get(row_ids[i - 1]) == - boolean_column->get(row_ids[i])) { - assert(integer_column->get(row_ids[i - 1]) <= - integer_column->get(row_ids[i])); - if (integer_column->get(row_ids[i - 1]) == - integer_column->get(row_ids[i])) { - assert(string_column->get(row_ids[i - 1]) >= - string_column->get(row_ids[i])); - } - } - } - } + assert(db->reorder_table(&error, "Table_3", "Table_2")); + assert(db->get_table(&error, 0)->name() == "Table_1"); + assert(db->get_table(&error, 1)->name() == "Table_2"); + assert(db->get_table(&error, 2)->name() == "Table_3"); - // Boolean, Integer, _id を基準に整列する. - std::vector<grnxx::RowID> answer; - { - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter( - table->create_sorter("Boolean,-Integer,-_id")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size()); - for (grnxx::Int64 i = 1; i < DATA_SIZE; ++i) { - assert(boolean_column->get(row_ids[i - 1]) <= - boolean_column->get(row_ids[i])); - if (boolean_column->get(row_ids[i - 1]) == - boolean_column->get(row_ids[i])) { - assert(integer_column->get(row_ids[i - 1]) >= - integer_column->get(row_ids[i])); - if (integer_column->get(row_ids[i - 1]) == - integer_column->get(row_ids[i])) { - assert(row_ids[i - 1] > row_ids[i]); - } - } - } - answer = row_ids; - } + assert(db->reorder_table(&error, "Table_3", "")); + assert(db->get_table(&error, 0)->name() == "Table_3"); + assert(db->get_table(&error, 1)->name() == "Table_1"); + assert(db->get_table(&error, 2)->name() == "Table_2"); - // Boolean, Integer, _id を基準に整列する. - { - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter( - table->create_sorter("Boolean,-Integer,-_id")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size(), 800, 100); - for (grnxx::Int64 i = 800; i < 900; ++i) { - assert(row_ids[i] == answer[i]); - } - } + assert(db->reorder_table(&error, "Table_2", "Table_3")); + assert(db->get_table(&error, 0)->name() == "Table_3"); + assert(db->get_table(&error, 1)->name() == "Table_2"); + assert(db->get_table(&error, 2)->name() == "Table_1"); } -void test_sorter_large() { - constexpr grnxx::Int64 DATA_SIZE = 100000; - - grnxx::Database database; - - grnxx::Table *table = database.create_table("Table"); - assert(table); - - auto less_than_10 = dynamic_cast<grnxx::ColumnImpl<grnxx::Int64> *>( - table->create_column("LessThan10", grnxx::INTEGER)); - assert(less_than_10); - auto less_than_100 = dynamic_cast<grnxx::ColumnImpl<grnxx::Int64> *>( - table->create_column("LessThan100", grnxx::INTEGER)); - assert(less_than_100); - auto less_than_1000 = dynamic_cast<grnxx::ColumnImpl<grnxx::Int64> *>( - table->create_column("LessThan1000", grnxx::INTEGER)); - assert(less_than_1000); - - std::mt19937_64 random; - for (grnxx::Int64 i = 0; i < DATA_SIZE; ++i) { - grnxx::RowID row_id = table->insert_row(); - less_than_10->set(row_id, random() % 10); - less_than_100->set(row_id, random() % 100); - less_than_1000->set(row_id, random() % 1000); - } - - std::vector<grnxx::RowID> all_row_ids(DATA_SIZE); - std::unique_ptr<grnxx::RowIDCursor> cursor(table->create_cursor()); - assert(cursor->get_next(&all_row_ids[0], DATA_SIZE) == DATA_SIZE); - - std::vector<grnxx::RowID> answer; - { - // LessThan10, LessThan100, LessThan1000 を基準に整列する. - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter( - table->create_sorter("LessThan10,LessThan100,LessThan1000,_id")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size()); - for (grnxx::Int64 i = 1; i < DATA_SIZE; ++i) { - assert(less_than_10->get(row_ids[i - 1]) <= - less_than_10->get(row_ids[i])); - if (less_than_10->get(row_ids[i - 1]) == - less_than_10->get(row_ids[i])) { - assert(less_than_100->get(row_ids[i - 1]) <= - less_than_100->get(row_ids[i])); - if (less_than_100->get(row_ids[i - 1]) == - less_than_100->get(row_ids[i])) { - assert(less_than_1000->get(row_ids[i - 1]) <= - less_than_1000->get(row_ids[i])); - if (less_than_1000->get(row_ids[i - 1]) == - less_than_1000->get(row_ids[i])) { - assert(row_ids[i - 1] < row_ids[i]); - } - } - } - } - // 重複がないことを確認する. - std::vector<bool> bitmap(DATA_SIZE, false); - for (grnxx::RowID row_id : row_ids) { - assert(!bitmap[row_id]); - bitmap[row_id] = true; - } - answer = row_ids; - } - - { - // LessThan10, LessThan100, LessThan1000 を基準に整列する. - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter( - table->create_sorter("LessThan10,LessThan100,LessThan1000,_id")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size(), 0, DATA_SIZE / 2); - for (grnxx::Int64 i = 0; i < (DATA_SIZE / 2); ++i) { - assert(row_ids[i] == answer[i]); - } - } - - { - // LessThan10, LessThan100, LessThan1000 を基準に整列する. - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter( - table->create_sorter("LessThan10,LessThan100,LessThan1000,_id")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size(), - DATA_SIZE / 2, DATA_SIZE / 2); - for (grnxx::Int64 i = DATA_SIZE / 2; i < DATA_SIZE; ++i) { - assert(row_ids[i] == answer[i]); - } - } - - { - // LessThan10, LessThan100, LessThan1000 を基準に整列する. - std::vector<grnxx::RowID> row_ids(all_row_ids); - std::unique_ptr<grnxx::Sorter> sorter( - table->create_sorter("LessThan10,LessThan100,LessThan1000,_id")); - assert(sorter); - sorter->sort(&*row_ids.begin(), row_ids.size(), - DATA_SIZE / 4, DATA_SIZE / 2); - for (grnxx::Int64 i = DATA_SIZE / 4; i < (DATA_SIZE / 4 * 3); ++i) { - assert(row_ids[i] == answer[i]); - } - } -} +void test_table() { + grnxx::Error error; -void test_index() { - grnxx::Database database; + auto db = grnxx::open_db(&error, "", grnxx::DBOptions()); + assert(db); - grnxx::Table *table = database.create_table("Table"); + auto table = db->create_table(&error, "Table", grnxx::TableOptions()); assert(table); - - auto boolean_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Boolean> *>( - table->create_column("Boolean", grnxx::BOOLEAN)); - assert(boolean_column); - auto integer_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Int64> *>( - table->create_column("Integer", grnxx::INTEGER)); - assert(integer_column); - auto float_column = dynamic_cast<grnxx::ColumnImpl<grnxx::Float> *>( - table->create_column("Float", grnxx::FLOAT)); - assert(float_column); - auto string_column = dynamic_cast<grnxx::ColumnImpl<grnxx::String> *>( - table->create_column("String", grnxx::STRING)); - assert(string_column); - - auto integer_index = - table->create_index("Integer", "Integer", grnxx::TREE_MAP); - assert(integer_index); - auto float_index = - table->create_index("Float", "Float", grnxx::TREE_MAP); - assert(float_index); - auto string_index = - table->create_index("String", "String", grnxx::TREE_MAP); - assert(string_index); - - std::mt19937_64 random; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = table->insert_row(); - boolean_column->set(row_id, random() & 1); - integer_column->set(row_id, random() % 100); - float_column->set(row_id, 1.0 * random() / random.max()); - std::string str(1 + (random() % 10), 'A'); - for (std::size_t i = 0; i < str.size(); ++i) { - str[i] += random() % 26; - } - string_column->set(row_id, str); - } - - // Integer 昇順に全件. - { - std::unique_ptr<grnxx::RowIDCursor> cursor(integer_index->find_all()); - assert(cursor); - std::vector<grnxx::RowID> row_ids(1000); - assert(cursor->get_next(&row_ids[0], 1000) == 1000); - auto value = integer_column->get(row_ids[0]); - assert(value >= 0); - assert(value < 100); - for (std::size_t i = 1; i < row_ids.size(); ++i) { - auto prev_value = value; - value = integer_column->get(row_ids[i]); - assert(value >= 0); - assert(value < 100); - assert(value >= prev_value); - prev_value = value; - } - } - - // Integer 昇順に 30 より大きく 70 より小さい範囲. - { - std::unique_ptr<grnxx::RowIDCursor> cursor( - integer_index->find_between(grnxx::Int64(30), grnxx::Int64(70), - false, false)); - assert(cursor); - std::vector<grnxx::RowID> row_ids(1000); - row_ids.resize(cursor->get_next(&row_ids[0], 1000)); - assert(row_ids.size() > 100); - auto value = integer_column->get(row_ids[0]); - assert(value > 30); - assert(value < 70); - for (std::size_t i = 1; i < row_ids.size(); ++i) { - auto prev_value = value; - value = integer_column->get(row_ids[i]); - assert(value > 30); - assert(value < 70); - assert(value >= prev_value); - prev_value = value; - } - } - - // Integer 昇順に 30 以上 70 以下の範囲. - { - std::unique_ptr<grnxx::RowIDCursor> cursor( - integer_index->find_between(grnxx::Int64(30), grnxx::Int64(70), - true, true)); - assert(cursor); - std::vector<grnxx::RowID> row_ids(1000); - row_ids.resize(cursor->get_next(&row_ids[0], 1000)); - assert(row_ids.size() > 100); - auto value = integer_column->get(row_ids[0]); - assert(value >= 30); - assert(value <= 70); - for (std::size_t i = 1; i < row_ids.size(); ++i) { - auto prev_value = value; - value = integer_column->get(row_ids[i]); - assert(value >= 30); - assert(value <= 70); - assert(value >= prev_value); - prev_value = value; - } - } - - // Float 昇順に 0.3 より大きく 0.7 より小さい範囲. - { - std::unique_ptr<grnxx::RowIDCursor> cursor( - float_index->find_between(0.3, 0.7)); - assert(cursor); - std::vector<grnxx::RowID> row_ids(1000); - row_ids.resize(cursor->get_next(&row_ids[0], 1000)); - assert(row_ids.size() > 100); - auto value = float_column->get(row_ids[0]); - assert(value > 0.3); - assert(value < 0.7); - for (std::size_t i = 1; i < row_ids.size(); ++i) { - auto prev_value = value; - value = float_column->get(row_ids[i]); - assert(value > 0.3); - assert(value < 0.7); - assert(value >= prev_value); - prev_value = value; - } - } - - // String 昇順に "G" より大きく "P" より小さい範囲. - { - std::unique_ptr<grnxx::RowIDCursor> cursor( - string_index->find_between("G", "P")); - assert(cursor); - std::vector<grnxx::RowID> row_ids(1000); - row_ids.resize(cursor->get_next(&row_ids[0], 1000)); - assert(row_ids.size() > 100); - auto value = string_column->get(row_ids[0]); - assert(value > "G"); - assert(value < "P"); - for (std::size_t i = 1; i < row_ids.size(); ++i) { - auto prev_value = value; - value = string_column->get(row_ids[i]); - assert(value > "G"); - assert(value < "P"); - assert(value >= prev_value); - prev_value = value; - } - } - - // Integer 降順に全件. - { - std::unique_ptr<grnxx::RowIDCursor> cursor(integer_index->find_all(true)); - assert(cursor); - std::vector<grnxx::RowID> row_ids(1000); - assert(cursor->get_next(&row_ids[0], 1000) == 1000); - auto value = integer_column->get(row_ids[0]); - assert(value >= 0); - assert(value < 100); - for (std::size_t i = 1; i < row_ids.size(); ++i) { - auto prev_value = value; - value = integer_column->get(row_ids[i]); - assert(value >= 0); - assert(value < 100); - assert(value <= prev_value); - prev_value = value; - } - } - - // Integer 降順に 30 より大きく 70 より小さい範囲. - { - std::unique_ptr<grnxx::RowIDCursor> cursor( - integer_index->find_between(grnxx::Int64(30), grnxx::Int64(70), - false, false, true)); - assert(cursor); - std::vector<grnxx::RowID> row_ids(1000); - row_ids.resize(cursor->get_next(&row_ids[0], 1000)); - assert(row_ids.size() > 100); - auto value = integer_column->get(row_ids[0]); - assert(value > 30); - assert(value < 70); - for (std::size_t i = 1; i < row_ids.size(); ++i) { - auto prev_value = value; - value = integer_column->get(row_ids[i]); - assert(value > 30); - assert(value < 70); - assert(value <= prev_value); - prev_value = value; - } - } - - // Integer 降順に 30 以上 70 以下の範囲. - { - std::unique_ptr<grnxx::RowIDCursor> cursor( - integer_index->find_between(grnxx::Int64(30), grnxx::Int64(70), - true, true, true)); - assert(cursor); - std::vector<grnxx::RowID> row_ids(1000); - row_ids.resize(cursor->get_next(&row_ids[0], 1000)); - assert(row_ids.size() > 100); - auto value = integer_column->get(row_ids[0]); - assert(value >= 30); - assert(value <= 70); - for (std::size_t i = 1; i < row_ids.size(); ++i) { - auto prev_value = value; - value = integer_column->get(row_ids[i]); - assert(value >= 30); - assert(value <= 70); - assert(value <= prev_value); - prev_value = value; - } - } - - // Float 降順に 0.3 より大きく 0.7 より小さい範囲. - { - std::unique_ptr<grnxx::RowIDCursor> cursor( - float_index->find_between(0.3, 0.7, false, false, true)); - assert(cursor); - std::vector<grnxx::RowID> row_ids(1000); - row_ids.resize(cursor->get_next(&row_ids[0], 1000)); - assert(row_ids.size() > 100); - auto value = float_column->get(row_ids[0]); - assert(value > 0.3); - assert(value < 0.7); - for (std::size_t i = 1; i < row_ids.size(); ++i) { - auto prev_value = value; - value = float_column->get(row_ids[i]); - assert(value > 0.3); - assert(value < 0.7); - assert(value <= prev_value); - prev_value = value; - } - } - - // String 降順に "G" より大きく "P" より小さい範囲. - { - std::unique_ptr<grnxx::RowIDCursor> cursor( - string_index->find_between("G", "P", false, false, true)); - assert(cursor); - std::vector<grnxx::RowID> row_ids(1000); - row_ids.resize(cursor->get_next(&row_ids[0], 1000)); - assert(row_ids.size() > 100); - auto value = string_column->get(row_ids[0]); - assert(value > "G"); - assert(value < "P"); - for (std::size_t i = 1; i < row_ids.size(); ++i) { - auto prev_value = value; - value = string_column->get(row_ids[i]); - assert(value > "G"); - assert(value < "P"); - assert(value <= prev_value); - prev_value = value; - } - } - - // Integer 昇順で論理和が使えることを確認する. - { - std::unique_ptr<grnxx::RowIDCursor> cursor(integer_index->find_all()); - assert(cursor); - std::vector<grnxx::RowID> ordered_row_ids(1000); - assert(cursor->get_next(&ordered_row_ids[0], 1000) == 1000); - std::unique_ptr<grnxx::Calc> calc( - table->create_calc("(Boolean && Integer >= 50) || (String <= \"O\")")); - assert(calc); - std::vector<grnxx::RowID> row_ids(ordered_row_ids); - grnxx::Int64 num_row_ids = calc->filter(&*row_ids.begin(), row_ids.size()); - grnxx::Int64 count = 0; - for (grnxx::Int64 i = 0; i < 1000; ++i) { - grnxx::RowID row_id = ordered_row_ids[i]; - if ((boolean_column->get(row_id) && - (integer_column->get(row_id) >= 50)) || - (string_column->get(row_id) <= "O")) { - assert(row_ids[count] == row_id); - assert(++count <= num_row_ids); - } - } - assert(count == num_row_ids); - } + assert(table->db() == db.get()); + assert(table->name() == "Table"); + assert(table->num_columns() == 0); + assert(!table->key_column()); + assert(table->max_row_id() == 0); + + auto column = table->create_column(&error, "Column_1", grnxx::BOOL_DATA, + grnxx::ColumnOptions()); + assert(column); + assert(column->name() == "Column_1"); + assert(table->num_columns() == 1); + + assert(table->get_column(&error, 0) == column); + assert(table->find_column(&error, "Column_1") == column); + + assert(!table->create_column(&error, "Column_1", grnxx::BOOL_DATA, + grnxx::ColumnOptions())); + + assert(table->create_column(&error, "Column_2", grnxx::BOOL_DATA, + grnxx::ColumnOptions())); + assert(table->create_column(&error, "Column_3", grnxx::BOOL_DATA, + grnxx::ColumnOptions())); + assert(table->num_columns() == 3); + + assert(table->remove_column(&error, "Column_2")); + assert(table->num_columns() == 2); + + assert(table->get_column(&error, 0)->name() == "Column_1"); + assert(table->get_column(&error, 1)->name() == "Column_3"); + + assert(table->create_column(&error, "Column_2", grnxx::BOOL_DATA, + grnxx::ColumnOptions())); + + assert(table->reorder_column(&error, "Column_3", "Column_2")); + assert(table->get_column(&error, 0)->name() == "Column_1"); + assert(table->get_column(&error, 1)->name() == "Column_2"); + assert(table->get_column(&error, 2)->name() == "Column_3"); + + assert(table->reorder_column(&error, "Column_3", "")); + assert(table->get_column(&error, 0)->name() == "Column_3"); + assert(table->get_column(&error, 1)->name() == "Column_1"); + assert(table->get_column(&error, 2)->name() == "Column_2"); + + assert(table->reorder_column(&error, "Column_2", "Column_3")); + assert(table->get_column(&error, 0)->name() == "Column_3"); + assert(table->get_column(&error, 1)->name() == "Column_2"); + assert(table->get_column(&error, 2)->name() == "Column_1"); + + // TODO: set_key_column(), unset_key_column(). + + grnxx::Int row_id; + assert(table->insert_row(&error, grnxx::NULL_ROW_ID, + grnxx::Datum(), &row_id)); + assert(row_id == 1); + assert(table->max_row_id() == 1); + assert(!table->test_row(&error, 0)); + assert(table->test_row(&error, 1)); + assert(!table->test_row(&error, 2)); + + assert(table->insert_row(&error, grnxx::NULL_ROW_ID, + grnxx::Datum(), &row_id)); + assert(table->insert_row(&error, grnxx::NULL_ROW_ID, + grnxx::Datum(), &row_id)); + assert(row_id == 3); + assert(table->max_row_id() == 3); + assert(!table->test_row(&error, 0)); + assert(table->test_row(&error, 1)); + assert(table->test_row(&error, 2)); + assert(table->test_row(&error, 3)); + assert(!table->test_row(&error, 4)); + + assert(table->remove_row(&error, 2)); + assert(table->max_row_id() == 3); + assert(!table->test_row(&error, 0)); + assert(table->test_row(&error, 1)); + assert(!table->test_row(&error, 2)); + assert(table->test_row(&error, 3)); + assert(!table->test_row(&error, 4)); + + // TODO: find_row(). + + // TODO: create_cursor(). } } // namespace int main() { - test_database(); + test_db(); test_table(); - test_column(); - test_calc(); - test_reference(); - test_deep_reference(); - test_primary_key(); - test_sorter(); - test_sorter_large(); - test_index(); + return 0; }