[Groonga-commit] groonga/groonga at 38b65e3 [master] grn_ts: reimplementation with C

Back to archive index

susumu.yata null+****@clear*****
Wed Sep 2 10:58:34 JST 2015


susumu.yata	2015-09-02 10:58:34 +0900 (Wed, 02 Sep 2015)

  New Revision: 38b65e3591e494e9bc57a865e1009f5239156745
  https://github.com/groonga/groonga/commit/38b65e3591e494e9bc57a865e1009f5239156745

  Message:
    grn_ts: reimplementation with C

  Added files:
    lib/ts.c
  Removed files:
    lib/grn_ts.hpp
    lib/ts.cpp
  Modified files:
    lib/CMakeLists.txt
    lib/grn_ts.h
    lib/sources.am

  Modified: lib/CMakeLists.txt (+1 -1)
===================================================================
--- lib/CMakeLists.txt    2015-09-01 21:58:28 +0900 (17995ea)
+++ lib/CMakeLists.txt    2015-09-02 10:58:34 +0900 (1a7da11)
@@ -36,7 +36,7 @@ string(REGEX REPLACE "([^;]+)" "mrb/\\1"
 set_source_files_properties(${LIBGROONGA_SOURCES} ${LIBGRNMRB_SOURCES}
   PROPERTIES
   COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
-set_source_files_properties(dat.cpp ts.cpp ${LIBGRNDAT_SOURCES}
+set_source_files_properties(dat.cpp ${LIBGRNDAT_SOURCES}
   PROPERTIES
   COMPILE_FLAGS "${GRN_CXX_COMPILE_FLAGS}")
 

  Modified: lib/grn_ts.h (+287 -36)
===================================================================
--- lib/grn_ts.h    2015-09-01 21:58:28 +0900 (4f19e64)
+++ lib/grn_ts.h    2015-09-02 10:58:34 +0900 (4504c7d)
@@ -19,58 +19,93 @@
 #ifndef GRN_TS_H
 #define GRN_TS_H
 
+#ifdef GRN_WITH_TS
+
+#include <stddef.h>
+#include <stdint.h>
+
 #include "grn.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-// Constant values.
+/*-------------------------------------------------------------
+ * Enumeration types.
+ */
 
+/* grn_builtin_type or table ID. */
 typedef grn_id grn_ts_data_type;
 
+enum { GRN_TS_VECTOR_FLAG = 1 << 7 };
+
 typedef enum {
-  GRN_TS_VOID,      // GRN_DB_VOID.
-  GRN_TS_BOOL,      // GRN_DB_BOOL.
-  GRN_TS_INT,       // GRN_DB_(U)INT8/16/32/64.
-  GRN_TS_FLOAT,     // GRN_DB_FLOAT.
-  GRN_TS_TIME,      // GRN_DB_TIME.
-  GRN_TS_TEXT,      // GRN_DB_(SHORT_/LONG_)TEST.
-  GRN_TS_GEO_POINT, // GRN_DB_TOKYO/WGS84_GEO_POINT.
-  GRN_TS_REF        // Table reference.
+  GRN_TS_VOID             = 0, /* GRN_DB_VOID */
+  GRN_TS_BOOL             = 1, /* GRN_DB_BOOL */
+  GRN_TS_INT              = 2, /* GRN_DB_[U]INT(8/16/32/64) */
+  GRN_TS_FLOAT            = 3, /* GRN_DB_FLOAT */
+  GRN_TS_TIME             = 4, /* GRN_DB_TIME */
+  GRN_TS_TEXT             = 5, /* GRN_DB_[SHORT_/LONG_]TEST */
+  GRN_TS_GEO_POINT        = 6, /* GRN_DB_(TOKYO/WGS84)_GEO_POINT */
+  GRN_TS_REF              = 7, /* Table reference. */
+  GRN_TS_BOOL_VECTOR      = GRN_TS_VECTOR_FLAG | GRN_TS_BOOL,
+  GRN_TS_INT_VECTOR       = GRN_TS_VECTOR_FLAG | GRN_TS_INT,
+  GRN_TS_FLOAT_VECTOR     = GRN_TS_VECTOR_FLAG | GRN_TS_FLOAT,
+  GRN_TS_TIME_VECTOR      = GRN_TS_VECTOR_FLAG | GRN_TS_TIME,
+  GRN_TS_TEXT_VECTOR      = GRN_TS_VECTOR_FLAG | GRN_TS_TEXT,
+  GRN_TS_GEO_POINT_VECTOR = GRN_TS_VECTOR_FLAG | GRN_TS_GEO_POINT,
+  GRN_TS_REF_VECTOR       = GRN_TS_VECTOR_FLAG | GRN_TS_REF
 } grn_ts_data_kind;
 
 typedef enum {
-  GRN_TS_NOP,
-  GRN_TS_LOGICAL_NOT,
-  GRN_TS_LOGICAL_AND,
-  GRN_TS_LOGICAL_OR,
-  GRN_TS_EQUAL,
-  GRN_TS_NOT_EQUAL,
-  GRN_TS_LESS,
-  GRN_TS_LESS_EQUAL,
-  GRN_TS_GREATER,
-  GRN_TS_GREATER_EQUAL
-} grn_ts_operator_type;
+  /* Invalid operator. */
+  GRN_TS_OP_NOP,
+
+  /* Unary operators. */
+  GRN_TS_OP_LOGICAL_NOT,  /* ! */
+  GRN_TS_OP_BITWISE_NOT,  /* ~ */
+  GRN_TS_OP_POSITIVE,     /* + */
+  GRN_TS_OP_NEGATIVE,     /* - */
+
+  /* Binary operators. */
+  GRN_TS_OP_LOGICAL_AND,  /* && */
+  GRN_TS_OP_LOGICAL_OR,   /* || */
+  GRN_TS_OP_BITWISE_AND,  /* & */
+  GRN_TS_OP_BITWISE_OR,   /* | */
+  GRN_TS_OP_BITWISE_XOR,  /* ^ */
+  GRN_TS_OP_EQUAL,        /* == */
+  GRN_TS_OP_NOT_EQUAL,    /* != */
+  GRN_TS_OP_LESS,         /* < */
+  GRN_TS_OP_LESS_EQUAL,   /* <= */
+  GRN_TS_OP_GREATER,      /* > */
+  GRN_TS_OP_GREATER_EQUAL /* >= */
+} grn_ts_op_type;
 
 typedef enum {
-  GRN_TS_ID_NODE,
-  GRN_TS_SCORE_NODE,
-  GRN_TS_CONSTANT_NODE,
-  GRN_TS_COLUMN_NODE,
-  GRN_TS_OPERATOR_NODE
-} grn_ts_expression_node_type;
+  GRN_TS_EXPR_INCOMPLETE, /* An incomplete expression. */
+  GRN_TS_EXPR_BROKEN,     /* A broken expression. */
+                          /* Any operation fails for a broken expression. */
+  GRN_TS_EXPR_ID,         /* An expression associated with _id. */
+  GRN_TS_EXPR_SCORE,      /* An expression associated with _score. */
+  GRN_TS_EXPR_CONST,      /* A const. */
+  GRN_TS_EXPR_VARIABLE    /* An expression that contains a variable. */
+} grn_ts_expr_type;
 
 typedef enum {
-  GRN_TS_INCOMPLETE,
-  GRN_TS_ID,
-  GRN_TS_SCORE,
-  GRN_TS_CONSTANT,
-  GRN_TS_VARIABLE
-} grn_ts_expression_type;
+  GRN_TS_EXPR_ID_NODE,     /* Associated with the ID (_id). */
+  GRN_TS_EXPR_SCORE_NODE,  /* Asscoaited with the score (_score). */
+  GRN_TS_EXPR_KEY_NODE,    /* Asscoaited with the key (_key). */
+  GRN_TS_EXPR_VALUE_NODE,  /* Asscoaited with the embedded value (_value). */
+  GRN_TS_EXPR_CONST_NODE,  /* Associated with a const. */
+  GRN_TS_EXPR_COLUMN_NODE, /* Associated with a column. */
+  GRN_TS_EXPR_OP_NODE      /* Associated with an operator. */
+} grn_ts_expr_node_type;
 
-// Built-in data types.
+/*-------------------------------------------------------------
+ * Built-in data types.
+ */
 
+/* ID (_id), score (_score), and record types. */
 typedef grn_id grn_ts_id;
 typedef float grn_ts_score;
 typedef struct {
@@ -78,6 +113,7 @@ typedef struct {
   grn_ts_score score;
 } grn_ts_record;
 
+/* Built-in scalar data types. */
 typedef grn_bool grn_ts_bool;
 typedef int64_t grn_ts_int;
 typedef double grn_ts_float;
@@ -87,10 +123,223 @@ typedef struct {
   size_t size;
 } grn_ts_text;
 typedef grn_geo_point grn_ts_geo_point;
+typedef grn_geo_point grn_ts_tokyo_geo_point;
+typedef grn_geo_point grn_ts_wgs84_geo_point;
+typedef grn_ts_record grn_ts_ref;
+
+/* Built-in vector data types. */
+typedef struct {
+  const grn_ts_bool *ptr;
+  size_t size;
+} grn_ts_bool_vector;
+typedef struct {
+  const grn_ts_int *ptr;
+  size_t size;
+} grn_ts_int_vector;
+typedef struct {
+  const grn_ts_float *ptr;
+  size_t size;
+} grn_ts_float_vector;
+typedef struct {
+  const grn_ts_time *ptr;
+  size_t size;
+} grn_ts_time_vector;
 typedef struct {
-  const void *ptr;
+  const grn_ts_text *ptr;
   size_t size;
-} grn_ts_vector;
+} grn_ts_text_vector;
+typedef struct {
+  const grn_ts_geo_point *ptr;
+  size_t size;
+} grn_ts_geo_point_vector;
+typedef grn_ts_geo_point_vector grn_ts_tokyo_geo_point_vector;
+typedef grn_ts_geo_point_vector grn_ts_wgs84_geo_point_vector;
+typedef struct {
+  const grn_ts_ref *ptr;
+  size_t size;
+} grn_ts_ref_vector;
+
+/*-------------------------------------------------------------
+ * Expression components.
+ */
+
+#define GRN_TS_EXPR_NODE_COMMON_MEMBERS\
+  grn_ts_expr_node_type type; /* Node type. */\
+  grn_ts_data_kind data_kind; /* Abstract data type. */\
+  grn_ts_data_type data_type; /* Detailed data type. */
+
+typedef struct {
+  GRN_TS_EXPR_NODE_COMMON_MEMBERS
+} grn_ts_expr_node;
+
+typedef struct {
+  grn_obj *table;             /* Associated table. */
+  grn_obj *curr_table;        /* Current table. */
+  grn_ts_expr_type type;      /* Expression type. */
+  grn_ts_data_kind data_kind; /* Abstract data type. */
+  grn_ts_data_type data_type; /* Detailed data type. */
+  grn_ts_expr_node *root;     /* Root node. */
+  grn_ts_expr_node **nodes;   /* Buffer for a node stack. */
+  size_t n_nodes;             /* Number of nodes. */
+  size_t max_n_nodes;         /* Max. number (capacity) of nodes. */
+  grn_ts_expr_node **stack;   /* Node stack. */
+  size_t stack_depth;         /* Node stack's current depth. */
+  size_t stack_size;          /* Node stack's size (capacity). */
+} grn_ts_expr;
+
+/* grn_ts_expr_open() creates an empty expression. */
+grn_rc grn_ts_expr_open(grn_ctx *ctx, grn_obj *table, grn_ts_expr **expr);
+
+/* grn_ts_expr_parse() creates an expression from a string. */
+grn_rc grn_ts_expr_parse(grn_ctx *ctx, grn_obj *table,
+                         const char *str, size_t str_size,
+                         grn_ts_expr **expr);
+
+/* grn_ts_expr_close() destroys an expression. */
+grn_rc grn_ts_expr_close(grn_ctx *ctx, grn_ts_expr *expr);
+
+/*
+ * grn_ts_expr_get_table() returns the associated table.
+ * If arguments are invalid, the return value is NULL.
+ */
+grn_obj *grn_ts_expr_get_table(grn_ctx *ctx, grn_ts_expr *expr);
+
+/*
+ * grn_ts_expr_get_type() returns the expression type.
+ * If arguments are invalid, the return value is GRN_EXPR_BROKEN.
+ */
+grn_ts_expr_type grn_ts_expr_get_type(grn_ctx *ctx, grn_ts_expr *expr);
+
+/*
+ * grn_ts_expr_get_data_kind() returns the data kind.
+ * If arguments are invalid, the return value is GRN_TS_VOID.
+ */
+grn_ts_data_kind grn_ts_expr_get_data_kind(grn_ctx *ctx, grn_ts_expr *expr);
+
+/*
+ * grn_ts_expr_get_data_type() returns the data type.
+ * If arguments are invalid, the return value is GRN_DB_VOID.
+ */
+
+grn_ts_data_type grn_ts_expr_get_data_type(grn_ctx *ctx, grn_ts_expr *expr);
+
+/*
+ * grn_ts_expr_get_root() returns the root node.
+ * If arguments are invalid, the return value is NULL.
+ */
+grn_ts_expr_node *grn_ts_expr_get_root(grn_ctx *ctx, grn_ts_expr *expr);
+
+/* grn_ts_expr_push() pushes expression nodes expressed by a string. */
+grn_rc grn_ts_expr_push(grn_ctx *ctx, grn_ts_expr *expr,
+                        const char *str, size_t str_size);
+
+/*
+ * grn_ts_expr_push_obj() pushes an object.
+ *
+ * Acceptable objects are as follows:
+ * - Consts
+ *  - GRN_BULK: GRN_DB_*.
+ *  - GRN_UVECTOR: GRN_DB_* except GRN_DB_[SHORT/LONG_]TEXT.
+ *  - GRN_VECTOR: GRN_DB_[SHORT/LONG_]TEXT.
+ * - Columns
+ *  - GRN_ACCESSOR: _id, _score, _key, and _value.
+ *  - GRN_COLUMN_FIX_SIZE: GRN_DB_* except GRN_DB_[SHORT/LONG_]TEXT.
+ *  - GRN_COLUMN_VAR_SIZE: GRN_DB_[SHORT/LONG_]TEXT.
+ */
+grn_rc grn_ts_expr_push_obj(grn_ctx *ctx, grn_ts_expr *expr, grn_obj *obj);
+
+/* grn_ts_expr_push_id() pushes "_id". */
+grn_rc grn_ts_expr_push_id(grn_ctx *ctx, grn_ts_expr *expr);
+/* grn_ts_expr_push_score() pushes "_score". */
+grn_rc grn_ts_expr_push_score(grn_ctx *ctx, grn_ts_expr *expr);
+/* grn_ts_expr_push_key() pushes "_key". */
+grn_rc grn_ts_expr_push_key(grn_ctx *ctx, grn_ts_expr *expr);
+/* grn_ts_expr_push_key() pushes "_value". */
+grn_rc grn_ts_expr_push_value(grn_ctx *ctx, grn_ts_expr *expr);
+/* grn_ts_expr_push_const() pushes a column. */
+grn_rc grn_ts_expr_push_const(grn_ctx *ctx, grn_ts_expr *expr,
+                              grn_ts_data_kind kind, const void *value);
+/* grn_ts_expr_push_column() pushes a column. */
+grn_rc grn_ts_expr_push_column(grn_ctx *ctx, grn_ts_expr *expr,
+                               grn_obj *column);
+/* grn_ts_expr_push_operator() pushes an operator. */
+grn_rc grn_ts_expr_push_operator(grn_ctx *ctx, grn_ts_expr *expr,
+                                 grn_ts_op_type op_type);
+
+/* grn_ts_expr_push_bool() pushes a Bool const. */
+grn_rc grn_ts_expr_push_bool(grn_ctx *ctx, grn_ts_expr *expr,
+                             grn_ts_bool value);
+/* grn_ts_expr_push_int() pushes an Int64 const. */
+grn_rc grn_ts_expr_push_int(grn_ctx *ctx, grn_ts_expr *expr,
+                            grn_ts_int value);
+/* grn_ts_expr_push_float() pushes a Float const. */
+grn_rc grn_ts_expr_push_float(grn_ctx *ctx, grn_ts_expr *expr,
+                              grn_ts_float value);
+/* grn_ts_expr_push_time() pushes a Time const. */
+grn_rc grn_ts_expr_push_time(grn_ctx *ctx, grn_ts_expr *expr,
+                             grn_ts_time value);
+/* grn_ts_expr_push_text() pushes a Text const. */
+grn_rc grn_ts_expr_push_text(grn_ctx *ctx, grn_ts_expr *expr,
+                             grn_ts_text value);
+/* grn_ts_expr_push_geo_point() pushes a GeoPoint const. */
+grn_rc grn_ts_expr_push_geo_point(grn_ctx *ctx, grn_ts_expr *expr,
+                                  grn_ts_geo_point value);
+/* grn_ts_expr_push_tokyo_geo_point() pushes a TokyoGeoPoint const. */
+grn_rc grn_ts_expr_push_tokyo_geo_point(grn_ctx *ctx, grn_ts_expr *expr,
+                                        grn_ts_geo_point value);
+/* grn_ts_expr_push_wgs84_geo_point() pushes a WGS84GeoPoint const. */
+grn_rc grn_ts_expr_push_wgs84_geo_point(grn_ctx *ctx, grn_ts_expr *expr,
+                                        grn_ts_geo_point value);
+/* grn_ts_expr_push_bool_vector() pushes a Bool vector const. */
+grn_rc grn_ts_expr_push_bool_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                                    grn_ts_bool_vector value);
+/* grn_ts_expr_push_int_vector() pushes an Int64 vector const. */
+grn_rc grn_ts_expr_push_int_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                                   grn_ts_int_vector value);
+/* grn_ts_expr_push_float_vector() pushes a Float vector const. */
+grn_rc grn_ts_expr_push_float_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                                     grn_ts_float_vector value);
+/* grn_ts_expr_push_time_vector() pushes a Time vector const. */
+grn_rc grn_ts_expr_push_time_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                                    grn_ts_time_vector value);
+/* grn_ts_expr_push_text_vector() pushes a Text vector const. */
+grn_rc grn_ts_expr_push_text_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                                    grn_ts_text_vector value);
+/* grn_ts_expr_push_geo_point_vector() pushes a GeoPoint vector const. */
+grn_rc grn_ts_expr_push_geo_point_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                                         grn_ts_geo_point_vector value);
+/*
+ * grn_ts_expr_push_tokyo_geo_point_vector() pushes a TokyoGeoPoint vector
+ * const.
+ */
+grn_rc grn_ts_expr_push_tokyo_geo_point_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                                               grn_ts_geo_point_vector value);
+/*
+ * grn_ts_expr_push_wgs84_geo_point_vector() pushes a WGS84GeoPoint vector
+ * const.
+ */
+grn_rc grn_ts_expr_push_wgs84_geo_point_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                                               grn_ts_geo_point_vector value);
+
+/* grn_rc grn_ts_expr_complete() completes an expression. */
+grn_rc grn_ts_expr_complete(grn_ctx *ctx, grn_ts_expr *expr);
+
+/* grn_ts_expr_evaluate() evaluates an expression. */
+grn_rc grn_ts_expr_evaluate(grn_ctx *ctx, grn_ts_expr *expr,
+                            const grn_ts_record *in, size_t n_in, void *out);
+
+/* grn_ts_expr_filter() filters records. */
+grn_rc grn_ts_expr_filter(grn_ctx *ctx, grn_ts_expr *expr,
+                          grn_ts_record *in, size_t n_in,
+                          grn_ts_record *out, size_t *n_out);
+
+/* grn_ts_expr_adjust() updates scores. */
+grn_rc grn_ts_expr_adjust(grn_ctx *ctx, grn_ts_expr *expr,
+                          grn_ts_record *io, size_t n_io);
+
+/*-------------------------------------------------------------
+ * API.
+ */
 
 /*
  * grn_ts_select() finds records passing through a filter (specified by
@@ -108,10 +357,12 @@ typedef struct {
 grn_rc grn_ts_select(grn_ctx *ctx, grn_obj *table,
                      const char *filter, size_t filter_size,
                      const char *output_columns, size_t output_columns_size,
-                     int offset, int limit);
+                     size_t offset, size_t limit);
 
 #ifdef __cplusplus
 }
 #endif
 
+#endif /* GRN_WITH_TS */
+
 #endif /* GRN_TS_H */

  Deleted: lib/grn_ts.hpp (+0 -250) 100644
===================================================================
--- lib/grn_ts.hpp    2015-09-01 21:58:28 +0900 (631fc49)
+++ /dev/null
@@ -1,250 +0,0 @@
-/* -*- c-basic-offset: 2 -*- */
-/*
-  Copyright(C) 2015 Brazil
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License version 2.1 as published by the Free Software Foundation.
-
-  This library is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with this library; if not, write to the Free Software
-  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#ifndef GRN_TS_HPP
-#define GRN_TS_HPP
-
-#include <cstring>
-#include <vector>
-
-#include "grn_ts.h"
-
-namespace grn {
-namespace ts {
-
-// Constant values.
-
-typedef grn_ts_operator_type OperatorType;
-typedef grn_ts_data_type DataType;
-typedef grn_ts_data_kind DataKind;
-
-typedef grn_ts_expression_node_type ExpressionNodeType;
-typedef grn_ts_expression_type ExpressionType;
-
-// Built-in data types.
-
-typedef grn_ts_id ID;
-typedef grn_ts_score Score;
-typedef grn_ts_record Record;
-
-struct Bool {
-  typedef grn_ts_bool Raw;
-  Raw raw;
-
-  Bool() : raw() {}
-  Bool(const Bool &value) : raw(value.raw) {}
-  explicit Bool(Raw value) : raw(value) {}
-  ~Bool() {}
-};
-
-inline bool operator!(Bool value) { return !value.raw; }
-inline bool operator==(Bool lhs, Bool rhs) { return lhs.raw == rhs.raw; }
-inline bool operator!=(Bool lhs, Bool rhs) { return lhs.raw != rhs.raw; }
-
-struct Int {
-  typedef grn_ts_int Raw;
-  Raw raw;
-
-  Int() : raw() {}
-  Int(const Int &value) : raw(value.raw) {}
-  explicit Int(Raw value) : raw(value) {}
-  ~Int() {}
-};
-
-inline bool operator==(Int lhs, Int rhs) { return lhs.raw == rhs.raw; }
-inline bool operator!=(Int lhs, Int rhs) { return lhs.raw != rhs.raw; }
-inline bool operator<(Int lhs, Int rhs) { return lhs.raw < rhs.raw; }
-inline bool operator<=(Int lhs, Int rhs) { return lhs.raw <= rhs.raw; }
-inline bool operator>(Int lhs, Int rhs) { return lhs.raw > rhs.raw; }
-inline bool operator>=(Int lhs, Int rhs) { return lhs.raw >= rhs.raw; }
-
-struct Float {
-  typedef grn_ts_float Raw;
-  Raw raw;
-
-  Float() : raw() {}
-  Float(const Float &value) : raw(value.raw) {}
-  explicit Float(Raw value) : raw(value) {}
-  ~Float() {}
-};
-
-inline bool operator==(Float lhs, Float rhs) {
-  return (lhs.raw <= rhs.raw) && (lhs.raw >= rhs.raw);
-}
-inline bool operator!=(Float lhs, Float rhs) {
-  return (lhs.raw < rhs.raw) || (lhs.raw > rhs.raw);
-}
-inline bool operator<(Float lhs, Float rhs) { return lhs.raw < rhs.raw; }
-inline bool operator<=(Float lhs, Float rhs) { return lhs.raw <= rhs.raw; }
-inline bool operator>(Float lhs, Float rhs) { return lhs.raw > rhs.raw; }
-inline bool operator>=(Float lhs, Float rhs) { return lhs.raw >= rhs.raw; }
-
-struct Time {
-  typedef grn_ts_time Raw;
-  Raw raw;
-
-  Time() : raw() {}
-  Time(const Time &value) : raw(value.raw) {}
-  explicit Time(Raw value) : raw(value) {}
-  ~Time() {}
-};
-
-inline bool operator==(Time lhs, Time rhs) { return lhs.raw == rhs.raw; }
-inline bool operator!=(Time lhs, Time rhs) { return lhs.raw != rhs.raw; }
-inline bool operator<(Time lhs, Time rhs) { return lhs.raw < rhs.raw; }
-inline bool operator<=(Time lhs, Time rhs) { return lhs.raw <= rhs.raw; }
-inline bool operator>(Time lhs, Time rhs) { return lhs.raw > rhs.raw; }
-inline bool operator>=(Time lhs, Time rhs) { return lhs.raw >= rhs.raw; }
-
-struct Text {
-  typedef grn_ts_text Raw;
-  Raw raw;
-
-  Text() : raw() {}
-  Text(const Text &value) : raw(value.raw) {}
-  explicit Text(const Raw &value) : raw(value) {}
-  Text(const char *ptr, size_t size) : raw((Raw){ptr, size}) {}
-  ~Text() {}
-};
-
-inline bool operator==(const Text &lhs, const Text &rhs) {
-  if (lhs.raw.size != rhs.raw.size) {
-    return false;
-  }
-  return std::memcmp(lhs.raw.ptr, rhs.raw.ptr, lhs.raw.size) == 0;
-}
-inline bool operator!=(const Text &lhs, const Text &rhs) {
-  if (lhs.raw.size != rhs.raw.size) {
-    return true;
-  }
-  return std::memcmp(lhs.raw.ptr, rhs.raw.ptr, lhs.raw.size) != 0;
-}
-inline bool operator<(const Text &lhs, const Text &rhs) {
-  size_t min_size = (lhs.raw.size < rhs.raw.size) ?
-                    lhs.raw.size : rhs.raw.size;
-  int result = std::memcmp(lhs.raw.ptr, rhs.raw.ptr, min_size);
-  if (result == 0) {
-    return lhs.raw.size < rhs.raw.size;
-  }
-  return result < 0;
-}
-inline bool operator<=(const Text &lhs, const Text &rhs) {
-  size_t min_size = (lhs.raw.size < rhs.raw.size) ?
-                    lhs.raw.size : rhs.raw.size;
-  int result = std::memcmp(lhs.raw.ptr, rhs.raw.ptr, min_size);
-  if (result == 0) {
-    return lhs.raw.size <= rhs.raw.size;
-  }
-  return result <= 0;
-}
-inline bool operator>(const Text &lhs, const Text &rhs) { return rhs < lhs; }
-inline bool operator>=(const Text &lhs, const Text &rhs) { return rhs <= lhs; }
-
-struct GeoPoint {
-  typedef grn_ts_geo_point Raw;
-  Raw raw;
-
-  GeoPoint() : raw() {}
-  GeoPoint(const GeoPoint &value) : raw(value.raw) {}
-  explicit GeoPoint(Raw value) : raw(value) {}
-  GeoPoint(int latitude, int longitude) : raw((Raw){ latitude, longitude }) {}
-  ~GeoPoint() {}
-};
-
-inline bool operator==(GeoPoint lhs, GeoPoint rhs) {
-  return (lhs.raw.latitude == rhs.raw.latitude) &&
-         (lhs.raw.longitude == rhs.raw.longitude);
-}
-inline bool operator!=(GeoPoint lhs, GeoPoint rhs) {
-  return (lhs.raw.latitude != rhs.raw.latitude) ||
-         (lhs.raw.longitude != rhs.raw.longitude);
-}
-
-// Cursor is a base class which provides an interface for sequential access to
-// records.
-class Cursor {
- public:
-  Cursor() {}
-  virtual ~Cursor() {}
-
-  // FIXME: Give me options.
-  static grn_rc open_table_cursor(grn_ctx *ctx, grn_obj *table,
-                                  Cursor **cursor);
-
-  virtual grn_rc read(Record *records, size_t size, size_t *count);
-};
-
-// ExpressionNode is an element of Expression.
-class ExpressionNode;
-
-// Expression is a class which represents an expression.
-class Expression {
- public:
-  Expression(grn_ctx *ctx, grn_obj *table);
-  ~Expression();
-
-  static grn_rc open(grn_ctx *ctx, grn_obj *table, Expression **expression);
-  static grn_rc parse(grn_ctx *ctx, grn_obj *table,
-                      const char *query, size_t query_size,
-                      Expression **expression);
-
-  ExpressionType type() const {
-    return type_;
-  }
-  DataKind data_kind() const;
-  DataType data_type() const;
-  DataType output_type() const;
-  int dimension() const;
-
-  grn_rc push_object(grn_obj *obj);
-  grn_rc push_operator(OperatorType operator_type);
-
-  grn_rc filter(Record *input, size_t input_size,
-                Record *output, size_t *output_size);
-  grn_rc adjust(Record *records, size_t num_records);
-
-  grn_rc evaluate(const Record *records, size_t num_records, void *results);
-
- private:
-  grn_ctx *ctx_;
-  grn_obj *table_;
-  ExpressionType type_;
-  DataType output_type_;
-  std::vector<ExpressionNode *> stack_;
-
-  // Disable copy and assignment.
-  Expression(const Expression &);
-  Expression &operator=(const Expression &);
-
-  ExpressionNode *root() const;
-
-  void update_type();
-
-  grn_rc push_constant_object(grn_obj *obj);
-  grn_rc push_column_object(grn_obj *obj);
-
-  grn_rc create_unary_node(OperatorType operator_type,
-    ExpressionNode *arg, ExpressionNode **node);
-  grn_rc create_binary_node(OperatorType operator_type,
-    ExpressionNode *arg1, ExpressionNode *arg2, ExpressionNode **node);
-};
-
-}  // namespace ts
-}  // namespace grn
-
-#endif  // GRN_TS_HPP

  Modified: lib/sources.am (+1 -2)
===================================================================
--- lib/sources.am    2015-09-01 21:58:28 +0900 (8aea371)
+++ lib/sources.am    2015-09-02 10:58:34 +0900 (70306da)
@@ -11,9 +11,8 @@ libgroonga_la_SOURCES =				\
 	grn_dat.h				\
 	db.c					\
 	grn_db.h				\
-	ts.cpp					\
+	ts.c					\
 	grn_ts.h				\
-	grn_ts.hpp				\
 	error.c					\
 	grn_error.h				\
 	expr.c					\

  Added: lib/ts.c (+2962 -0) 100644
===================================================================
--- /dev/null
+++ lib/ts.c    2015-09-02 10:58:34 +0900 (daf8fee)
@@ -0,0 +1,2962 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+  Copyright(C) 2015 Brazil
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License version 2.1 as published by the Free Software Foundation.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+/* TS is an acronym for "Turbo Selector". */
+
+#ifdef GRN_WITH_TS
+
+#include "grn_ts.h"
+
+#include <ctype.h>
+#include <math.h>
+#include <string.h>
+
+#include "grn_ctx_impl.h"
+#include "grn_db.h"
+#include "grn_geo.h"
+#include "grn_output.h"
+#include "grn_store.h"
+#include "grn_str.h"
+
+/*-------------------------------------------------------------
+ * Miscellaneous.
+ */
+
+enum { GRN_TS_BATCH_SIZE = 1024 };
+
+/* grn_ts_bool_is_valid() returns whether a value is valid or not. */
+inline static grn_bool
+grn_ts_bool_is_valid(grn_ts_bool value) {
+  return GRN_TRUE;
+}
+
+/* grn_ts_int_is_valid() returns whether a value is valid or not. */
+inline static grn_bool
+grn_ts_int_is_valid(grn_ts_int value) {
+  return GRN_TRUE;
+}
+
+/* grn_ts_float_is_valid() returns whether a value is valid or not. */
+inline static grn_bool
+grn_ts_float_is_valid(grn_ts_float value) {
+  return !isnan(value);
+}
+
+/* grn_ts_time_is_valid() returns whether a value is valid or not. */
+inline static grn_bool
+grn_ts_time_is_valid(grn_ts_time value) {
+  return GRN_TRUE;
+}
+
+/* grn_ts_text_is_valid() returns whether a value is valid or not. */
+inline static grn_bool
+grn_ts_text_is_valid(grn_ts_text value) {
+  return value.ptr || !value.size;
+}
+
+/* grn_ts_geo_point_is_valid() returns whether a value is valid or not. */
+inline static grn_bool
+grn_ts_geo_point_is_valid(grn_ts_geo_point value) {
+  return ((value.latitude >= GRN_GEO_MIN_LATITUDE) &&
+          (value.latitude <= GRN_GEO_MAX_LATITUDE)) &&
+         ((value.longitude >= GRN_GEO_MIN_LONGITUDE) &&
+          (value.longitude <= GRN_GEO_MAX_LONGITUDE));
+}
+
+#define GRN_TS_VECTOR_IS_VALID(type)\
+  if (value.size) {\
+    size_t i;\
+    if (!value.ptr) {\
+      return GRN_FALSE;\
+    }\
+    for (i = 0; i < value.size; i++) {\
+      if (!grn_ts_ ## type ## _is_valid(value.ptr[i])) {\
+        return GRN_FALSE;\
+      }\
+    }\
+  }\
+  return GRN_TRUE;
+/* grn_ts_bool_vector_is_valid() returns whether a value is valid or not. */
+inline static grn_bool
+grn_ts_bool_vector_is_valid(grn_ts_bool_vector value) {
+  GRN_TS_VECTOR_IS_VALID(bool)
+}
+
+/* grn_ts_int_vector_is_valid() returns whether a value is valid or not. */
+inline static grn_bool
+grn_ts_int_vector_is_valid(grn_ts_int_vector value) {
+  GRN_TS_VECTOR_IS_VALID(int)
+}
+
+/* grn_ts_float_vector_is_valid() returns whether a value is valid or not. */
+inline static grn_bool
+grn_ts_float_vector_is_valid(grn_ts_float_vector value) {
+  GRN_TS_VECTOR_IS_VALID(float)
+}
+
+/* grn_ts_time_vector_is_valid() returns whether a value is valid or not. */
+inline static grn_bool
+grn_ts_time_vector_is_valid(grn_ts_time_vector value) {
+  GRN_TS_VECTOR_IS_VALID(time)
+}
+
+/* grn_ts_text_vector_is_valid() returns whether a value is valid or not. */
+inline static grn_bool
+grn_ts_text_vector_is_valid(grn_ts_text_vector value) {
+  GRN_TS_VECTOR_IS_VALID(text)
+}
+
+/*
+ * grn_ts_geo_point_vector_is_valid() returns whether a value is valid or
+ * not.
+ */
+inline static grn_bool
+grn_ts_geo_point_vector_is_valid(grn_ts_geo_point_vector value) {
+  GRN_TS_VECTOR_IS_VALID(geo_point)
+}
+#undef GRN_TS_VECTOR_IS_VALID
+
+/* grn_ts_bool_zero() returns a zero. */
+inline static grn_ts_bool
+grn_ts_bool_zero(void) {
+  return GRN_FALSE;
+}
+
+/* grn_ts_int_zero() returns a zero. */
+inline static grn_ts_int
+grn_ts_int_zero(void) {
+  return 0;
+}
+
+/* grn_ts_float_zero() returns a zero. */
+inline static grn_ts_float
+grn_ts_float_zero(void) {
+  return 0.0;
+}
+
+/* grn_ts_time_zero() returns a zero. */
+inline static grn_ts_time
+grn_ts_time_zero(void) {
+  return 0;
+}
+
+/* grn_ts_text_zero() returns a zero. */
+inline static grn_ts_text
+grn_ts_text_zero(void) {
+  return (grn_ts_text){ NULL, 0 };
+}
+
+/* grn_ts_geo_point_zero() returns a zero. */
+inline static grn_ts_geo_point
+grn_ts_geo_point_zero(void) {
+  return (grn_ts_geo_point){ 0, 0 };
+}
+
+/* grn_ts_geo_point_zero() returns a zero. */
+inline static grn_ts_ref
+grn_ts_ref_zero(void) {
+  return (grn_ts_ref){ 0, 0.0 };
+}
+
+/* grn_ts_bool_vector_zero() returns a zero. */
+inline static grn_ts_bool_vector
+grn_ts_bool_vector_zero(void) {
+  return (grn_ts_bool_vector){ NULL, 0 };
+}
+
+/* grn_ts_int_vector_zero() returns a zero. */
+inline static grn_ts_int_vector
+grn_ts_int_vector_zero(void) {
+  return (grn_ts_int_vector){ NULL, 0 };
+}
+
+/* grn_ts_float_vector_zero() returns a zero. */
+inline static grn_ts_float_vector
+grn_ts_float_vector_zero(void) {
+  return (grn_ts_float_vector){ NULL, 0 };
+}
+
+/* grn_ts_time_vector_zero() returns a zero. */
+inline static grn_ts_time_vector
+grn_ts_time_vector_zero(void) {
+  return (grn_ts_time_vector){ NULL, 0 };
+}
+
+/* grn_ts_text_vector_zero() returns a zero. */
+inline static grn_ts_text_vector
+grn_ts_text_vector_zero(void) {
+  return (grn_ts_text_vector){ NULL, 0 };
+}
+
+/* grn_ts_geo_point_vector_zero() returns a zero. */
+inline static grn_ts_geo_point_vector
+grn_ts_geo_point_vector_zero(void) {
+  return (grn_ts_geo_point_vector){ NULL, 0 };
+}
+
+/* grn_ts_ref_vector_zero() returns a zero. */
+inline static grn_ts_ref_vector
+grn_ts_ref_vector_zero(void) {
+  return (grn_ts_ref_vector){ NULL, 0 };
+}
+
+/* grn_ts_data_type_to_kind() returns a kind associated with a type. */
+static grn_ts_data_kind
+grn_ts_data_type_to_kind(grn_ts_data_type type) {
+  switch (type) {
+    case GRN_DB_VOID: {
+      return GRN_TS_VOID;
+    }
+    case GRN_DB_BOOL: {
+      return GRN_TS_BOOL;
+    }
+    case GRN_DB_INT8:
+    case GRN_DB_INT16:
+    case GRN_DB_INT32:
+    case GRN_DB_INT64:
+    case GRN_DB_UINT8:
+    case GRN_DB_UINT16:
+    case GRN_DB_UINT32:
+    case GRN_DB_UINT64: {
+      return GRN_TS_INT;
+    }
+    case GRN_DB_FLOAT: {
+      return GRN_TS_FLOAT;
+    }
+    case GRN_DB_TIME: {
+      return GRN_TS_TIME;
+    }
+    case GRN_DB_SHORT_TEXT:
+    case GRN_DB_TEXT:
+    case GRN_DB_LONG_TEXT: {
+      return GRN_TS_TEXT;
+    }
+    case GRN_DB_TOKYO_GEO_POINT:
+    case GRN_DB_WGS84_GEO_POINT: {
+      return GRN_TS_GEO_POINT;
+    }
+    default: {
+      return GRN_TS_REF;
+    }
+  }
+}
+
+/* grn_ts_data_kind_to_type() returns a type associated with a kind. */
+static grn_ts_data_type
+grn_ts_data_kind_to_type(grn_ts_data_kind kind) {
+  switch (kind & ~GRN_TS_VECTOR_FLAG) {
+    case GRN_TS_BOOL: {
+      return GRN_DB_BOOL;
+    }
+    case GRN_TS_INT: {
+      return GRN_DB_INT64;
+    }
+    case GRN_TS_FLOAT: {
+      return GRN_DB_FLOAT;
+    }
+    case GRN_TS_TIME: {
+      return GRN_DB_TIME;
+    }
+    case GRN_TS_TEXT: {
+      return GRN_DB_TEXT;
+    }
+    case GRN_TS_GEO_POINT: {
+      /* GRN_DB_TOKYO_GEO_POINT or GRN_DB_WGS84_GEO_POINT. */
+      return GRN_DB_VOID;
+    }
+    default: {
+      return GRN_DB_VOID;
+    }
+  }
+}
+
+/*
+ * grn_ts_obj_increment_ref_count() increments the reference count of an
+ * object.
+ */
+static grn_rc
+grn_ts_obj_increment_ref_count(grn_ctx *ctx, grn_obj *obj) {
+  grn_obj *obj_clone = grn_ctx_at(ctx, grn_obj_id(ctx, obj));
+  if (!obj_clone) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (obj_clone != obj) {
+    grn_obj_unlink(ctx, obj_clone);
+    return GRN_INVALID_ARGUMENT;
+  }
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_obj_is_table() returns whether an object is a column or not */
+static grn_bool
+grn_ts_obj_is_table(grn_ctx *ctx, grn_obj *obj) {
+  return grn_obj_is_table(ctx, obj);
+}
+
+/* grn_ts_obj_is_column() returns whether an object is a column or not */
+static grn_bool
+grn_ts_obj_is_column(grn_ctx *ctx, grn_obj *obj) {
+  switch (obj->header.type) {
+    case GRN_COLUMN_FIX_SIZE:
+    case GRN_COLUMN_VAR_SIZE: {
+      return GRN_TRUE;
+    }
+    /* GRN_COLUMN_INDEX is not supported. */
+    default: {
+      return GRN_FALSE;
+    }
+  }
+}
+
+/* grn_ts_table_has_key() returns whether a table has _key or not. */
+static grn_bool
+grn_ts_table_has_key(grn_ctx *ctx, grn_obj *table) {
+  switch (table->header.type) {
+    case GRN_TABLE_HASH_KEY:
+    case GRN_TABLE_PAT_KEY:
+    case GRN_TABLE_DAT_KEY: {
+      return GRN_TRUE;
+    }
+    default: {
+      return GRN_FALSE;
+    }
+  }
+}
+
+/* grn_ts_table_has_value() returns whether a table has _value or not. */
+static grn_bool
+grn_ts_table_has_value(grn_ctx *ctx, grn_obj *table) {
+  return DB_OBJ(table)->range != GRN_DB_VOID;
+}
+
+/* grn_ts_table_get_value() writes a value (_value) into buf. */
+static size_t
+grn_ts_table_get_value(grn_ctx *ctx, grn_obj *table, grn_id id, void *buf) {
+  switch (table->header.type) {
+    case GRN_TABLE_HASH_KEY: {
+      return grn_hash_get_value(ctx, (grn_hash *)table, id, buf);
+    }
+    case GRN_TABLE_PAT_KEY: {
+      return grn_pat_get_value(ctx, (grn_pat *)table, id, buf);
+    }
+    case GRN_TABLE_NO_KEY: {
+      return grn_array_get_value(ctx, (grn_array *)table, id, buf);
+    }
+    default: {
+      return 0;
+    }
+  }
+}
+
+/*-------------------------------------------------------------
+ * grn_ts_expr_node.
+ */
+
+typedef struct {
+  GRN_TS_EXPR_NODE_COMMON_MEMBERS
+} grn_ts_expr_id_node;
+
+typedef struct {
+  GRN_TS_EXPR_NODE_COMMON_MEMBERS
+} grn_ts_expr_score_node;
+
+typedef struct {
+  GRN_TS_EXPR_NODE_COMMON_MEMBERS
+  grn_obj *table;
+  grn_obj buf;
+} grn_ts_expr_key_node;
+
+typedef struct {
+  GRN_TS_EXPR_NODE_COMMON_MEMBERS
+  grn_obj *table;
+} grn_ts_expr_value_node;
+
+typedef struct {
+  GRN_TS_EXPR_NODE_COMMON_MEMBERS
+  union {
+    grn_ts_bool bool_value;
+    grn_ts_int int_value;
+    grn_ts_float float_value;
+    grn_ts_time time_value;
+    grn_ts_text text_value;
+    grn_ts_geo_point geo_point_value;
+    grn_ts_bool_vector bool_vector_value;
+    grn_ts_int_vector int_vector_value;
+    grn_ts_float_vector float_vector_value;
+    grn_ts_time_vector time_vector_value;
+    grn_ts_text_vector text_vector_value;
+    grn_ts_geo_point_vector geo_point_vector_value;
+  } content;
+  char *text_buf;
+  void *vector_buf;
+} grn_ts_expr_const_node;
+
+typedef struct {
+  GRN_TS_EXPR_NODE_COMMON_MEMBERS
+  grn_obj *column;
+  grn_obj buf;
+  union {
+    void *buf;
+    struct {
+      grn_ts_text *ptr;
+      size_t size;
+    } text_buf;
+    struct {
+      grn_ts_ref *ptr;
+      size_t size;
+    } ref_buf;
+  } body;
+} grn_ts_expr_column_node;
+
+typedef struct {
+  GRN_TS_EXPR_NODE_COMMON_MEMBERS
+  grn_ts_op_type op_type;
+  // TODO
+} grn_ts_expr_op_node;
+
+/* grn_ts_expr_node_fin() finalizes a node. */
+static grn_rc
+grn_ts_expr_node_fin(grn_ctx *ctx, grn_ts_expr_node *node) {
+  switch (node->type) {
+    case GRN_TS_EXPR_ID_NODE:
+    case GRN_TS_EXPR_SCORE_NODE: {
+      return GRN_SUCCESS;
+    }
+    case GRN_TS_EXPR_KEY_NODE: {
+      grn_ts_expr_key_node *key_node = (grn_ts_expr_key_node *)node;
+      GRN_OBJ_FIN(ctx, &key_node->buf);
+      if (key_node->table) {
+        grn_obj_unlink(ctx, key_node->table);
+      }
+      return GRN_SUCCESS;
+    }
+    case GRN_TS_EXPR_VALUE_NODE: {
+      grn_ts_expr_value_node *value_node = (grn_ts_expr_value_node *)node;
+      if (value_node->table) {
+        grn_obj_unlink(ctx, value_node->table);
+      }
+      return GRN_SUCCESS;
+    }
+    case GRN_TS_EXPR_CONST_NODE: {
+      grn_ts_expr_const_node *const_node = (grn_ts_expr_const_node *)node;
+      if (const_node->vector_buf) {
+        GRN_FREE(const_node->vector_buf);
+      }
+      if (const_node->text_buf) {
+        GRN_FREE(const_node->text_buf);
+      }
+      return GRN_SUCCESS;
+    }
+    case GRN_TS_EXPR_COLUMN_NODE: {
+      grn_ts_expr_column_node *column_node = (grn_ts_expr_column_node *)node;
+      if (column_node->body.buf) {
+        GRN_FREE(column_node->body.buf);
+      }
+      GRN_OBJ_FIN(ctx, &column_node->buf);
+      if (column_node->column) {
+        grn_obj_unlink(ctx, column_node->column);
+      }
+      return GRN_SUCCESS;
+    }
+    case GRN_TS_EXPR_OP_NODE: {
+      // TODO: Unlink objects and free memory.
+      return GRN_SUCCESS;
+    }
+    default: {
+      return GRN_INVALID_ARGUMENT;
+    }
+  }
+}
+
+/* grn_ts_expr_node_close() destroys a node. */
+static grn_rc
+grn_ts_expr_node_close(grn_ctx *ctx, grn_ts_expr_node *node) {
+  grn_rc rc;
+  if (!node) {
+    return GRN_SUCCESS;
+  }
+  rc = grn_ts_expr_node_fin(ctx, node);
+  GRN_FREE(node);
+  return rc;
+}
+
+/* grn_ts_expr_id_node_open() creates a node associated with ID (_id). */
+static grn_rc
+grn_ts_expr_id_node_open(grn_ctx *ctx, grn_ts_expr_node **node) {
+  grn_ts_expr_id_node *new_node = GRN_MALLOCN(grn_ts_expr_id_node, 1);
+  if (!new_node) {
+    return GRN_NO_MEMORY_AVAILABLE;
+  }
+  new_node->type = GRN_TS_EXPR_ID_NODE;
+  new_node->data_kind = GRN_TS_INT;
+  new_node->data_type = GRN_DB_UINT32;
+  *node = (grn_ts_expr_node *)new_node;
+  return GRN_SUCCESS;
+}
+
+/*
+ * grn_ts_expr_score_node_open() creates a node associated with score
+ * (_score).
+ */
+static grn_rc
+grn_ts_expr_score_node_open(grn_ctx *ctx, grn_ts_expr_node **node) {
+  grn_ts_expr_score_node *new_node = GRN_MALLOCN(grn_ts_expr_score_node, 1);
+  if (!new_node) {
+    return GRN_NO_MEMORY_AVAILABLE;
+  }
+  new_node->type = GRN_TS_EXPR_SCORE_NODE;
+  new_node->data_kind = GRN_TS_FLOAT;
+  new_node->data_type = GRN_DB_FLOAT;
+  *node = (grn_ts_expr_node *)new_node;
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_key_node_open() creates a node associated with key (_key). */
+static grn_rc
+grn_ts_expr_key_node_open(grn_ctx *ctx, grn_obj *table,
+                          grn_ts_expr_node **node) {
+  grn_rc rc;
+  grn_ts_expr_key_node *new_node;
+  if (!grn_ts_table_has_key(ctx, table)) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  new_node = GRN_MALLOCN(grn_ts_expr_key_node, 1);
+  if (!new_node) {
+    return GRN_NO_MEMORY_AVAILABLE;
+  }
+  rc = grn_ts_obj_increment_ref_count(ctx, table);
+  if (rc != GRN_SUCCESS) {
+    GRN_FREE(new_node);
+    return rc;
+  }
+  new_node->type = GRN_TS_EXPR_KEY_NODE;
+  new_node->data_kind = grn_ts_data_type_to_kind(table->header.domain);
+  new_node->data_type = table->header.domain;
+  new_node->table = table;
+  GRN_TEXT_INIT(&new_node->buf, 0);
+  *node = (grn_ts_expr_node *)new_node;
+  return GRN_SUCCESS;
+}
+
+/*
+ * grn_ts_expr_value_node_open() creates a node associated with value
+ * (_value).
+ */
+static grn_rc
+grn_ts_expr_value_node_open(grn_ctx *ctx, grn_obj *table,
+                            grn_ts_expr_node **node) {
+  grn_rc rc;
+  grn_ts_expr_value_node *new_node;
+  if (!grn_ts_table_has_value(ctx, table)) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  new_node = GRN_MALLOCN(grn_ts_expr_value_node, 1);
+  if (!new_node) {
+    return GRN_NO_MEMORY_AVAILABLE;
+  }
+  memset(new_node, 0, sizeof(*new_node));
+  rc = grn_ts_obj_increment_ref_count(ctx, table);
+  if (rc != GRN_SUCCESS) {
+    GRN_FREE(new_node);
+    return rc;
+  }
+  new_node->type = GRN_TS_EXPR_VALUE_NODE;
+  new_node->data_kind = grn_ts_data_type_to_kind(DB_OBJ(table)->range);
+  new_node->data_type = DB_OBJ(table)->range;
+  new_node->table = table;
+  *node = (grn_ts_expr_node *)new_node;
+  return GRN_SUCCESS;
+}
+
+#define GRN_TS_EXPR_CONST_NODE_OPEN_CASE_BLOCK(KIND, kind)\
+  case GRN_TS_ ## KIND: {\
+    node->content.kind ## _value = *(const grn_ts_ ## kind *)value;\
+    return GRN_SUCCESS;\
+  }
+/* grn_ts_expr_const_node_init_scalar() initializes a const scalar node. */
+static grn_rc
+grn_ts_expr_const_node_init_scalar(grn_ctx *ctx, grn_ts_expr_const_node *node,
+                                   const void *value) {
+  switch (node->data_kind) {
+    GRN_TS_EXPR_CONST_NODE_OPEN_CASE_BLOCK(BOOL, bool)
+    GRN_TS_EXPR_CONST_NODE_OPEN_CASE_BLOCK(INT, int)
+    GRN_TS_EXPR_CONST_NODE_OPEN_CASE_BLOCK(FLOAT, float)
+    GRN_TS_EXPR_CONST_NODE_OPEN_CASE_BLOCK(TIME, time)
+    case GRN_TS_TEXT: {
+      grn_ts_text text_value;
+      char *text_buf;
+      text_value = *(const grn_ts_text *)value;
+      if (!text_value.size) {
+        node->content.text_value.ptr = NULL;
+        node->content.text_value.size = 0;
+        return GRN_SUCCESS;
+      }
+      text_buf = (char *)GRN_MALLOC(text_value.size);
+      if (!text_buf) {
+        return GRN_NO_MEMORY_AVAILABLE;
+      }
+      node->text_buf = text_buf;
+      grn_memcpy(text_buf, text_value.ptr, text_value.size);
+      node->content.text_value.ptr = text_buf;
+      node->content.text_value.size = text_value.size;
+      return GRN_SUCCESS;
+    }
+    GRN_TS_EXPR_CONST_NODE_OPEN_CASE_BLOCK(GEO_POINT, geo_point)
+    default: {
+      return GRN_INVALID_ARGUMENT;
+    }
+  }
+}
+#undef GRN_TS_EXPR_CONST_NODE_OPEN_CASE_BLOCK
+
+#define GRN_TS_EXPR_CONST_NODE_OPEN_VECTOR_CASE_BLOCK(KIND, kind)\
+  case GRN_TS_ ## KIND ## _VECTOR: {\
+    grn_ts_ ## kind ## _vector vector;\
+    grn_ts_ ## kind *vector_buf;\
+    vector = *(const grn_ts_ ## kind ## _vector *)value;\
+    if (!vector.size) {\
+      node->content.kind ## _vector_value.ptr = NULL;\
+      node->content.kind ## _vector_value.size = 0;\
+      return GRN_SUCCESS;\
+    }\
+    vector_buf = GRN_MALLOCN(grn_ts_ ## kind, vector.size);\
+    if (!vector_buf) {\
+      return GRN_NO_MEMORY_AVAILABLE;\
+    }\
+    node->vector_buf = vector_buf;\
+    grn_memcpy(vector_buf, vector.ptr,\
+               sizeof(grn_ts_ ## kind) * vector.size);\
+    node->content.kind ## _vector_value.ptr = vector_buf;\
+    node->content.kind ## _vector_value.size = vector.size;\
+    return GRN_SUCCESS;\
+  }
+/* grn_ts_expr_const_node_init_vector() initializes a const vector node. */
+static grn_rc
+grn_ts_expr_const_node_init_vector(grn_ctx *ctx, grn_ts_expr_const_node *node,
+                                   const void *value) {
+  switch (node->data_kind) {
+    GRN_TS_EXPR_CONST_NODE_OPEN_VECTOR_CASE_BLOCK(BOOL, bool)
+    GRN_TS_EXPR_CONST_NODE_OPEN_VECTOR_CASE_BLOCK(INT, int)
+    GRN_TS_EXPR_CONST_NODE_OPEN_VECTOR_CASE_BLOCK(FLOAT, float)
+    GRN_TS_EXPR_CONST_NODE_OPEN_VECTOR_CASE_BLOCK(TIME, time)
+    case GRN_TS_TEXT_VECTOR: {
+      size_t i, offset = 0, total_size = 0;
+      grn_ts_text_vector vector;
+      grn_ts_text *vector_buf;
+      char *text_buf;
+      vector = *(const grn_ts_text_vector *)value;
+      if (!vector.size) {
+        node->content.text_vector_value.ptr = NULL;
+        node->content.text_vector_value.size = 0;
+        return GRN_SUCCESS;
+      }
+      vector_buf = GRN_MALLOCN(grn_ts_text, vector.size);
+      if (!vector_buf) {
+        return GRN_NO_MEMORY_AVAILABLE;
+      }
+      node->vector_buf = vector_buf;
+      for (i = 0; i < vector.size; i++) {
+        total_size += vector.ptr[i].size;
+      }
+      if (total_size) {
+        text_buf = (char *)GRN_MALLOC(total_size);
+        if (!text_buf) {
+          return GRN_NO_MEMORY_AVAILABLE;
+        }
+        node->text_buf = text_buf;
+      }
+      for (i = 0; i < vector.size; i++) {
+        grn_memcpy(text_buf + offset, vector.ptr[i].ptr, vector.ptr[i].size);
+        vector_buf[i].ptr = text_buf + offset;
+        vector_buf[i].size = vector.ptr[i].size;
+        offset += vector.ptr[i].size;
+      }
+      node->content.text_vector_value.ptr = vector_buf;
+      node->content.text_vector_value.size = vector.size;
+      return GRN_SUCCESS;
+    }
+    GRN_TS_EXPR_CONST_NODE_OPEN_VECTOR_CASE_BLOCK(GEO_POINT, geo_point)
+    default: {
+      return GRN_UNKNOWN_ERROR;
+    }
+  }
+}
+#undef GRN_TS_EXPR_CONST_NODE_OPEN_VECTOR_CASE_BLOCK
+
+/* grn_ts_expr_const_node_open() creates a node associated with a const. */
+static grn_rc
+grn_ts_expr_const_node_open(grn_ctx *ctx, grn_ts_data_kind kind,
+                            const void *value, grn_ts_expr_node **node) {
+  grn_rc rc = GRN_SUCCESS;
+  grn_ts_expr_const_node *new_node = GRN_MALLOCN(grn_ts_expr_const_node, 1);
+  if (!new_node) {
+    return GRN_NO_MEMORY_AVAILABLE;
+  }
+  new_node->type = GRN_TS_EXPR_CONST_NODE;
+  new_node->data_kind = kind;
+  new_node->data_type = grn_ts_data_kind_to_type(kind);
+  new_node->text_buf = NULL;
+  new_node->vector_buf = NULL;
+  if (kind & GRN_TS_VECTOR_FLAG) {
+    rc = grn_ts_expr_const_node_init_vector(ctx, new_node, value);
+  } else {
+    rc = grn_ts_expr_const_node_init_scalar(ctx, new_node, value);
+  }
+  if (rc != GRN_SUCCESS) {
+    grn_ts_expr_node_close(ctx, (grn_ts_expr_node *)new_node);
+    return rc;
+  }
+  *node = (grn_ts_expr_node *)new_node;
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_column_node_open() creates a node associated with a column. */
+static grn_rc
+grn_ts_expr_column_node_open(grn_ctx *ctx, grn_obj *column,
+                             grn_ts_expr_node **node) {
+  grn_rc rc;
+  grn_ts_expr_column_node *new_node = GRN_MALLOCN(grn_ts_expr_column_node, 1);
+  if (!new_node) {
+    return GRN_NO_MEMORY_AVAILABLE;
+  }
+  memset(new_node, 0, sizeof(*new_node));
+  rc = grn_ts_obj_increment_ref_count(ctx, column);
+  if (rc != GRN_SUCCESS) {
+    GRN_FREE(new_node);
+    return rc;
+  }
+  new_node->type = GRN_TS_EXPR_COLUMN_NODE;
+  new_node->data_kind = grn_ts_data_type_to_kind(DB_OBJ(column)->range);
+  if (column->header.type == GRN_COLUMN_VAR_SIZE) {
+    grn_obj_flags type = column->header.flags & GRN_OBJ_COLUMN_TYPE_MASK;
+    if (type == GRN_OBJ_COLUMN_VECTOR) {
+      new_node->data_kind |= GRN_TS_VECTOR_FLAG;
+    }
+  }
+  new_node->data_type = DB_OBJ(column)->range;
+  new_node->column = column;
+  GRN_TEXT_INIT(&new_node->buf, 0);
+  new_node->body.buf = NULL;
+  *node = (grn_ts_expr_node *)new_node;
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_id_node_evaluate() outputs IDs. */
+static grn_rc
+grn_ts_expr_id_node_evaluate(grn_ctx *ctx, grn_ts_expr_id_node *node,
+                             const grn_ts_record *in, size_t n_in,
+                             void *out) {
+  size_t i;
+  grn_ts_int *out_ptr = (grn_ts_int *)out;
+  for (i = 0; i < n_in; i++) {
+    out_ptr[i] = (grn_ts_int)in[i].id;
+  }
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_score_node_evaluate() outputs scores. */
+static grn_rc
+grn_ts_expr_score_node_evaluate(grn_ctx *ctx, grn_ts_expr_score_node *node,
+                                const grn_ts_record *in, size_t n_in,
+                                void *out) {
+  size_t i;
+  grn_ts_float *out_ptr = (grn_ts_float *)out;
+  for (i = 0; i < n_in; i++) {
+    out_ptr[i] = (grn_ts_float)in[i].score;
+  }
+  return GRN_SUCCESS;
+}
+
+#define GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE_BLOCK(KIND, kind)\
+  case GRN_TS_ ## KIND: {\
+    size_t i;\
+    grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\
+    for (i = 0; i < n_in; i++) {\
+      if (!grn_table_get_key(ctx, node->table, in[i].id, &out_ptr[i],\
+                             sizeof(grn_ts_ ## kind))) {\
+        out_ptr[i] = grn_ts_ ## kind ## _zero();\
+      }\
+    }\
+    return GRN_SUCCESS;\
+  }
+#define GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE_BLOCK(TYPE, type)\
+  case GRN_DB_ ## TYPE: {\
+    for (i = 0; i < n_in; i++) {\
+      type ## _t key;\
+      if (grn_table_get_key(ctx, node->table, in[i].id, &key,\
+                            sizeof(type ## _t))) {\
+        out_ptr[i] = (grn_ts_int)key;\
+      } else {\
+        out_ptr[i] = grn_ts_int_zero();\
+      }\
+    }\
+    return GRN_SUCCESS;\
+  }
+/* grn_ts_expr_key_node_evaluate() outputs keys. */
+/* FIXME: Errors are ignored. */
+static grn_rc
+grn_ts_expr_key_node_evaluate(grn_ctx *ctx, grn_ts_expr_key_node *node,
+                              const grn_ts_record *in, size_t n_in,
+                              void *out) {
+  switch (node->data_kind) {
+    GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE_BLOCK(BOOL, bool)
+    case GRN_TS_INT: {
+      size_t i;
+      grn_ts_int *out_ptr = (grn_ts_int *)out;
+      switch (node->data_type) {
+        GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE_BLOCK(INT8, int8)
+        GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE_BLOCK(INT16, int16)
+        GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE_BLOCK(INT32, int32)
+        GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE_BLOCK(INT64, int64)
+        GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE_BLOCK(UINT8, uint8)
+        GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE_BLOCK(UINT16, uint16)
+        GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE_BLOCK(UINT32, uint32)
+        GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE_BLOCK(UINT64, uint64)
+        default: {
+          return GRN_OBJECT_CORRUPT;
+        }
+      }
+    }
+    GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE_BLOCK(FLOAT, float)
+    GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE_BLOCK(TIME, time)
+    case GRN_TS_TEXT: {
+      size_t i;
+      char *buf_ptr;
+      grn_ts_text *out_ptr = (grn_ts_text *)out;
+      GRN_BULK_REWIND(&node->buf);
+      for (i = 0; i < n_in; i++) {
+        out_ptr[i].size = grn_table_get_key2(ctx, node->table, in[i].id,
+                                             &node->buf);
+      }
+      buf_ptr = GRN_BULK_HEAD(&node->buf);
+      for (i = 0; i < n_in; i++) {
+        out_ptr[i].ptr = buf_ptr;
+        buf_ptr += out_ptr[i].size;
+      }
+      return GRN_SUCCESS;
+    }
+    GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE_BLOCK(GEO_POINT, geo_point)
+    case GRN_TS_REF: {
+      size_t i;
+      grn_ts_ref *out_ptr = (grn_ts_ref *)out;
+      for (i = 0; i < n_in; i++) {
+        if (!grn_table_get_key(ctx, node->table, in[i].id, &out_ptr[i].id,
+                               sizeof(grn_ts_id))) {
+          out_ptr[i].id = GRN_ID_NIL;
+        }
+        out_ptr[i].score = in[i].score;
+      }
+      return GRN_SUCCESS;
+    }
+    default: {
+      return GRN_OBJECT_CORRUPT;
+    }
+  }
+}
+#undef GRN_TS_EXPR_KEY_NODE_EVALUATE_INT_CASE_BLOCK
+#undef GRN_TS_EXPR_KEY_NODE_EVALUATE_CASE_BLOCK
+
+#define GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE_BLOCK(KIND, kind)\
+  case GRN_TS_ ## KIND: {\
+    size_t i;\
+    grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\
+    for (i = 0; i < n_in; i++) {\
+      if (!grn_ts_table_get_value(ctx, node->table, in[i].id,\
+                                  &out_ptr[i])) {\
+        out_ptr[i] = grn_ts_ ## kind ## _zero();\
+      }\
+    }\
+    return GRN_SUCCESS;\
+  }
+#define GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE_BLOCK(TYPE, type)\
+  case GRN_DB_ ## TYPE: {\
+    for (i = 0; i < n_in; i++) {\
+      type ## _t value;\
+      if (grn_ts_table_get_value(ctx, node->table, in[i].id, &value)) {\
+        out_ptr[i] = (grn_ts_int)value;\
+      } else {\
+        out_ptr[i] = grn_ts_int_zero();\
+      }\
+    }\
+    return GRN_SUCCESS;\
+  }
+/* grn_ts_expr_value_node_evaluate() outputs values. */
+/* FIXME: Errors are ignored. */
+static grn_rc
+grn_ts_expr_value_node_evaluate(grn_ctx *ctx, grn_ts_expr_value_node *node,
+                                const grn_ts_record *in, size_t n_in,
+                                void *out) {
+  switch (node->data_kind) {
+    GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE_BLOCK(BOOL, bool)
+    case GRN_TS_INT: {
+      size_t i;
+      grn_ts_int *out_ptr = (grn_ts_int *)out;
+      switch (node->data_type) {
+        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE_BLOCK(INT8, int8)
+        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE_BLOCK(INT16, int16)
+        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE_BLOCK(INT32, int32)
+        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE_BLOCK(INT64, int64)
+        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE_BLOCK(UINT8, uint8)
+        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE_BLOCK(UINT16, uint16)
+        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE_BLOCK(UINT32, uint32)
+        GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE_BLOCK(UINT64, uint64)
+        default: {
+          return GRN_OBJECT_CORRUPT;
+        }
+      }
+    }
+    GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE_BLOCK(FLOAT, float)
+    GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE_BLOCK(TIME, time)
+    GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE_BLOCK(GEO_POINT, geo_point)
+    case GRN_TS_REF: {
+      size_t i;
+      grn_ts_ref *out_ptr = (grn_ts_ref *)out;
+      for (i = 0; i < n_in; i++) {
+        if (!grn_ts_table_get_value(ctx, node->table, in[i].id,
+                                    &out_ptr[i].id)) {
+          out_ptr[i].id = GRN_ID_NIL;
+        }
+        out_ptr[i].score = in[i].score;
+      }
+      return GRN_SUCCESS;
+    }
+    default: {
+      return GRN_OBJECT_CORRUPT;
+    }
+  }
+}
+#undef GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE_BLOCK
+#undef GRN_TS_EXPR_VALUE_NODE_EVALUATE_CASE_BLOCK
+
+#define GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE_BLOCK(KIND, kind)\
+  case GRN_TS_ ## KIND: {\
+    size_t i;\
+    grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\
+    for (i = 0; i < n_in; i++) {\
+      out_ptr[i] = node->content.kind ## _value;\
+    }\
+    return GRN_SUCCESS;\
+  }
+#define GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE_BLOCK(KIND, kind)\
+  GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE_BLOCK(KIND ## _VECTOR, kind ## _vector)
+/* grn_ts_expr_const_node_evaluate() outputs consts. */
+static grn_rc
+grn_ts_expr_const_node_evaluate(grn_ctx *ctx, grn_ts_expr_const_node *node,
+                                const grn_ts_record *in, size_t n_in,
+                                void *out) {
+  switch (node->data_kind) {
+    GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE_BLOCK(BOOL, bool)
+    GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE_BLOCK(INT, int)
+    GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE_BLOCK(FLOAT, float)
+    GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE_BLOCK(TIME, time)
+    GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE_BLOCK(TEXT, text)
+    GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE_BLOCK(GEO_POINT, geo_point)
+    GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE_BLOCK(BOOL, bool)
+    GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE_BLOCK(INT, int)
+    GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE_BLOCK(FLOAT, float)
+    GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE_BLOCK(TIME, time)
+    GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE_BLOCK(TEXT, text)
+    GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE_BLOCK(GEO_POINT, geo_point)
+    default: {
+      return GRN_INVALID_ARGUMENT;
+    }
+  }
+}
+#undef GRN_TS_EXPR_CONST_NODE_EVALUATE_VECTOR_CASE_BLOCK
+#undef GRN_TS_EXPR_CONST_NODE_EVALUATE_CASE_BLOCK
+
+#define GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE_BLOCK(KIND, kind)\
+  case GRN_TS_ ## KIND: {\
+    size_t i;\
+    grn_ts_ ## kind *out_ptr = (grn_ts_ ## kind *)out;\
+    grn_ra *ra = (grn_ra *)node->column;\
+    grn_ra_cache cache;\
+    GRN_RA_CACHE_INIT(ra, &cache);\
+    for (i = 0; i < n_in; i++) {\
+      grn_ts_ ## kind *ptr = NULL;\
+      if (in[i].id) {\
+        ptr = (grn_ts_ ## kind *)grn_ra_ref_cache(ctx, ra, in[i].id, &cache);\
+      }\
+      out_ptr[i] = ptr ? *ptr : grn_ts_ ## kind ## _zero();\
+    }\
+    GRN_RA_CACHE_FIN(ra, &cache);\
+    return GRN_SUCCESS;\
+  }
+#define GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE_BLOCK(TYPE, type)\
+  case GRN_DB_ ## TYPE: {\
+    size_t i;\
+    grn_ts_int *out_ptr = (grn_ts_int *)out;\
+    grn_ra *ra = (grn_ra *)node->column;\
+    grn_ra_cache cache;\
+    GRN_RA_CACHE_INIT(ra, &cache);\
+    for (i = 0; i < n_in; i++) {\
+      type ## _t *ptr = NULL;\
+      if (in[i].id) {\
+        ptr = (type ## _t *)grn_ra_ref_cache(ctx, ra, in[i].id, &cache);\
+      }\
+      out_ptr[i] = ptr ? (grn_ts_int)*ptr : grn_ts_int_zero();\
+    }\
+    GRN_RA_CACHE_FIN(ra, &cache);\
+    return GRN_SUCCESS;\
+  }
+/* grn_ts_expr_column_node_evaluate_scalar() outputs scalar column values. */
+/* FIXME: Errors are ignored. */
+static grn_rc
+grn_ts_expr_column_node_evaluate_scalar(grn_ctx *ctx,
+                                        grn_ts_expr_column_node *node,
+                                        const grn_ts_record *in, size_t n_in,
+                                        void *out) {
+  switch (node->data_kind) {
+    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE_BLOCK(BOOL, bool)
+    case GRN_TS_INT: {
+      switch (node->data_type) {
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE_BLOCK(INT8, int8)
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE_BLOCK(INT16, int16)
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE_BLOCK(INT32, int32)
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE_BLOCK(INT64, int64)
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE_BLOCK(UINT8, uint8)
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE_BLOCK(UINT16, uint16)
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE_BLOCK(UINT32, uint32)
+        /* The behavior is undefined if a value is greater than 2^63 - 1. */
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE_BLOCK(UINT64, uint64)
+        default: {
+          return GRN_OBJECT_CORRUPT;
+        }
+      }
+    }
+    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE_BLOCK(FLOAT, float)
+    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE_BLOCK(TIME, time)
+    case GRN_TS_TEXT: {
+      size_t i, offset = 0;
+      char *buf_ptr;
+      grn_ja *ja = (grn_ja *)node->column;
+      grn_ts_text *out_ptr = (grn_ts_text *)out;
+      /* Read column values into node->buf and save the size of each value. */
+      GRN_BULK_REWIND(&node->buf);
+      for (i = 0; i < n_in; i++) {
+        if (grn_ja_get_value(ctx, ja, in[i].id, &node->buf)) {
+          size_t size = GRN_BULK_VSIZE(&node->buf);
+          out_ptr[i].size = size - offset;
+          offset = size;
+        } else {
+          out_ptr[i].size = 0;
+        }
+      }
+      buf_ptr = GRN_BULK_HEAD(&node->buf);
+      for (i = 0; i < n_in; i++) {
+        out_ptr[i].ptr = buf_ptr;
+        buf_ptr += out_ptr[i].size;
+      }
+      return GRN_SUCCESS;
+    }
+    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE_BLOCK(GEO_POINT, geo_point)
+    case GRN_TS_REF: {
+      size_t i;
+      grn_ts_ref *out_ptr = (grn_ts_ref *)out;
+      grn_ra *ra = (grn_ra *)node->column;
+      grn_ra_cache cache;
+      GRN_RA_CACHE_INIT(ra, &cache);
+      for (i = 0; i < n_in; i++) {
+        grn_ts_id *ptr = NULL;
+        if (in[i].id) {
+          ptr = (grn_ts_id *)grn_ra_ref_cache(ctx, ra, in[i].id, &cache);
+        }
+        out_ptr[i].id = ptr ? *ptr : GRN_ID_NIL;
+        out_ptr[i].score = in[i].score;
+      }
+      GRN_RA_CACHE_FIN(ra, &cache);
+      return GRN_SUCCESS;
+    }
+    default: {
+      return GRN_OBJECT_CORRUPT;
+    }
+  }
+}
+#undef GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_INT_CASE_BLOCK
+#undef GRN_TS_EXPR_COLUMN_NODE_EVALUATE_SCALAR_CASE_BLOCK
+
+#define GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE_BLOCK(KIND, kind)\
+  case GRN_TS_ ## KIND ## _VECTOR: {\
+    size_t i, offset = 0;\
+    grn_ts_ ## kind *buf_ptr;\
+    grn_ts_ ## kind ## _vector *out_ptr = (grn_ts_ ## kind ## _vector *)out;\
+    /* Read column values into node->buf and save the size of each value. */\
+    GRN_BULK_REWIND(&node->buf);\
+    for (i = 0; i < n_in; i++) {\
+      if (grn_obj_get_value(ctx, node->column, in[i].id, &node->buf)) {\
+        size_t size = GRN_BULK_VSIZE(&node->buf) / sizeof(grn_ts_ ## kind);\
+        out_ptr[i].size = size - offset;\
+        offset = size;\
+      } else {\
+        return GRN_UNKNOWN_ERROR;\
+      }\
+    }\
+    buf_ptr = (grn_ts_ ## kind *)GRN_BULK_HEAD(&node->buf);\
+    for (i = 0; i < n_in; i++) {\
+      out_ptr[i].ptr = buf_ptr;\
+      buf_ptr += out_ptr[i].size;\
+    }\
+    return GRN_SUCCESS;\
+  }
+#define GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE_BLOCK(TYPE, type)\
+  case GRN_DB_ ## TYPE: {\
+    size_t i, j;\
+    grn_obj src_buf;\
+    grn_ts_int *buf_ptr;\
+    grn_ts_int_vector *out_ptr = (grn_ts_int_vector *)out;\
+    GRN_ ## TYPE ## _INIT(&src_buf, GRN_OBJ_VECTOR);\
+    /*
+     * Read column values into src_buf and typecast the values to grn_ts_int.
+     * Then, store the grn_ts_int values into node->buf and save the size of
+     * each value.
+     */\
+    GRN_BULK_REWIND(&node->buf);\
+    for (i = 0; i < n_in; i++) {\
+      GRN_BULK_REWIND(&src_buf);\
+      if (grn_obj_get_value(ctx, node->column, in[i].id, &src_buf)) {\
+        type ## _t *src_ptr = (type ## _t *)GRN_BULK_HEAD(&src_buf);\
+        out_ptr[i].size = GRN_BULK_VSIZE(&src_buf) / sizeof(type ## _t);\
+        for (j = 0; j < out_ptr[i].size; j++) {\
+          grn_ts_int value = (grn_ts_int)src_ptr[j];\
+          grn_rc rc = grn_bulk_write(ctx, &node->buf, (char *)&value,\
+                                     sizeof(value));\
+          if (rc != GRN_SUCCESS) {\
+            GRN_OBJ_FIN(ctx, &src_buf);\
+            return rc;\
+          }\
+        }\
+      } else {\
+        GRN_OBJ_FIN(ctx, &src_buf);\
+        return GRN_UNKNOWN_ERROR;\
+      }\
+    }\
+    buf_ptr = (grn_ts_int *)GRN_BULK_HEAD(&node->buf);\
+    for (i = 0; i < n_in; i++) {\
+      out_ptr[i].ptr = buf_ptr;\
+      buf_ptr += out_ptr[i].size;\
+    }\
+    GRN_OBJ_FIN(ctx, &src_buf);\
+    return GRN_SUCCESS;\
+  }
+/* grn_ts_expr_column_node_evaluate_vector() outputs vector column values. */
+/* FIXME: Errors are ignored. */
+static grn_rc
+grn_ts_expr_column_node_evaluate_vector(grn_ctx *ctx,
+                                        grn_ts_expr_column_node *node,
+                                        const grn_ts_record *in, size_t n_in,
+                                        void *out) {
+  switch (node->data_kind) {
+    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE_BLOCK(BOOL, bool)
+    case GRN_TS_INT_VECTOR: {
+      switch (node->data_type) {
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE_BLOCK(INT8, int8)
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE_BLOCK(INT16, int16)
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE_BLOCK(INT32, int32)
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE_BLOCK(INT64, int64)
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE_BLOCK(UINT8, uint8)
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE_BLOCK(UINT16, uint16)
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE_BLOCK(UINT32, uint32)
+        /* The behavior is undefined if a value is greater than 2^63 - 1. */
+        GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE_BLOCK(UINT64, uint64)
+        default: {
+          return GRN_OBJECT_CORRUPT;
+        }
+      }
+    }
+    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE_BLOCK(FLOAT, float)
+    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE_BLOCK(TIME, time)
+    case GRN_TS_TEXT_VECTOR: {
+      size_t i, offset = 0;
+      grn_ts_text *text_ptr;
+      grn_ts_text_vector *out_ptr = (grn_ts_text_vector *)out;
+      /* Read column values into node->buf and save the size of each value. */
+      GRN_BULK_REWIND(&node->buf);
+      for (i = 0; i < n_in; i++) {
+        if (grn_obj_get_value(ctx, node->column, in[i].id, &node->buf)) {
+          size_t size = grn_vector_size(ctx, &node->buf);
+          out_ptr[i].size = size - offset;
+          offset = size;
+        } else {
+          out_ptr[i].size = 0;
+        }
+      }
+      /* Resize node->body.text_buf. */
+      if (node->body.text_buf.size < offset) {
+        size_t n_bytes = sizeof(grn_ts_text) * offset;
+        grn_ts_text *new_buf;
+        new_buf = (grn_ts_text *)GRN_REALLOC(node->body.text_buf.ptr, n_bytes);
+        if (!new_buf) {
+          return GRN_NO_MEMORY_AVAILABLE;
+        }
+        node->body.text_buf.ptr = new_buf;
+        node->body.text_buf.size = offset;
+      }
+      /* Compose the result. */
+      text_ptr = node->body.text_buf.ptr;
+      for (i = 0; i < offset; i++) {
+        text_ptr[i].size = grn_vector_get_element(ctx, &node->buf, i,
+                                                  &text_ptr[i].ptr,
+                                                  NULL, NULL);
+      }
+      for (i = 0; i < n_in; i++) {
+        out_ptr[i].ptr = text_ptr;
+        text_ptr += out_ptr[i].size;
+      }
+      return GRN_SUCCESS;
+    }
+    GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE_BLOCK(GEO_POINT, geo_point)
+    case GRN_TS_REF_VECTOR: {
+      size_t i, j, offset = 0;
+      grn_ts_id *buf_ptr;
+      grn_ts_ref *ref_ptr;
+      grn_ts_ref_vector *out_ptr = (grn_ts_ref_vector *)out;
+      /* Read column values into node->buf and save the size of each value. */
+      GRN_BULK_REWIND(&node->buf);
+      for (i = 0; i < n_in; i++) {
+        if (grn_obj_get_value(ctx, node->column, in[i].id, &node->buf)) {
+          size_t size = GRN_BULK_VSIZE(&node->buf) / sizeof(grn_ts_id);
+          out_ptr[i].size = size - offset;
+          offset = size;
+        } else {
+          return GRN_UNKNOWN_ERROR;
+        }
+      }
+      /* Resize node->body.ref_buf. */
+      if (node->body.ref_buf.size < offset) {
+        size_t n_bytes = sizeof(grn_ts_ref) * offset;
+        grn_ts_ref *new_buf;
+        new_buf = (grn_ts_ref *)GRN_REALLOC(node->body.ref_buf.ptr, n_bytes);
+        if (!new_buf) {
+          return GRN_NO_MEMORY_AVAILABLE;
+        }
+        node->body.ref_buf.ptr = new_buf;
+        node->body.ref_buf.size = offset;
+      }
+      /* Compose the result. */
+      buf_ptr = (grn_ts_id *)GRN_BULK_HEAD(&node->buf);
+      ref_ptr = node->body.ref_buf.ptr;
+      for (i = 0; i < n_in; i++) {
+        out_ptr[i].ptr = ref_ptr;
+        for (j = 0; j < out_ptr[i].size; j++, buf_ptr++, ref_ptr++) {
+          ref_ptr->id = *buf_ptr;
+          ref_ptr->score = in[i].score;
+        }
+      }
+      return GRN_SUCCESS;
+    }
+    default: {
+      return GRN_OBJECT_CORRUPT;
+    }
+  }
+}
+#undef GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_INT_CASE_BLOCK
+#undef GRN_TS_EXPR_COLUMN_NODE_EVALUATE_VECTOR_CASE_BLOCK
+
+/* grn_ts_expr_column_node_evaluate() outputs column values. */
+static grn_rc
+grn_ts_expr_column_node_evaluate(grn_ctx *ctx, grn_ts_expr_column_node *node,
+                                 const grn_ts_record *in, size_t n_in,
+                                 void *out) {
+  if (node->data_kind & GRN_TS_VECTOR_FLAG) {
+    return grn_ts_expr_column_node_evaluate_vector(ctx, node, in, n_in, out);
+  } else {
+    return grn_ts_expr_column_node_evaluate_scalar(ctx, node, in, n_in, out);
+  }
+}
+
+/* grn_ts_expr_op_node_evaluate() outputs results of an operator. */
+static grn_rc
+grn_ts_expr_op_node_evaluate(grn_ctx *ctx, grn_ts_expr_op_node *node,
+                             const grn_ts_record *in, size_t n_in,
+                             void *out) {
+  // TODO
+  return GRN_SUCCESS;
+}
+
+#define GRN_TS_EXPR_NODE_EVALUATE_CASE_BLOCK(TYPE, type)\
+  case GRN_TS_EXPR_ ## TYPE ## _NODE: {\
+    grn_ts_expr_ ## type ## _node *type ## _node;\
+    type ## _node = (grn_ts_expr_ ## type ## _node *)node;\
+    return grn_ts_expr_ ## type ## _node_evaluate(ctx, type ## _node,\
+                                                  in, n_in, out);\
+  }
+/* grn_ts_expr_node_evaluate() evaluates a subexpression. */
+static grn_rc
+grn_ts_expr_node_evaluate(grn_ctx *ctx, grn_ts_expr_node *node,
+                          const grn_ts_record *in, size_t n_in,
+                          void *out) {
+  switch (node->type) {
+    GRN_TS_EXPR_NODE_EVALUATE_CASE_BLOCK(ID, id)
+    GRN_TS_EXPR_NODE_EVALUATE_CASE_BLOCK(SCORE, score)
+    GRN_TS_EXPR_NODE_EVALUATE_CASE_BLOCK(KEY, key)
+    GRN_TS_EXPR_NODE_EVALUATE_CASE_BLOCK(VALUE, value)
+    GRN_TS_EXPR_NODE_EVALUATE_CASE_BLOCK(CONST, const)
+    GRN_TS_EXPR_NODE_EVALUATE_CASE_BLOCK(COLUMN, column)
+    GRN_TS_EXPR_NODE_EVALUATE_CASE_BLOCK(OP, op)
+    default: {
+      return GRN_INVALID_ARGUMENT;
+    }
+  }
+}
+#undef GRN_TS_EXPR_NODE_EVALUATE_CASE_BLOCK
+
+/* grn_ts_expr_key_node_filter() filters records. */
+static grn_rc
+grn_ts_expr_key_node_filter(grn_ctx *ctx, grn_ts_expr_key_node *node,
+                            grn_ts_record *in, size_t n_in,
+                            grn_ts_record *out, size_t *n_out) {
+  size_t i, count = 0;
+  for (i = 0; i < n_in; i++) {
+    grn_ts_bool key;
+    grn_table_get_key(ctx, node->table, in[i].id, &key, sizeof(key));
+    if (key) {
+      out[count++] = in[i];
+    }
+  }
+  *n_out = count;
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_value_node_filter() filters records. */
+static grn_rc
+grn_ts_expr_value_node_filter(grn_ctx *ctx, grn_ts_expr_value_node *node,
+                              grn_ts_record *in, size_t n_in,
+                              grn_ts_record *out, size_t *n_out) {
+  size_t i, count = 0;
+  for (i = 0; i < n_in; i++) {
+    grn_ts_bool value;
+    grn_ts_table_get_value(ctx, node->table, in[i].id, &value);
+    if (value) {
+      out[count++] = in[i];
+    }
+  }
+  *n_out = count;
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_const_node_filter() filters records. */
+static grn_rc
+grn_ts_expr_const_node_filter(grn_ctx *ctx, grn_ts_expr_const_node *node,
+                              grn_ts_record *in, size_t n_in,
+                              grn_ts_record *out, size_t *n_out) {
+  if (node->content.bool_value) {
+    if (in != out) {
+      size_t i;
+      for (i = 0; i < n_in; i++) {
+        out[i] = in[i];
+      }
+    }
+    *n_out = n_in;
+  } else {
+    *n_out = 0;
+  }
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_column_node_filter() filters records. */
+static grn_rc
+grn_ts_expr_column_node_filter(grn_ctx *ctx, grn_ts_expr_column_node *node,
+                               grn_ts_record *in, size_t n_in,
+                               grn_ts_record *out, size_t *n_out) {
+  size_t i, count = 0;
+  grn_ra *ra = (grn_ra *)node->column;
+  grn_ra_cache cache;
+  GRN_RA_CACHE_INIT(ra, &cache);
+  for (i = 0; i < n_in; i++) {
+    grn_ts_bool *ptr = NULL;
+    if (in[i].id) {
+      ptr = grn_ra_ref_cache(ctx, ra, in[i].id, &cache);
+    }
+    if (ptr && *ptr) {
+      out[count++] = in[i];
+    }
+  }
+  GRN_RA_CACHE_FIN(ra, &cache);
+  *n_out = count;
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_op_node_filter() filters records. */
+static grn_rc
+grn_ts_expr_op_node_filter(grn_ctx *ctx, grn_ts_expr_op_node *node,
+                           grn_ts_record *in, size_t n_in,
+                           grn_ts_record *out, size_t *n_out) {
+  // TODO
+  return GRN_SUCCESS;
+}
+
+#define GRN_TS_EXPR_NODE_FILTER_CASE_BLOCK(TYPE, type)\
+  case GRN_TS_EXPR_ ## TYPE ## _NODE: {\
+    grn_ts_expr_ ## type ## _node *type ## _node;\
+    type ## _node = (grn_ts_expr_ ## type ## _node *)node;\
+    return grn_ts_expr_ ## type ## _node_filter(ctx, type ## _node,\
+                                                in, n_in, out, n_out);\
+  }
+/* grn_ts_expr_node_filter() filters records. */
+static grn_rc
+grn_ts_expr_node_filter(grn_ctx *ctx, grn_ts_expr_node *node,
+                        grn_ts_record *in, size_t n_in,
+                        grn_ts_record *out, size_t *n_out) {
+  if (node->data_kind != GRN_TS_BOOL) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  switch (node->type) {
+    GRN_TS_EXPR_NODE_FILTER_CASE_BLOCK(KEY, key)
+    GRN_TS_EXPR_NODE_FILTER_CASE_BLOCK(VALUE, value)
+    GRN_TS_EXPR_NODE_FILTER_CASE_BLOCK(CONST, const)
+    GRN_TS_EXPR_NODE_FILTER_CASE_BLOCK(COLUMN, column)
+    GRN_TS_EXPR_NODE_FILTER_CASE_BLOCK(OP, op)
+    default: {
+      return GRN_INVALID_ARGUMENT;
+    }
+  }
+}
+#undef GRN_TS_EXPR_NODE_FILTER_CASE_BLOCK
+
+/* grn_ts_expr_score_node_adjust() updates scores. */
+static grn_rc
+grn_ts_expr_score_node_adjust(grn_ctx *ctx, grn_ts_expr_score_node *node,
+                              grn_ts_record *io, size_t n_io) {
+  /* Nothing to do. */
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_key_node_adjust() updates scores. */
+static grn_rc
+grn_ts_expr_key_node_adjust(grn_ctx *ctx, grn_ts_expr_key_node *node,
+                            grn_ts_record *io, size_t n_io) {
+  size_t i;
+  for (i = 0; i < n_io; i++) {
+    grn_ts_float key;
+    grn_table_get_key(ctx, node->table, io[i].id, &key, sizeof(key));
+    io[i].score = (grn_ts_score)key;
+  }
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_value_node_adjust() updates scores. */
+static grn_rc
+grn_ts_expr_value_node_adjust(grn_ctx *ctx, grn_ts_expr_value_node *node,
+                              grn_ts_record *io, size_t n_io) {
+  size_t i;
+  for (i = 0; i < n_io; i++) {
+    grn_ts_float value;
+    grn_ts_table_get_value(ctx, node->table, io[i].id, &value);
+    io[i].score = (grn_ts_score)value;
+  }
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_const_node_adjust() updates scores. */
+static grn_rc
+grn_ts_expr_const_node_adjust(grn_ctx *ctx, grn_ts_expr_const_node *node,
+                              grn_ts_record *io, size_t n_io) {
+  size_t i;
+  grn_ts_score score = (grn_ts_score)node->content.float_value;
+  for (i = 0; i < n_io; i++) {
+    io[i].score = score;
+  }
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_column_node_adjust() updates scores. */
+/* FIXME: Errors are ignored. */
+static grn_rc
+grn_ts_expr_column_node_adjust(grn_ctx *ctx, grn_ts_expr_column_node *node,
+                               grn_ts_record *io, size_t n_io) {
+  size_t i;
+  grn_ra *ra = (grn_ra *)node->column;
+  grn_ra_cache cache;
+  GRN_RA_CACHE_INIT(ra, &cache);
+  for (i = 0; i < n_io; i++) {
+    grn_ts_float *ptr = NULL;
+    if (io[i].id) {
+      ptr = grn_ra_ref_cache(ctx, ra, io[i].id, &cache);
+    }
+    if (ptr) {
+      io[i].score = (grn_ts_score)*ptr;
+    }
+  }
+  GRN_RA_CACHE_FIN(ra, &cache);
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_op_node_adjust() updates scores. */
+static grn_rc
+grn_ts_expr_op_node_adjust(grn_ctx *ctx, grn_ts_expr_op_node *node,
+                           grn_ts_record *io, size_t n_io) {
+  // TODO
+  return GRN_SUCCESS;
+}
+
+#define GRN_TS_EXPR_NODE_ADJUST_CASE_BLOCK(TYPE, type)\
+  case GRN_TS_EXPR_ ## TYPE ## _NODE: {\
+    grn_ts_expr_ ## type ## _node *type ## _node;\
+    type ## _node = (grn_ts_expr_ ## type ## _node *)node;\
+    return grn_ts_expr_ ## type ## _node_adjust(ctx, type ## _node, io, n_io);\
+  }
+/* grn_ts_expr_node_adjust() updates scores. */
+static grn_rc
+grn_ts_expr_node_adjust(grn_ctx *ctx, grn_ts_expr_node *node,
+                        grn_ts_record *io, size_t n_io) {
+  if (node->data_kind != GRN_TS_FLOAT) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  switch (node->type) {
+    GRN_TS_EXPR_NODE_ADJUST_CASE_BLOCK(SCORE, score)
+    GRN_TS_EXPR_NODE_ADJUST_CASE_BLOCK(KEY, key)
+    GRN_TS_EXPR_NODE_ADJUST_CASE_BLOCK(VALUE, value)
+    GRN_TS_EXPR_NODE_ADJUST_CASE_BLOCK(CONST, const)
+    GRN_TS_EXPR_NODE_ADJUST_CASE_BLOCK(COLUMN, column)
+    GRN_TS_EXPR_NODE_ADJUST_CASE_BLOCK(OP, op)
+    default: {
+      return GRN_INVALID_ARGUMENT;
+    }
+  }
+}
+#undef GRN_TS_EXPR_NODE_ADJUST_CASE_BLOCK
+
+/*-------------------------------------------------------------
+ * grn_ts_expr.
+ */
+
+/* grn_ts_expr_init() initializes an expression. */
+static grn_rc
+grn_ts_expr_init(grn_ctx *ctx, grn_ts_expr *expr, grn_obj *table) {
+  /* Increment a reference count for table and curr_table. */
+  grn_rc rc = grn_ts_obj_increment_ref_count(ctx, table);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  rc = grn_ts_obj_increment_ref_count(ctx, table);
+  if (rc != GRN_SUCCESS) {
+    grn_obj_unlink(ctx, table);
+    return rc;
+  }
+  memset(expr, 0, sizeof(*expr));
+  expr->table = table;
+  expr->curr_table = table;
+  expr->root = NULL;
+  expr->nodes = NULL;
+  expr->stack = NULL;
+  // TODO: Initialize new members.
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_open(grn_ctx *ctx, grn_obj *table, grn_ts_expr **expr) {
+  grn_rc rc;
+  grn_ts_expr *new_expr;
+  if (!ctx || !table || !grn_ts_obj_is_table(ctx, table) || !expr) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  new_expr = GRN_MALLOCN(grn_ts_expr, 1);
+  if (!new_expr) {
+    return GRN_NO_MEMORY_AVAILABLE;
+  }
+  rc = grn_ts_expr_init(ctx, new_expr, table);
+  if (rc != GRN_SUCCESS) {
+    GRN_FREE(new_expr);
+    return rc;
+  }
+  *expr = new_expr;
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_parse(grn_ctx *ctx, grn_obj *table,
+                  const char *str, size_t str_size, grn_ts_expr **expr) {
+  grn_rc rc;
+  grn_ts_expr *new_expr;
+  if (!ctx || !table || !grn_ts_obj_is_table(ctx, table) ||
+      !str || !str_size || !expr) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  rc = grn_ts_expr_open(ctx, table, &new_expr);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  // TODO: Hmm...
+  {
+    rc = grn_ts_expr_push(ctx, new_expr, str, str_size);
+    if (rc != GRN_SUCCESS) {
+      grn_ts_expr_close(ctx, new_expr);
+      return rc;
+    }
+    rc = grn_ts_expr_complete(ctx, new_expr);
+    if (rc != GRN_SUCCESS) {
+      grn_ts_expr_close(ctx, new_expr);
+      return rc;
+    }
+  }
+  *expr = new_expr;
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_fin() finalizes an expression. */
+static grn_rc
+grn_ts_expr_fin(grn_ctx *ctx, grn_ts_expr *expr) {
+  grn_rc rc = GRN_SUCCESS;
+  // TODO: Finalize new members.
+  if (expr->stack) {
+    GRN_FREE(expr->stack);
+  }
+  if (expr->nodes) {
+    size_t i;
+    for (i = 0; i < expr->n_nodes; i++) {
+      if (expr->nodes[i]) {
+        grn_rc rc_new = grn_ts_expr_node_close(ctx, expr->nodes[i]);
+        if (rc == GRN_SUCCESS) {
+          rc = rc_new;
+        }
+      }
+    }
+    GRN_FREE(expr->nodes);
+  }
+  if (expr->curr_table) {
+    grn_obj_unlink(ctx, expr->curr_table);
+  }
+  if (expr->table) {
+    grn_obj_unlink(ctx, expr->table);
+  }
+  return rc;
+}
+
+grn_rc
+grn_ts_expr_close(grn_ctx *ctx, grn_ts_expr *expr) {
+  grn_rc rc;
+  if (!ctx || !expr) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  rc = grn_ts_expr_fin(ctx, expr);
+  GRN_FREE(expr);
+  return rc;
+}
+
+grn_obj *
+grn_ts_expr_get_table(grn_ctx *ctx, grn_ts_expr *expr) {
+  if (!ctx || !expr) {
+    return NULL;
+  }
+  /* The reference counting will never fail in practice. */
+  if (grn_ts_obj_increment_ref_count(ctx, expr->table) != GRN_SUCCESS) {
+    return NULL;
+  }
+  return expr->table;
+}
+
+grn_ts_expr_type
+grn_ts_expr_get_type(grn_ctx *ctx, grn_ts_expr *expr) {
+  return (!ctx || !expr) ? GRN_TS_EXPR_BROKEN : expr->type;
+}
+
+grn_ts_data_kind
+grn_ts_expr_get_data_kind(grn_ctx *ctx, grn_ts_expr *expr) {
+  return (!ctx || !expr) ? GRN_TS_VOID : expr->data_kind;
+}
+
+grn_ts_data_type
+grn_ts_expr_get_data_type(grn_ctx *ctx, grn_ts_expr *expr) {
+  return (!ctx || !expr) ? GRN_DB_VOID : expr->data_type;
+}
+
+grn_ts_expr_node *
+grn_ts_expr_get_root(grn_ctx *ctx, grn_ts_expr *expr) {
+  return (!ctx || !expr) ? NULL : expr->root;
+}
+
+/* grn_ts_expr_reserve_nodes() extends a node buffer for a new node. */
+static grn_rc
+grn_ts_expr_reserve_nodes(grn_ctx *ctx, grn_ts_expr *expr) {
+  size_t i, n_bytes, new_max_n_nodes;
+  grn_ts_expr_node **new_nodes;
+  if (expr->n_nodes < expr->max_n_nodes) {
+    return GRN_SUCCESS;
+  }
+  new_max_n_nodes = expr->n_nodes * 2;
+  if (!new_max_n_nodes) {
+    new_max_n_nodes = 1;
+  }
+  n_bytes = sizeof(grn_ts_expr_node *) * new_max_n_nodes;
+  new_nodes = (grn_ts_expr_node **)GRN_REALLOC(expr->nodes, n_bytes);
+  if (!new_nodes) {
+    return GRN_NO_MEMORY_AVAILABLE;
+  }
+  for (i = expr->n_nodes; i < new_max_n_nodes; i++) {
+    new_nodes[i] = NULL;
+  }
+  expr->nodes = new_nodes;
+  expr->max_n_nodes = new_max_n_nodes;
+  return GRN_SUCCESS;
+}
+
+/*
+ * grn_ts_expr_open_id_node() opens and registers an ID node.
+ * Registered nodes will be closed in grn_ts_expr_fin().
+ */
+static grn_rc
+grn_ts_expr_open_id_node(grn_ctx *ctx, grn_ts_expr *expr,
+                         grn_ts_expr_node **node) {
+  grn_ts_expr_node *new_node;
+  grn_rc rc = grn_ts_expr_reserve_nodes(ctx, expr);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  rc = grn_ts_expr_id_node_open(ctx, &new_node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  expr->nodes[expr->n_nodes++] = new_node;
+  *node = new_node;
+  return GRN_SUCCESS;
+}
+
+/*
+ * grn_ts_expr_open_score_node() opens and registers a score node.
+ * Registered nodes will be closed in grn_ts_expr_fin().
+ */
+static grn_rc
+grn_ts_expr_open_score_node(grn_ctx *ctx, grn_ts_expr *expr,
+                            grn_ts_expr_node **node) {
+  grn_ts_expr_node *new_node;
+  grn_rc rc = grn_ts_expr_reserve_nodes(ctx, expr);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  rc = grn_ts_expr_score_node_open(ctx, &new_node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  expr->nodes[expr->n_nodes++] = new_node;
+  *node = new_node;
+  return GRN_SUCCESS;
+}
+
+/*
+ * grn_ts_expr_open_key_node() opens and registers a key node.
+ * Registered nodes will be closed in grn_ts_expr_fin().
+ */
+static grn_rc
+grn_ts_expr_open_key_node(grn_ctx *ctx, grn_ts_expr *expr,
+                          grn_ts_expr_node **node) {
+  grn_ts_expr_node *new_node;
+  grn_rc rc = grn_ts_expr_reserve_nodes(ctx, expr);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  rc = grn_ts_expr_key_node_open(ctx, expr->curr_table, &new_node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  expr->nodes[expr->n_nodes++] = new_node;
+  *node = new_node;
+  return GRN_SUCCESS;
+}
+
+/*
+ * grn_ts_expr_open_value_node() opens and registers a value node.
+ * Registered nodes will be closed in grn_ts_expr_fin().
+ */
+static grn_rc
+grn_ts_expr_open_value_node(grn_ctx *ctx, grn_ts_expr *expr,
+                            grn_ts_expr_node **node) {
+  grn_ts_expr_node *new_node;
+  grn_rc rc = grn_ts_expr_reserve_nodes(ctx, expr);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  rc = grn_ts_expr_value_node_open(ctx, expr->curr_table, &new_node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  expr->nodes[expr->n_nodes++] = new_node;
+  *node = new_node;
+  return GRN_SUCCESS;
+}
+
+/*
+ * grn_ts_expr_open_const_node() opens and registers a const node.
+ * Registered nodes will be closed in grn_ts_expr_fin().
+ */
+static grn_rc
+grn_ts_expr_open_const_node(grn_ctx *ctx, grn_ts_expr *expr,
+                            grn_ts_data_kind kind, const void *value,
+                            grn_ts_expr_node **node) {
+  grn_ts_expr_node *new_node;
+  grn_rc rc = grn_ts_expr_reserve_nodes(ctx, expr);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  rc = grn_ts_expr_const_node_open(ctx, kind, value, &new_node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  expr->nodes[expr->n_nodes++] = new_node;
+  *node = new_node;
+  return GRN_SUCCESS;
+}
+
+/*
+ * grn_ts_expr_open_column_node() opens and registers a column.
+ * Registered nodes will be closed in grn_ts_expr_fin().
+ */
+static grn_rc
+grn_ts_expr_open_column_node(grn_ctx *ctx, grn_ts_expr *expr,
+                             grn_obj *column, grn_ts_expr_node **node) {
+  grn_ts_expr_node *new_node;
+  grn_rc rc = grn_ts_expr_reserve_nodes(ctx, expr);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  rc = grn_ts_expr_column_node_open(ctx, column, &new_node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  expr->nodes[expr->n_nodes++] = new_node;
+  *node = new_node;
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_expr_push_node() pushes a node to stack. */
+static grn_rc
+grn_ts_expr_push_node(grn_ctx *ctx, grn_ts_expr *expr,
+                      grn_ts_expr_node *node) {
+  if (expr->stack_depth == expr->stack_size) {
+    size_t i, n_bytes, new_size;
+    grn_ts_expr_node **new_stack;
+    new_size = expr->stack_size * 2;
+    if (!new_size) {
+      new_size = 1;
+    }
+    n_bytes = sizeof(grn_ts_expr_node *) * new_size;
+    new_stack = GRN_REALLOC(expr->stack, n_bytes);
+    if (!new_stack) {
+      return GRN_NO_MEMORY_AVAILABLE;
+    }
+    for (i = expr->stack_size; i < new_size; i++) {
+      new_stack[i] = NULL;
+    }
+    expr->stack = new_stack;
+    expr->stack_size = new_size;
+  }
+  expr->stack[expr->stack_depth++] = node;
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push(grn_ctx *ctx, grn_ts_expr *expr,
+                 const char *str, size_t str_size) {
+  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_BROKEN) ||
+      !str || !str_size) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if ((str_size == GRN_COLUMN_NAME_ID_LEN) &&
+      !memcmp(str, GRN_COLUMN_NAME_ID, GRN_COLUMN_NAME_ID_LEN)) {
+    return grn_ts_expr_push_id(ctx, expr);
+  } else if ((str_size == GRN_COLUMN_NAME_KEY_LEN) &&
+             !memcmp(str, GRN_COLUMN_NAME_KEY, GRN_COLUMN_NAME_KEY_LEN)) {
+    return grn_ts_expr_push_key(ctx, expr);
+  } else if ((str_size == GRN_COLUMN_NAME_VALUE_LEN) &&
+             !memcmp(str, GRN_COLUMN_NAME_VALUE, GRN_COLUMN_NAME_VALUE_LEN)) {
+    return grn_ts_expr_push_value(ctx, expr);
+  } else if ((str_size == GRN_COLUMN_NAME_SCORE_LEN) &&
+             !memcmp(str, GRN_COLUMN_NAME_SCORE, GRN_COLUMN_NAME_SCORE_LEN)) {
+    return grn_ts_expr_push_score(ctx, expr);
+  } else if ((str_size == 4) && !memcmp(str, "true", 4)) {
+    return grn_ts_expr_push_bool(ctx, expr, GRN_TRUE);
+  } else if ((str_size == 5) && !memcmp(str, "false", 5)) {
+    return grn_ts_expr_push_bool(ctx, expr, GRN_FALSE);
+  } else if (isdigit((unsigned char)str[0])) {
+    char buf[1024];
+    grn_ts_int value;
+    if (str_size >= sizeof(buf)) {
+      return GRN_INVALID_ARGUMENT;
+    }
+    memcpy(buf, str, str_size);
+    buf[str_size] = '\0';
+    value = strtol(buf, NULL, 10);
+    return grn_ts_expr_push_int(ctx, expr, value);
+  } else {
+    grn_rc rc;
+    grn_obj *column = grn_obj_column(ctx, expr->curr_table, str, str_size);
+    if (!column) {
+      return GRN_INVALID_ARGUMENT;
+    }
+    rc = grn_ts_expr_push_column(ctx, expr, column);
+    grn_obj_unlink(ctx, column);
+    return rc;
+  }
+  // TODO: Parse the given string and push it.
+  return GRN_SUCCESS;
+}
+
+#define GRN_TS_EXPR_PUSH_BULK_CASE_BLOCK(TYPE, kind)\
+  case GRN_DB_ ## TYPE: {\
+    return grn_ts_expr_push_ ## kind(ctx, expr, GRN_ ## TYPE ## _VALUE(obj));\
+  }
+/* grn_ts_expr_push_bulk() pushes a scalar. */
+static grn_rc
+grn_ts_expr_push_bulk(grn_ctx *ctx, grn_ts_expr *expr, grn_obj *obj) {
+  switch (obj->header.domain) {
+    GRN_TS_EXPR_PUSH_BULK_CASE_BLOCK(BOOL, bool)
+    GRN_TS_EXPR_PUSH_BULK_CASE_BLOCK(INT8, int)
+    GRN_TS_EXPR_PUSH_BULK_CASE_BLOCK(INT16, int)
+    GRN_TS_EXPR_PUSH_BULK_CASE_BLOCK(INT32, int)
+    GRN_TS_EXPR_PUSH_BULK_CASE_BLOCK(INT64, int)
+    GRN_TS_EXPR_PUSH_BULK_CASE_BLOCK(UINT8, int)
+    GRN_TS_EXPR_PUSH_BULK_CASE_BLOCK(UINT16, int)
+    GRN_TS_EXPR_PUSH_BULK_CASE_BLOCK(UINT32, int)
+    /* The behavior is undefined if a value is greater than 2^63 - 1. */
+    GRN_TS_EXPR_PUSH_BULK_CASE_BLOCK(UINT64, int)
+    GRN_TS_EXPR_PUSH_BULK_CASE_BLOCK(FLOAT, float)
+    GRN_TS_EXPR_PUSH_BULK_CASE_BLOCK(TIME, time)
+    case GRN_DB_SHORT_TEXT:
+    case GRN_DB_TEXT:
+    case GRN_DB_LONG_TEXT: {
+      grn_ts_text value = { GRN_TEXT_VALUE(obj), GRN_TEXT_LEN(obj) };
+      return grn_ts_expr_push_text(ctx, expr, value);
+    }
+    case GRN_DB_TOKYO_GEO_POINT: {
+      grn_ts_geo_point value;
+      GRN_GEO_POINT_VALUE(obj, value.latitude, value.longitude);
+      return grn_ts_expr_push_tokyo_geo_point(ctx, expr, value);
+    }
+    case GRN_DB_WGS84_GEO_POINT: {
+      grn_ts_geo_point value;
+      GRN_GEO_POINT_VALUE(obj, value.latitude, value.longitude);
+      return grn_ts_expr_push_wgs84_geo_point(ctx, expr, value);
+    }
+    default: {
+      return GRN_INVALID_ARGUMENT;
+    }
+  }
+}
+#undef GRN_TS_EXPR_PUSH_BULK_CASE_BLOCK
+
+#define GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK(TYPE, kind)\
+  case GRN_DB_ ## TYPE: {\
+    grn_ts_ ## kind ##_vector value = { (grn_ts_ ## kind *)GRN_BULK_HEAD(obj),\
+                                        grn_uvector_size(ctx, obj) };\
+    return grn_ts_expr_push_ ## kind ## _vector(ctx, expr, value);\
+  }
+#define GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK_WITH_TYPECAST(TYPE, kind)\
+  case GRN_DB_ ## TYPE: {\
+    size_t i;\
+    grn_rc rc;\
+    grn_ts_ ## kind ## _vector value = { NULL, grn_uvector_size(ctx, obj) };\
+    if (!value.size) {\
+      return grn_ts_expr_push_ ## kind ## _vector(ctx, expr, value);\
+    }\
+    grn_ts_ ## kind *buf = GRN_MALLOCN(grn_ts_ ## kind, value.size);\
+    if (!buf) {\
+      return GRN_NO_MEMORY_AVAILABLE;\
+    }\
+    for (i = 0; i < value.size; i++) {\
+      buf[i] = GRN_ ## TYPE ##_VALUE_AT(obj, i);\
+    }\
+    value.ptr = buf;\
+    rc = grn_ts_expr_push_ ## kind ## _vector(ctx, expr, value);\
+    GRN_FREE(buf);\
+    return rc;\
+  }
+/* grn_ts_expr_push_uvector() pushes an array of fixed-size values. */
+static grn_rc
+grn_ts_expr_push_uvector(grn_ctx *ctx, grn_ts_expr *expr, grn_obj *obj) {
+  switch (obj->header.domain) {
+    GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK(BOOL, bool)
+    GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK_WITH_TYPECAST(INT8, int)
+    GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK_WITH_TYPECAST(INT16, int)
+    GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK_WITH_TYPECAST(INT32, int)
+    GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK(INT64, int)
+    GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK_WITH_TYPECAST(UINT8, int)
+    GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK_WITH_TYPECAST(UINT16, int)
+    GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK_WITH_TYPECAST(UINT32, int)
+    GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK(UINT64, int)
+    GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK(TIME, time)
+    GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK(TOKYO_GEO_POINT, tokyo_geo_point)
+    GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK(WGS84_GEO_POINT, wgs84_geo_point)
+    default: {
+      return GRN_INVALID_ARGUMENT;
+    }
+  }
+}
+#undef GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK_WITH_TYPECAST
+#undef GRN_TS_EXPR_PUSH_UVECTOR_CASE_BLOCK
+
+/* grn_ts_expr_push_uvector() pushes an array of texts. */
+static grn_rc
+grn_ts_expr_push_vector(grn_ctx *ctx, grn_ts_expr *expr, grn_obj *obj) {
+  switch (obj->header.domain) {
+    case GRN_DB_SHORT_TEXT:
+    case GRN_DB_TEXT:
+    case GRN_DB_LONG_TEXT: {
+      size_t i;
+      grn_rc rc;
+      grn_ts_text_vector value = { NULL, grn_vector_size(ctx, obj) };
+      if (!value.size) {
+        return grn_ts_expr_push_text_vector(ctx, expr, value);
+      }
+      grn_ts_text *buf = GRN_MALLOCN(grn_ts_text, value.size);
+      if (!buf) {
+        return GRN_NO_MEMORY_AVAILABLE;
+      }
+      for (i = 0; i < value.size; i++) {
+        buf[i].size = grn_vector_get_element(ctx, obj, i, &buf[i].ptr,
+                                             NULL, NULL);
+      }
+      value.ptr = buf;
+      rc = grn_ts_expr_push_text_vector(ctx, expr, value);
+      GRN_FREE(buf);
+      return rc;
+    }
+    default: {
+      return GRN_INVALID_ARGUMENT;
+    }
+  }
+}
+
+static grn_rc
+grn_ts_expr_push_accessor(grn_ctx *ctx, grn_ts_expr *expr,
+                          grn_accessor *accessor) {
+  if (accessor->next) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  switch (accessor->action) {
+    case GRN_ACCESSOR_GET_ID: {
+      return grn_ts_expr_push_id(ctx, expr);
+    }
+    case GRN_ACCESSOR_GET_SCORE: {
+      return grn_ts_expr_push_score(ctx, expr);
+    }
+    case GRN_ACCESSOR_GET_KEY: {
+      return grn_ts_expr_push_key(ctx, expr);
+    }
+    case GRN_ACCESSOR_GET_VALUE: {
+      return grn_ts_expr_push_value(ctx, expr);
+    }
+    default: {
+      return GRN_INVALID_ARGUMENT;
+    }
+  }
+}
+
+grn_rc
+grn_ts_expr_push_obj(grn_ctx *ctx, grn_ts_expr *expr, grn_obj *obj) {
+  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_BROKEN) || !obj) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  switch (obj->header.type) {
+    case GRN_BULK: {
+      return grn_ts_expr_push_bulk(ctx, expr, obj);
+    }
+    case GRN_UVECTOR: {
+      return grn_ts_expr_push_uvector(ctx, expr, obj);
+    }
+    case GRN_VECTOR: {
+      return grn_ts_expr_push_vector(ctx, expr, obj);
+    }
+    case GRN_ACCESSOR: {
+      return grn_ts_expr_push_accessor(ctx, expr, (grn_accessor *)obj);
+    }
+    case GRN_COLUMN_FIX_SIZE:
+    case GRN_COLUMN_VAR_SIZE: {
+      return grn_ts_expr_push_column(ctx, expr, obj);
+    }
+    default: {
+      return GRN_INVALID_ARGUMENT;
+    }
+  }
+}
+
+grn_rc
+grn_ts_expr_push_id(grn_ctx *ctx, grn_ts_expr *expr) {
+  grn_rc rc;
+  grn_ts_expr_node *node;
+  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_BROKEN)) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  rc = grn_ts_expr_open_id_node(ctx, expr, &node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  rc = grn_ts_expr_push_node(ctx, expr, node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_score(grn_ctx *ctx, grn_ts_expr *expr) {
+  grn_rc rc;
+  grn_ts_expr_node *node;
+  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_BROKEN)) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  rc = grn_ts_expr_open_score_node(ctx, expr, &node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  rc = grn_ts_expr_push_node(ctx, expr, node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_key(grn_ctx *ctx, grn_ts_expr *expr) {
+  grn_rc rc;
+  grn_ts_expr_node *node;
+  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_BROKEN)) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  rc = grn_ts_expr_open_key_node(ctx, expr, &node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  rc = grn_ts_expr_push_node(ctx, expr, node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_value(grn_ctx *ctx, grn_ts_expr *expr) {
+  grn_rc rc;
+  grn_ts_expr_node *node;
+  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_BROKEN)) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  rc = grn_ts_expr_open_value_node(ctx, expr, &node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  rc = grn_ts_expr_push_node(ctx, expr, node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  return GRN_SUCCESS;
+}
+
+#define GRN_TS_EXPR_PUSH_CONST_CASE_BLOCK(KIND, kind)\
+  case GRN_TS_ ## KIND: {\
+    return grn_ts_expr_push_ ## kind(ctx, expr,\
+                                     *(const grn_ts_ ## kind *)value);\
+  }
+grn_rc
+grn_ts_expr_push_const(grn_ctx *ctx, grn_ts_expr *expr,
+                       grn_ts_data_kind kind, const void *value) {
+  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_BROKEN) || !value) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  switch (kind) {
+    GRN_TS_EXPR_PUSH_CONST_CASE_BLOCK(BOOL, bool)
+    GRN_TS_EXPR_PUSH_CONST_CASE_BLOCK(INT, int)
+    GRN_TS_EXPR_PUSH_CONST_CASE_BLOCK(FLOAT, float)
+    GRN_TS_EXPR_PUSH_CONST_CASE_BLOCK(TIME, time)
+    GRN_TS_EXPR_PUSH_CONST_CASE_BLOCK(TEXT, text)
+    GRN_TS_EXPR_PUSH_CONST_CASE_BLOCK(GEO_POINT, geo_point)
+    GRN_TS_EXPR_PUSH_CONST_CASE_BLOCK(BOOL_VECTOR, bool_vector)
+    GRN_TS_EXPR_PUSH_CONST_CASE_BLOCK(INT_VECTOR, int_vector)
+    GRN_TS_EXPR_PUSH_CONST_CASE_BLOCK(FLOAT_VECTOR, float_vector)
+    GRN_TS_EXPR_PUSH_CONST_CASE_BLOCK(TIME_VECTOR, time_vector)
+    GRN_TS_EXPR_PUSH_CONST_CASE_BLOCK(TEXT_VECTOR, text_vector)
+    GRN_TS_EXPR_PUSH_CONST_CASE_BLOCK(GEO_POINT_VECTOR, geo_point_vector)
+    default: {
+      return GRN_INVALID_ARGUMENT;
+    }
+  }
+}
+#undef GRN_TS_EXPR_PUSH_CONST_CASE_BLOCK
+
+grn_rc
+grn_ts_expr_push_column(grn_ctx *ctx, grn_ts_expr *expr, grn_obj *column) {
+  grn_rc rc;
+  grn_ts_expr_node *node;
+  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_BROKEN) ||
+      !column || !grn_ts_obj_is_column(ctx, column) ||
+      (DB_OBJ(expr->curr_table)->id != column->header.domain)) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  rc = grn_ts_expr_open_column_node(ctx, expr, column, &node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  rc = grn_ts_expr_push_node(ctx, expr, node);
+  if (rc != GRN_SUCCESS) {
+    return rc;
+  }
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_operator(grn_ctx *ctx, grn_ts_expr *expr,
+                          grn_ts_op_type op_type) {
+  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_BROKEN)) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  // TODO
+  return GRN_SUCCESS;
+}
+
+#define GRN_TS_EXPR_PUSH_CONST(KIND, kind)\
+  grn_rc rc;\
+  grn_ts_expr_node *node;\
+  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_BROKEN) ||\
+      !grn_ts_ ## kind ## _is_valid(value)) {\
+    return GRN_INVALID_ARGUMENT;\
+  }\
+  rc = grn_ts_expr_open_const_node(ctx, expr, GRN_TS_ ## KIND, &value, &node);\
+  if (rc != GRN_SUCCESS) {\
+    return rc;\
+  }\
+  rc = grn_ts_expr_push_node(ctx, expr, node);\
+  if (rc != GRN_SUCCESS) {\
+    return rc;\
+  }
+grn_rc
+grn_ts_expr_push_bool(grn_ctx *ctx, grn_ts_expr *expr, grn_ts_bool value) {
+  GRN_TS_EXPR_PUSH_CONST(BOOL, bool)
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_int(grn_ctx *ctx, grn_ts_expr *expr, grn_ts_int value) {
+  GRN_TS_EXPR_PUSH_CONST(INT, int)
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_float(grn_ctx *ctx, grn_ts_expr *expr, grn_ts_float value) {
+  GRN_TS_EXPR_PUSH_CONST(FLOAT, float)
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_time(grn_ctx *ctx, grn_ts_expr *expr, grn_ts_time value) {
+  GRN_TS_EXPR_PUSH_CONST(TIME, time)
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_text(grn_ctx *ctx, grn_ts_expr *expr, grn_ts_text value) {
+  GRN_TS_EXPR_PUSH_CONST(TEXT, text)
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_geo_point(grn_ctx *ctx, grn_ts_expr *expr,
+                           grn_ts_geo_point value) {
+  GRN_TS_EXPR_PUSH_CONST(GEO_POINT, geo_point)
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_tokyo_geo_point(grn_ctx *ctx, grn_ts_expr *expr,
+                                 grn_ts_geo_point value) {
+  GRN_TS_EXPR_PUSH_CONST(GEO_POINT, geo_point)
+  node->data_type = GRN_DB_TOKYO_GEO_POINT;
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_wgs84_geo_point(grn_ctx *ctx, grn_ts_expr *expr,
+                                 grn_ts_geo_point value) {
+  GRN_TS_EXPR_PUSH_CONST(GEO_POINT, geo_point)
+  node->data_type = GRN_DB_WGS84_GEO_POINT;
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_bool_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                             grn_ts_bool_vector value) {
+  GRN_TS_EXPR_PUSH_CONST(BOOL_VECTOR, bool_vector)
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_int_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                            grn_ts_int_vector value) {
+  GRN_TS_EXPR_PUSH_CONST(INT_VECTOR, int_vector)
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_float_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                              grn_ts_float_vector value) {
+  GRN_TS_EXPR_PUSH_CONST(FLOAT_VECTOR, float_vector)
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_time_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                             grn_ts_time_vector value) {
+  GRN_TS_EXPR_PUSH_CONST(TIME_VECTOR, time_vector)
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_text_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                             grn_ts_text_vector value) {
+  GRN_TS_EXPR_PUSH_CONST(TEXT_VECTOR, text_vector)
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_geo_point_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                                  grn_ts_geo_point_vector value) {
+  GRN_TS_EXPR_PUSH_CONST(GEO_POINT_VECTOR, geo_point_vector)
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_tokyo_geo_point_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                                        grn_ts_geo_point_vector value) {
+  GRN_TS_EXPR_PUSH_CONST(GEO_POINT_VECTOR, geo_point_vector)
+  node->data_type = GRN_DB_TOKYO_GEO_POINT;
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_push_wgs84_geo_point_vector(grn_ctx *ctx, grn_ts_expr *expr,
+                                        grn_ts_geo_point_vector value) {
+  GRN_TS_EXPR_PUSH_CONST(GEO_POINT_VECTOR, geo_point_vector)
+  node->data_type = GRN_DB_TOKYO_GEO_POINT;
+  return GRN_SUCCESS;
+}
+#undef GRN_TS_EXPR_PUSH_CONST
+
+grn_rc
+grn_ts_expr_complete(grn_ctx *ctx, grn_ts_expr *expr) {
+  grn_ts_expr_node *root;
+  if (!ctx || !expr || (expr->type != GRN_TS_EXPR_INCOMPLETE)) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (expr->stack_depth != 1) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  root = expr->stack[0];
+  switch (root->data_kind) {
+    case GRN_TS_REF:
+    case GRN_TS_REF_VECTOR: {
+      return GRN_INVALID_ARGUMENT;
+    }
+    default: {
+      break;
+    }
+  }
+  switch (root->type) {
+    case GRN_TS_EXPR_ID_NODE: {
+      expr->type = GRN_TS_EXPR_ID;
+      break;
+    }
+    case GRN_TS_EXPR_SCORE_NODE: {
+      expr->type = GRN_TS_EXPR_SCORE;
+      break;
+    }
+    case GRN_TS_EXPR_KEY_NODE:
+    case GRN_TS_EXPR_VALUE_NODE: {
+      expr->type = GRN_TS_EXPR_VARIABLE;
+      break;
+    }
+    case GRN_TS_EXPR_CONST_NODE: {
+      expr->type = GRN_TS_EXPR_CONST;
+      break;
+    }
+    case GRN_TS_EXPR_COLUMN_NODE:
+    case GRN_TS_EXPR_OP_NODE: {
+      expr->type = GRN_TS_EXPR_VARIABLE;
+      break;
+    }
+    default: {
+      return GRN_INVALID_ARGUMENT;
+    }
+  }
+  expr->data_type = root->data_type;
+  expr->data_kind = root->data_kind;
+  expr->root = root;
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_expr_evaluate(grn_ctx *ctx, grn_ts_expr *expr,
+                     const grn_ts_record *in, size_t n_in, void *out) {
+  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_INCOMPLETE) ||
+      (expr->type == GRN_TS_EXPR_BROKEN) || (!in && n_in) || (n_in && !out)) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!n_in) {
+    return GRN_SUCCESS;
+  }
+  return grn_ts_expr_node_evaluate(ctx, expr->root, in, n_in, out);
+}
+
+grn_rc
+grn_ts_expr_filter(grn_ctx *ctx, grn_ts_expr *expr,
+                   grn_ts_record *in, size_t n_in,
+                   grn_ts_record *out, size_t *n_out) {
+  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_INCOMPLETE) ||
+      (expr->type == GRN_TS_EXPR_BROKEN) || (!in && n_in) ||
+      !out || !n_out) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!n_in) {
+    *n_out = 0;
+    return GRN_SUCCESS;
+  }
+  return grn_ts_expr_node_filter(ctx, expr->root, in, n_in, out, n_out);
+}
+
+grn_rc
+grn_ts_expr_adjust(grn_ctx *ctx, grn_ts_expr *expr,
+                   grn_ts_record *io, size_t n_io) {
+  if (!ctx || !expr || (expr->type == GRN_TS_EXPR_INCOMPLETE) ||
+      (expr->type == GRN_TS_EXPR_BROKEN) || (!io && n_io)) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  if (!n_io) {
+    return GRN_SUCCESS;
+  }
+  return grn_ts_expr_node_adjust(ctx, expr->root, io, n_io);
+}
+
+/*-------------------------------------------------------------
+ * API.
+ */
+
+/* grn_ts_select_filter() applies a filter to all the records of a table. */
+static grn_rc
+grn_ts_select_filter(grn_ctx *ctx, grn_obj *table,
+                     const char *str, size_t str_size,
+                     size_t offset, size_t limit,
+                     grn_ts_record **out, size_t *n_out, size_t *n_hits) {
+  grn_rc rc, tmp_rc;
+  grn_table_cursor *cursor;
+  grn_ts_expr *expr;
+  grn_ts_record *buf = NULL;
+  size_t buf_size = 0;
+
+  *out = NULL;
+  *n_out = 0;
+  *n_hits = 0;
+
+  cursor = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1,
+                                 GRN_CURSOR_ASCENDING | GRN_CURSOR_BY_ID);
+  if (!cursor) {
+    return (ctx->rc != GRN_SUCCESS) ? ctx->rc : GRN_UNKNOWN_ERROR;
+  }
+
+  rc = grn_ts_expr_parse(ctx, table, str, str_size, &expr);
+  if (rc == GRN_SUCCESS) {
+    for ( ; ; ) {
+      size_t i, batch_size;
+      grn_ts_record *batch;
+
+      /* Extend the record buffer. */
+      if (buf_size < (*n_out + GRN_TS_BATCH_SIZE)) {
+        size_t new_size = buf_size ? (buf_size * 2) : GRN_TS_BATCH_SIZE;
+        size_t n_bytes = sizeof(grn_ts_record) * new_size;
+        grn_ts_record *new_buf = (grn_ts_record *)GRN_REALLOC(buf, n_bytes);
+        if (!new_buf) {
+          rc = GRN_NO_MEMORY_AVAILABLE;
+          break;
+        }
+        buf = new_buf;
+        buf_size = new_size;
+      }
+
+      /* Read records from the cursor. */
+      batch = buf + *n_out;
+      for (i = 0; i < GRN_TS_BATCH_SIZE; i++) {
+        batch[i].id = grn_table_cursor_next(ctx, cursor);
+        if (batch[i].id == GRN_ID_NIL) {
+          break;
+        }
+        batch[i].score = 0.0;
+      }
+      batch_size = i;
+      if (!batch_size) {
+        break;
+      }
+
+      /* Apply the filter. */
+      rc = grn_ts_expr_filter(ctx, expr, batch, batch_size,
+                              batch, &batch_size);
+      if (rc != GRN_SUCCESS) {
+        break;
+      }
+      *n_hits += batch_size;
+
+      /* Apply the offset and the limit. */
+      if (offset) {
+        if (batch_size <= offset) {
+          offset -= batch_size;
+          batch_size = 0;
+        } else {
+          size_t n_bytes = sizeof(grn_ts_record) * (batch_size - offset);
+          memcpy(batch, batch + offset, n_bytes);
+          batch_size -= offset;
+          offset = 0;
+        }
+      }
+      if (batch_size <= limit) {
+        limit -= batch_size;
+      } else {
+        batch_size = limit;
+        limit = 0;
+      }
+      *n_out += batch_size;
+    }
+    tmp_rc = grn_ts_expr_close(ctx, expr);
+    if (rc == GRN_SUCCESS) {
+      rc = tmp_rc;
+    }
+  }
+
+  tmp_rc = grn_table_cursor_close(ctx, cursor);
+  if (rc == GRN_SUCCESS) {
+    rc = tmp_rc;
+  }
+
+  if (rc != GRN_SUCCESS) {
+    if (buf) {
+      GRN_FREE(buf);
+    }
+    *n_out = 0;
+    *n_hits = 0;
+    return rc;
+  }
+  *out = buf;
+  return GRN_SUCCESS;
+}
+
+/* grn_ts_select_output() outputs the results. */
+/* FIXME: Too long. */
+/* FIXME: Errors are ignored. */
+static grn_rc
+grn_ts_select_output(grn_ctx *ctx, grn_obj *table,
+                     const char *str, size_t str_size,
+                     const grn_ts_record *in, size_t n_in, size_t n_hits) {
+  const char *rest = str;
+  size_t rest_size = str_size;
+  grn_ts_expr **exprs = NULL;
+  size_t i, j, k, n_exprs = 0;
+  grn_obj name_buf;
+  grn_ts_text *names = NULL;
+
+  GRN_TEXT_INIT(&name_buf, GRN_OBJ_VECTOR);
+  while (rest_size) {
+    const char *name = rest;
+    size_t name_size;
+
+    /* Find a delimiter. */
+    for (i = 0; i < rest_size; i++) {
+      if (rest[i] == ',') {
+        break;
+      }
+    }
+    name_size = i;
+
+    rest += name_size;
+    rest_size -= name_size;
+    if (rest_size) {
+      rest++;
+      rest_size--;
+    }
+
+    /* Trim spaces. */
+    for (i = 0; i < name_size; i++) {
+      if (!isspace((unsigned char)name[i])) {
+        break;
+      }
+    }
+    name += i;
+    name_size -= i;
+    for (i = name_size; i > 0; i--) {
+      if (!isspace((unsigned char)name[i - 1])) {
+        break;
+      }
+    }
+    name_size = i;
+    if (!name_size) {
+      continue;
+    }
+
+    /* Add column names to name_buf. */
+    if (name[name_size - 1] == '*') {
+      /* Expand a wildcard. */
+      grn_hash *columns = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+                                          GRN_OBJ_TABLE_HASH_KEY |
+                                          GRN_HASH_TINY);
+      if (columns) {
+        if (grn_table_columns(ctx, table, "", 0, (grn_obj *)columns)) {
+          grn_id *key;
+          GRN_HASH_EACH(ctx, columns, id, &key, NULL, NULL, {
+            grn_obj *column = grn_ctx_at(ctx, *key);
+            if (column) {
+              char buf[1024];
+              size_t len = grn_column_name(ctx, column, buf, 1024);
+              grn_vector_add_element(ctx, &name_buf, buf, len, 0, GRN_DB_TEXT);
+              grn_obj_unlink(ctx, column);
+            }
+          });
+        }
+        grn_hash_close(ctx, columns);
+      }
+    } else {
+      grn_vector_add_element(ctx, &name_buf, name, name_size, 0, GRN_DB_TEXT);
+    }
+  }
+
+  /* Create expressions. */
+  n_exprs = grn_vector_size(ctx, &name_buf);
+  if (n_exprs) {
+    size_t count = 0;
+    names = GRN_MALLOCN(grn_ts_text, n_exprs);
+    exprs = GRN_MALLOCN(grn_ts_expr *, n_exprs);
+    if (!names || !exprs) {
+      n_exprs = 0;
+    }
+    for (i = 0; i < n_exprs; i++) {
+      names[i].size = grn_vector_get_element(ctx, &name_buf, i, &names[i].ptr,
+                                             NULL, NULL);
+      if (grn_ts_expr_parse(ctx, table, names[i].ptr, names[i].size,
+                            &exprs[i]) != GRN_SUCCESS) {
+        exprs[i] = NULL;
+      }
+    }
+    for (i = 0; i < n_exprs; i++) {
+      if (exprs[i]) {
+        names[count] = names[i];
+        exprs[count] = exprs[i];
+        count++;
+      }
+    }
+    n_exprs = count;
+  }
+
+  GRN_OUTPUT_ARRAY_OPEN("RESULT", 1);
+  GRN_OUTPUT_ARRAY_OPEN("RESULTSET", 2 + n_in);
+
+  GRN_OUTPUT_ARRAY_OPEN("NHITS", 1);
+  grn_text_ulltoa(ctx, ctx->impl->outbuf, n_hits);
+  GRN_OUTPUT_ARRAY_CLOSE(); /* NHITS. */
+
+  GRN_OUTPUT_ARRAY_OPEN("COLUMNS", n_exprs);
+  for (size_t i = 0; i < n_exprs; ++i) {
+    GRN_OUTPUT_ARRAY_OPEN("COLUMN", 2);
+    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '"');
+    GRN_TEXT_PUT(ctx, ctx->impl->outbuf, names[i].ptr, names[i].size);
+    GRN_TEXT_PUT(ctx, ctx->impl->outbuf, "\",\"", 3);
+    switch (exprs[i]->data_type) {
+      case GRN_DB_VOID: {
+        if (exprs[i]->data_kind == GRN_TS_GEO_POINT) {
+          GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "GeoPoint");
+        } else {
+          GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "Void");
+        }
+        break;
+      }
+#define GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(TYPE, name)\
+  case GRN_DB_ ## TYPE: {\
+    GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, name);\
+    break;\
+  }
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(BOOL, "Bool")
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(INT8, "Int8")
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(INT16, "Int16")
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(INT32, "Int32")
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(INT64, "Int64")
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(UINT8, "UInt8")
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(UINT16, "UInt16")
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(UINT32, "UInt32")
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(UINT64, "UInt64")
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(FLOAT, "Float")
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(TIME, "Time")
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(SHORT_TEXT, "ShortText")
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(TEXT, "Text")
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(LONG_TEXT, "LongText")
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(TOKYO_GEO_POINT, "TokyoGeoPoint")
+      GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK(WGS84_GEO_POINT, "WGS84GeoPoint")
+#undef GRN_TS_SELECT_OUTPUT_TYPE_CASE_BLOCK
+      default: {
+        grn_obj *obj = grn_ctx_at(ctx, exprs[i]->data_type);
+        if (obj && grn_ts_obj_is_table(ctx, obj)) {
+          char name[GRN_TABLE_MAX_KEY_SIZE];
+          int len = grn_obj_name(ctx, obj, name, sizeof(name));
+          GRN_TEXT_PUT(ctx, ctx->impl->outbuf, name, len);
+          grn_obj_unlink(ctx, obj);
+        } else {
+          GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "Unknown");
+        }
+        break;
+      }
+    }
+    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '"');
+    GRN_OUTPUT_ARRAY_CLOSE();
+  }
+  GRN_OUTPUT_ARRAY_CLOSE(); /* COLUMNS. */
+
+  if (n_in) {
+    size_t count = 0;
+    void **bufs = GRN_MALLOCN(void *, n_exprs);
+    if (bufs) {
+      for (i = 0; i < n_exprs; i++) {
+        switch (exprs[i]->data_kind) {
+#define GRN_TS_SELECT_OUTPUT_MALLOC_CASE_BLOCK(KIND, kind)\
+  case GRN_TS_ ## KIND: {\
+    bufs[i] = GRN_MALLOCN(grn_ts_ ## kind, GRN_TS_BATCH_SIZE);\
+    break;\
+  }
+#define GRN_TS_SELECT_OUTPUT_MALLOC_VECTOR_CASE_BLOCK(KIND, kind)\
+  GRN_TS_SELECT_OUTPUT_MALLOC_CASE_BLOCK(KIND ## _VECTOR, kind ## _vector)
+          GRN_TS_SELECT_OUTPUT_MALLOC_CASE_BLOCK(BOOL, bool)
+          GRN_TS_SELECT_OUTPUT_MALLOC_CASE_BLOCK(INT, int)
+          GRN_TS_SELECT_OUTPUT_MALLOC_CASE_BLOCK(FLOAT, float)
+          GRN_TS_SELECT_OUTPUT_MALLOC_CASE_BLOCK(TIME, time)
+          GRN_TS_SELECT_OUTPUT_MALLOC_CASE_BLOCK(TEXT, text)
+          GRN_TS_SELECT_OUTPUT_MALLOC_CASE_BLOCK(GEO_POINT, geo_point)
+          GRN_TS_SELECT_OUTPUT_MALLOC_VECTOR_CASE_BLOCK(BOOL, bool)
+          GRN_TS_SELECT_OUTPUT_MALLOC_VECTOR_CASE_BLOCK(INT, int)
+          GRN_TS_SELECT_OUTPUT_MALLOC_VECTOR_CASE_BLOCK(FLOAT, float)
+          GRN_TS_SELECT_OUTPUT_MALLOC_VECTOR_CASE_BLOCK(TIME, time)
+          GRN_TS_SELECT_OUTPUT_MALLOC_VECTOR_CASE_BLOCK(TEXT, text)
+          GRN_TS_SELECT_OUTPUT_MALLOC_VECTOR_CASE_BLOCK(GEO_POINT, geo_point)
+#undef GRN_TS_SELECT_OUTPUT_MALLOC_VECTOR_CASE_BLOCK
+#undef GRN_TS_SELECT_OUTPUT_MALLOC_CASE_BLOCK
+          default: {
+            bufs[i] = NULL;
+            break;
+          }
+        }
+      }
+      while (count < n_in) {
+        size_t batch_size = GRN_TS_BATCH_SIZE;
+        if (batch_size > (n_in - count)) {
+          batch_size = n_in - count;
+        }
+        for (i = 0; i < n_exprs; ++i) {
+          if (!bufs[i]) {
+            continue;
+          }
+          grn_ts_expr_evaluate(ctx, exprs[i], in + count, batch_size, bufs[i]);
+        }
+        for (i = 0; i < batch_size; ++i) {
+          GRN_OUTPUT_ARRAY_OPEN("HIT", n_exprs);
+          for (j = 0; j < n_exprs; ++j) {
+            if (j) {
+              GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ',');
+            }
+            if (!bufs[j]) {
+              GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "null");
+              continue;
+            }
+            /* TODO: Define a function for each data kind. */
+            switch (exprs[j]->data_kind) {
+              case GRN_TS_BOOL: {
+                grn_ts_bool *batch = (grn_ts_bool *)bufs[j];
+                if (batch[i]) {
+                  GRN_TEXT_PUT(ctx, ctx->impl->outbuf, "true", 4);
+                } else {
+                  GRN_TEXT_PUT(ctx, ctx->impl->outbuf, "false", 5);
+                }
+                break;
+              }
+              case GRN_TS_INT: {
+                grn_ts_int *batch = (grn_ts_int *)bufs[j];
+                grn_text_lltoa(ctx, ctx->impl->outbuf, batch[i]);
+                break;
+              }
+              case GRN_TS_FLOAT: {
+                grn_ts_float *batch = (grn_ts_float *)bufs[j];
+                grn_text_ftoa(ctx, ctx->impl->outbuf, batch[i]);
+                break;
+              }
+              case GRN_TS_TIME: {
+                grn_ts_time *batch = (grn_ts_time *)bufs[j];
+                grn_text_ftoa(ctx, ctx->impl->outbuf, batch[i] * 0.000001);
+                break;
+              }
+              case GRN_TS_TEXT: {
+                grn_ts_text *batch = (grn_ts_text *)bufs[j];
+                grn_text_esc(ctx, ctx->impl->outbuf,
+                             batch[i].ptr, batch[i].size);
+                break;
+              }
+              case GRN_TS_GEO_POINT: {
+                grn_ts_geo_point *batch = (grn_ts_geo_point *)bufs[j];
+                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '"');
+                grn_text_itoa(ctx, ctx->impl->outbuf, batch[i].latitude);
+                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, 'x');
+                grn_text_itoa(ctx, ctx->impl->outbuf, batch[i].longitude);
+                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '"');
+                break;
+              }
+              case GRN_TS_BOOL_VECTOR: {
+                grn_ts_bool_vector vector;
+                vector = ((grn_ts_bool_vector *)bufs[j])[i];
+                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '[');
+                for (k = 0; k < vector.size; ++k) {
+                  if (k) {
+                    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ',');
+                  }
+                  if (vector.ptr[k]) {
+                    GRN_TEXT_PUT(ctx, ctx->impl->outbuf, "true", 4);
+                  } else {
+                    GRN_TEXT_PUT(ctx, ctx->impl->outbuf, "false", 5);
+                  }
+                }
+                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ']');
+                break;
+              }
+              case GRN_TS_INT_VECTOR: {
+                grn_ts_int_vector vector;
+                vector = ((grn_ts_int_vector *)bufs[j])[i];
+                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '[');
+                for (k = 0; k < vector.size; ++k) {
+                  if (k) {
+                    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ',');
+                  }
+                  grn_text_lltoa(ctx, ctx->impl->outbuf, vector.ptr[k]);
+                }
+                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ']');
+                break;
+              }
+              case GRN_TS_FLOAT_VECTOR: {
+                grn_ts_float_vector vector;
+                vector = ((grn_ts_float_vector *)bufs[j])[i];
+                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '[');
+                for (k = 0; k < vector.size; ++k) {
+                  if (k) {
+                    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ',');
+                  }
+                  grn_text_ftoa(ctx, ctx->impl->outbuf, vector.ptr[k]);
+                }
+                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ']');
+                break;
+              }
+              case GRN_TS_TIME_VECTOR: {
+                grn_ts_time_vector vector;
+                vector = ((grn_ts_time_vector *)bufs[j])[i];
+                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '[');
+                for (k = 0; k < vector.size; ++k) {
+                  if (k) {
+                    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ',');
+                  }
+                  grn_text_ftoa(ctx, ctx->impl->outbuf,
+                                vector.ptr[k] * 0.000001);
+                }
+                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ']');
+                break;
+              }
+              case GRN_TS_TEXT_VECTOR: {
+                grn_ts_text_vector vector;
+                vector = ((grn_ts_text_vector *)bufs[j])[i];
+                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '[');
+                for (k = 0; k < vector.size; ++k) {
+                  if (k) {
+                    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ',');
+                  }
+                  grn_text_esc(ctx, ctx->impl->outbuf,
+                               vector.ptr[k].ptr, vector.ptr[k].size);
+                }
+                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ']');
+                break;
+              }
+              case GRN_TS_GEO_POINT_VECTOR: {
+                grn_ts_geo_point_vector vector;
+                vector = ((grn_ts_geo_point_vector *)bufs[j])[i];
+                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '[');
+                for (k = 0; k < vector.size; ++k) {
+                  if (k) {
+                    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ',');
+                  }
+                  GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '"');
+                  grn_text_itoa(ctx, ctx->impl->outbuf,
+                                vector.ptr[k].latitude);
+                  GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, 'x');
+                  grn_text_itoa(ctx, ctx->impl->outbuf,
+                                vector.ptr[k].longitude);
+                  GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '"');
+                }
+                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ']');
+                break;
+              }
+              default: {
+                break;
+              }
+            }
+          }
+          GRN_OUTPUT_ARRAY_CLOSE(); /* HITS. */
+        }
+        count += batch_size;
+      }
+      for (i = 0; i < n_exprs; i++) {
+        if (bufs[i]) {
+          GRN_FREE(bufs[i]);
+        }
+      }
+      GRN_FREE(bufs);
+    }
+  }
+  GRN_OUTPUT_ARRAY_CLOSE(); /* RESULTSET. */
+  GRN_OUTPUT_ARRAY_CLOSE(); /* RESET. */
+
+  /* Finalize. */
+  if (exprs) {
+    for (i = 0; i < n_exprs; i++) {
+      if (exprs[i]) {
+        grn_ts_expr_close(ctx, exprs[i]);
+      }
+    }
+    GRN_FREE(exprs);
+  }
+  if (names) {
+    GRN_FREE(names);
+  }
+  GRN_OBJ_FIN(ctx, &name_buf);
+  return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ts_select(grn_ctx *ctx, grn_obj *table,
+              const char *filter, size_t filter_size,
+              const char *output_columns, size_t output_columns_size,
+              size_t offset, size_t limit) {
+  grn_rc rc;
+  grn_ts_record *records = NULL;
+  size_t n_records, n_hits;
+  if (!ctx || !table || !grn_ts_obj_is_table(ctx, table) ||
+      (!filter && filter_size) || (!output_columns && output_columns_size)) {
+    return GRN_INVALID_ARGUMENT;
+  }
+  rc = grn_ts_select_filter(ctx, table, filter, filter_size, offset, limit,
+                            &records, &n_records, &n_hits);
+  if (rc == GRN_SUCCESS) {
+    rc = grn_ts_select_output(ctx, table, output_columns, output_columns_size,
+                              records, n_records, n_hits);
+  }
+  if (records) {
+    GRN_FREE(records);
+  }
+  return rc;
+}
+
+#endif /* GRN_WITH_TS */

  Deleted: lib/ts.cpp (+0 -3739) 100644
===================================================================
--- lib/ts.cpp    2015-09-01 21:58:28 +0900 (022c6cc)
+++ /dev/null
@@ -1,3739 +0,0 @@
-/* -*- c-basic-offset: 2 -*- */
-/*
-  Copyright(C) 2015 Brazil
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Lesser General Public
-  License version 2.1 as published by the Free Software Foundation.
-
-  This library is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with this library; if not, write to the Free Software
-  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-/* TS is an acronym for "Turbo Selector". */
-
-#ifdef GRN_WITH_TS
-
-#include "grn_ts.hpp"
-
-#include <cctype>
-#include <cstdio>
-#include <cstdlib>
-#include <limits>
-#include <memory>
-#include <string>
-
-#include <iostream>  // for debug!
-
-#include "grn_ctx_impl.h"
-#include "grn_db.h"
-#include "grn_output.h"
-#include "grn_str.h"
-
-namespace {
-
-enum {
-  GRN_TS_MAX_DATA_TYPE = GRN_DB_WGS84_GEO_POINT,
-  GRN_TS_MAX_BATCH_SIZE = 1024
-};
-
-grn_ts_data_kind grn_ts_data_type_to_kind(grn_ts_data_type data_type) {
-  switch (data_type) {
-    case GRN_DB_VOID: {
-      return GRN_TS_VOID;
-    }
-    case GRN_DB_BOOL: {
-      return GRN_TS_BOOL;
-    }
-    case GRN_DB_INT8:
-    case GRN_DB_INT16:
-    case GRN_DB_INT32:
-    case GRN_DB_INT64:
-    case GRN_DB_UINT8:
-    case GRN_DB_UINT16:
-    case GRN_DB_UINT32:
-    case GRN_DB_UINT64: {
-      return GRN_TS_INT;
-    }
-    case GRN_DB_FLOAT: {
-      return GRN_TS_FLOAT;
-    }
-    case GRN_DB_TIME: {
-      return GRN_TS_TIME;
-    }
-    case GRN_DB_SHORT_TEXT:
-    case GRN_DB_TEXT:
-    case GRN_DB_LONG_TEXT: {
-      return GRN_TS_TEXT;
-    }
-    case GRN_DB_TOKYO_GEO_POINT:
-    case GRN_DB_WGS84_GEO_POINT: {
-      return GRN_TS_GEO_POINT;
-    }
-    default: {
-      return GRN_TS_REF;
-    }
-  }
-}
-
-}  // namespace
-
-namespace grn {
-namespace ts {
-
-// -- TableCursor --
-
-// TableCursor is a wrapper for grn_table_cursor:
-// - GRN_CURSOR_PAT_KEY
-// - GRN_CURSOR_DAT_KEY
-// - GRN_CURSOR_HASH_KEY
-// - GRN_CURSOR_NO_KEY
-class TableCursor : public Cursor {
- public:
-  ~TableCursor() {
-    grn_table_cursor_close(ctx_, cursor_);
-  }
-
-  static grn_rc open(grn_ctx *ctx, grn_obj *cursor, Score default_score,
-                     Cursor **wrapper);
-
-  grn_rc read(Record *records, size_t size, size_t *count);
-
- private:
-  grn_ctx *ctx_;
-  grn_obj *cursor_;
-  Score default_score_;
-
-  TableCursor(grn_ctx *ctx, grn_obj *cursor, Score default_score)
-    : Cursor(), ctx_(ctx), cursor_(cursor), default_score_(default_score) {}
-};
-
-grn_rc TableCursor::open(grn_ctx *ctx, grn_obj *cursor, Score default_score,
-                         Cursor **wrapper) {
-  if (!ctx || !cursor || !wrapper) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  switch (cursor->header.type) {
-    case GRN_CURSOR_TABLE_PAT_KEY:
-    case GRN_CURSOR_TABLE_DAT_KEY:
-    case GRN_CURSOR_TABLE_HASH_KEY:
-    case GRN_CURSOR_TABLE_NO_KEY: {
-      break;
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-  TableCursor *new_wrapper =
-    new (std::nothrow) TableCursor(ctx, cursor, default_score);
-  if (!new_wrapper) {
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  *wrapper = new_wrapper;
-  return GRN_SUCCESS;
-}
-
-grn_rc TableCursor::read(Record *records, size_t size, size_t *count) {
-  if ((!records && (size != 0)) || !count) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  switch (cursor_->header.type) {
-    case GRN_CURSOR_TABLE_PAT_KEY: {
-      for (size_t i = 0; i < size; ++i) {
-        grn_id id = grn_pat_cursor_next(
-          ctx_, reinterpret_cast<grn_pat_cursor *>(cursor_));
-        if (id == GRN_ID_NIL) {
-          *count = i;
-          return GRN_SUCCESS;
-        }
-        records[i].id = id;
-        records[i].score = default_score_;
-      }
-      break;
-    }
-    case GRN_CURSOR_TABLE_DAT_KEY: {
-      for (size_t i = 0; i < size; ++i) {
-        grn_id id = grn_dat_cursor_next(
-          ctx_, reinterpret_cast<grn_dat_cursor *>(cursor_));
-        if (id == GRN_ID_NIL) {
-          *count = i;
-          return GRN_SUCCESS;
-        }
-        records[i].id = id;
-        records[i].score = default_score_;
-      }
-      break;
-    }
-    case GRN_CURSOR_TABLE_HASH_KEY: {
-      for (size_t i = 0; i < size; ++i) {
-        grn_id id = grn_hash_cursor_next(
-          ctx_, reinterpret_cast<grn_hash_cursor *>(cursor_));
-        if (id == GRN_ID_NIL) {
-          *count = i;
-          return GRN_SUCCESS;
-        }
-        records[i].id = id;
-        records[i].score = default_score_;
-      }
-      break;
-    }
-    case GRN_CURSOR_TABLE_NO_KEY: {
-      for (size_t i = 0; i < size; ++i) {
-        grn_id id = grn_array_cursor_next(
-          ctx_, reinterpret_cast<grn_array_cursor *>(cursor_));
-        if (id == GRN_ID_NIL) {
-          *count = i;
-          return GRN_SUCCESS;
-        }
-        records[i].id = id;
-        records[i].score = default_score_;
-      }
-      break;
-    }
-    default: {
-      return GRN_UNKNOWN_ERROR;
-    }
-  }
-  *count = size;
-  return GRN_SUCCESS;
-}
-
-// -- Cursor --
-
-grn_rc Cursor::open_table_cursor(
-  grn_ctx *ctx, grn_obj *table, Cursor **cursor) {
-  if (!ctx || !grn_obj_is_table(ctx, table) || !cursor) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  grn_table_cursor *table_cursor = grn_table_cursor_open(
-    ctx, table, NULL, 0, NULL, 0, 0, -1,
-    GRN_CURSOR_ASCENDING | GRN_CURSOR_BY_ID);
-  if (!table_cursor) {
-    return ctx->rc;
-  }
-  grn_rc rc = TableCursor::open(ctx, table_cursor, 0.0, cursor);
-  if (rc != GRN_SUCCESS) {
-    grn_table_cursor_close(ctx, table_cursor);
-  }
-  return rc;
-}
-
-grn_rc Cursor::read(Record *records, size_t size, size_t *count) {
-  if ((!records && (size != 0)) || !count) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  *count = 0;
-  return GRN_SUCCESS;
-}
-
-// -- ExpressionNode --
-
-class ExpressionNode {
- public:
-  ExpressionNode() {}
-  virtual ~ExpressionNode() {}
-
-  virtual ExpressionNodeType type() const = 0;
-  virtual DataKind data_kind() const = 0;
-  virtual DataType data_type() const = 0;
-  virtual grn_obj *ref_table() const = 0;
-  virtual int dimension() const = 0;
-
-  virtual grn_rc filter(Record *input, size_t input_size,
-                        Record *output, size_t *output_size) {
-    return GRN_OPERATION_NOT_SUPPORTED;
-  }
-  virtual grn_rc adjust(Record *records, size_t num_records) {
-    return GRN_OPERATION_NOT_SUPPORTED;
-  }
-  virtual grn_rc evaluate(const Record *records, size_t num_records,
-                          void *results) = 0;
-};
-
-// -- IDNode --
-
-class IDNode : public ExpressionNode {
- public:
-  ~IDNode() {}
-
-  static grn_rc open(ExpressionNode **node) {
-    ExpressionNode *new_node = new (std::nothrow) IDNode;
-    if (!new_node) {
-      return GRN_NO_MEMORY_AVAILABLE;
-    }
-    *node = new_node;
-    return GRN_SUCCESS;
-  }
-
-  ExpressionNodeType type() const {
-    return GRN_TS_ID_NODE;
-  }
-  DataKind data_kind() const {
-    return GRN_TS_INT;
-  }
-  DataType data_type() const {
-    return GRN_DB_UINT32;
-  }
-  grn_obj *ref_table() const {
-    return NULL;
-  }
-  int dimension() const {
-    return 0;
-  }
-
-  grn_rc evaluate(const Record *records, size_t num_records, void *results) {
-    for (size_t i = 0; i < num_records; ++i) {
-      static_cast<grn_ts_int *>(results)[i] = records[i].id;
-    }
-    return GRN_SUCCESS;
-  }
-
- private:
-  IDNode() : ExpressionNode() {}
-};
-
-// -- ScoreNode --
-
-class ScoreNode : public ExpressionNode {
- public:
-  ~ScoreNode() {}
-
-  static grn_rc open(ExpressionNode **node) {
-    ExpressionNode *new_node = new (std::nothrow) ScoreNode;
-    if (!new_node) {
-      return GRN_NO_MEMORY_AVAILABLE;
-    }
-    *node = new_node;
-    return GRN_SUCCESS;
-  }
-
-  ExpressionNodeType type() const {
-    return GRN_TS_SCORE_NODE;
-  }
-  DataKind data_kind() const {
-    return GRN_TS_FLOAT;
-  }
-  DataType data_type() const {
-    return GRN_DB_FLOAT;
-  }
-  grn_obj *ref_table() const {
-    return NULL;
-  }
-  int dimension() const {
-    return 0;
-  }
-
-  grn_rc adjust(Record *records, size_t num_records) {
-    return GRN_SUCCESS;
-  }
-  grn_rc evaluate(
-    const Record *records, size_t num_records, void *results) {
-    for (size_t i = 0; i < num_records; ++i) {
-      static_cast<grn_ts_float *>(results)[i] = records[i].score;
-    }
-    return GRN_SUCCESS;
-  }
-
- private:
-  ScoreNode() : ExpressionNode() {}
-};
-
-// -- ConstantNode --
-
-class ConstantNode : public ExpressionNode {
- public:
-  ~ConstantNode() {
-    if (obj_) {
-      grn_obj_unlink(ctx_, obj_);
-    }
-    if (buf_) {
-      grn_obj_unlink(ctx_, buf_);
-    }
-  }
-
-  static grn_rc open(grn_ctx *ctx, grn_obj *obj, ExpressionNode **node);
-
-  ExpressionNodeType type() const {
-    return GRN_TS_CONSTANT_NODE;
-  }
-  DataKind data_kind() const {
-    return data_kind_;
-  }
-  DataType data_type() const {
-    return data_type_;
-  }
-  grn_obj *ref_table() const {
-    return NULL;
-  }
-  int dimension() const {
-    return dimension_;
-  }
-
-  grn_rc filter(Record *input, size_t input_size,
-                Record *output, size_t *output_size);
-  grn_rc adjust(Record *records, size_t num_records);
-  grn_rc evaluate(const Record *records, size_t num_records, void *results);
-
- private:
-  grn_ctx *ctx_;
-  grn_obj *obj_;
-  grn_obj *buf_;
-  DataKind data_kind_;
-  DataType data_type_;
-  int dimension_;
-
-  static grn_rc convert(grn_ctx *ctx, grn_obj *obj,
-                        grn_obj **new_obj, grn_obj **buf);
-  static grn_rc convert_bulk(grn_ctx *ctx, grn_obj *obj,
-                             grn_obj **new_obj, grn_obj **buf);
-  static grn_rc convert_uvector(grn_ctx *ctx, grn_obj *obj,
-                                grn_obj **new_obj, grn_obj **buf);
-  static grn_rc convert_vector(grn_ctx *ctx, grn_obj *obj,
-                               grn_obj **new_obj, grn_obj **buf);
-
-  ConstantNode(grn_ctx *ctx, grn_obj *obj, grn_obj *buf,
-               DataType data_type, int dimension)
-    : ExpressionNode(),
-      ctx_(ctx),
-      obj_(obj),
-      buf_(buf),
-      data_kind_(grn_ts_data_type_to_kind(data_type)),
-      data_type_(data_type),
-      dimension_(dimension) {}
-};
-
-grn_rc ConstantNode::open(grn_ctx *ctx, grn_obj *obj, ExpressionNode **node) {
-  grn_obj *new_obj;
-  grn_obj *buf;
-  grn_rc rc = convert(ctx, obj, &new_obj, &buf);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  DataType data_type = obj->header.domain;
-  int dimension = obj->header.type != GRN_BULK;
-  ConstantNode *new_node = new (std::nothrow) ConstantNode(
-    ctx, new_obj ? new_obj : obj, buf, data_type, dimension);
-  if (!new_node) {
-    if (new_obj) {
-      grn_obj_close(ctx, new_obj);
-    }
-    if (buf) {
-      grn_obj_close(ctx, buf);
-    }
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  if (new_obj) {
-    grn_obj_unlink(ctx, obj);
-  }
-  *node = new_node;
-  return GRN_SUCCESS;
-}
-
-grn_rc ConstantNode::filter(Record *input, size_t input_size,
-                            Record *output, size_t *output_size) {
-  if ((dimension() != 0) || (data_kind() != GRN_TS_BOOL)) {
-    return GRN_OPERATION_NOT_PERMITTED;
-  }
-  grn_ts_bool value = GRN_BOOL_VALUE(obj_);
-  if (value) {
-    // If the I/O areas are the same, there is no need to copy records.
-    if (input != output) {
-      for (size_t i = 0; i < input_size; ++i) {
-        output[i] = input[i];
-      }
-    }
-    *output_size = input_size;
-  } else {
-    *output_size = 0;
-  }
-  return GRN_SUCCESS;
-}
-
-grn_rc ConstantNode::adjust(Record *records, size_t num_records) {
-  if ((dimension() != 0) || (data_kind() != GRN_TS_FLOAT)) {
-    return GRN_OPERATION_NOT_PERMITTED;
-  }
-  grn_ts_float value = GRN_FLOAT_VALUE(obj_);
-  for (size_t i = 0; i < num_records; ++i) {
-    records[i].score = value;
-  }
-  return GRN_SUCCESS;
-}
-
-grn_rc ConstantNode::evaluate(const Record *records, size_t num_records,
-                              void *results) {
-  if (num_records == 0) {
-    return GRN_SUCCESS;
-  }
-  if (dimension() == 0) {
-    // Scalar types.
-    switch (data_kind()) {
-      case GRN_TS_BOOL: {
-        grn_ts_bool value = GRN_BOOL_VALUE(obj_);
-        for (size_t i = 0; i < num_records; ++i) {
-          static_cast<grn_ts_bool *>(results)[i] = value;
-        }
-        break;
-      }
-      case GRN_TS_INT:
-      case GRN_TS_FLOAT:
-      case GRN_TS_TIME:
-      case GRN_TS_GEO_POINT: {
-        const void *head = GRN_BULK_HEAD(obj_);
-        char *ptr = static_cast<char *>(results);
-        for (size_t i = 0; i < num_records; ++i) {
-          std::memcpy(ptr, head, 8);
-          ptr += 8;
-        }
-        break;
-      }
-      case GRN_TS_TEXT: {
-        grn_ts_text value;
-        value.ptr = GRN_BULK_HEAD(obj_);
-        value.size = GRN_BULK_VSIZE(obj_);
-        for (size_t i = 0; i < num_records; ++i) {
-          static_cast<grn_ts_text *>(results)[i] = value;
-        }
-        break;
-      }
-      default: {
-        return GRN_UNKNOWN_ERROR;
-      }
-    }
-  } else {
-    // Vector types.
-    switch (data_kind()) {
-      case GRN_TS_BOOL:
-      case GRN_TS_INT:
-      case GRN_TS_FLOAT:
-      case GRN_TS_TIME:
-      case GRN_TS_GEO_POINT: {
-        grn_ts_vector value;
-        value.ptr = GRN_BULK_HEAD(obj_);
-        value.size = grn_vector_size(ctx_, obj_);
-        for (size_t i = 0; i < num_records; ++i) {
-          static_cast<grn_ts_vector *>(results)[i] = value;
-        }
-        break;
-      }
-      case GRN_TS_TEXT: {
-        grn_ts_vector value;
-        value.ptr = GRN_BULK_HEAD(obj_);
-        value.size = GRN_BULK_VSIZE(obj_) / sizeof(grn_ts_text);
-        for (size_t i = 0; i < num_records; ++i) {
-          static_cast<grn_ts_vector *>(results)[i] = value;
-        }
-        break;
-      }
-      default: {
-        return GRN_UNKNOWN_ERROR;
-      }
-    }
-  }
-  return GRN_SUCCESS;
-}
-
-grn_rc ConstantNode::convert(grn_ctx *ctx, grn_obj *obj,
-                             grn_obj **new_obj, grn_obj **buf) {
-  *new_obj = NULL;
-  *buf = NULL;
-  switch (obj->header.type) {
-    case GRN_BULK: {
-      return convert_bulk(ctx, obj, new_obj, buf);
-    }
-    case GRN_UVECTOR: {
-      return convert_uvector(ctx, obj, new_obj, buf);
-    }
-    case GRN_VECTOR: {
-      return convert_vector(ctx, obj, new_obj, buf);
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-}
-
-#define GRN_TS_CONVERT_BULK_INT_CASE_BLOCK(type)\
-  case GRN_DB_ ## type: {\
-    GRN_INT64_SET(ctx, *new_obj, GRN_ ## type ## _VALUE(obj));\
-    break;\
-  }
-grn_rc ConstantNode::convert_bulk(grn_ctx *ctx, grn_obj *obj,
-                                  grn_obj **new_obj, grn_obj **buf) {
-  switch (obj->header.domain) {
-    case GRN_DB_BOOL:
-    case GRN_DB_INT64:
-    case GRN_DB_FLOAT:
-    case GRN_DB_TIME:
-    case GRN_DB_SHORT_TEXT:
-    case GRN_DB_TEXT:
-    case GRN_DB_LONG_TEXT:
-    case GRN_DB_TOKYO_GEO_POINT:
-    case GRN_DB_WGS84_GEO_POINT: {
-      return GRN_SUCCESS;
-    }
-    case GRN_DB_INT8:
-    case GRN_DB_INT16:
-    case GRN_DB_INT32:
-    case GRN_DB_UINT8:
-    case GRN_DB_UINT16:
-    case GRN_DB_UINT32:
-    case GRN_DB_UINT64: {
-      *new_obj = grn_obj_open(ctx, GRN_BULK, 0, GRN_DB_INT64);
-      if (!*new_obj) {
-        return GRN_NO_MEMORY_AVAILABLE;
-      }
-      switch (obj->header.domain) {
-        GRN_TS_CONVERT_BULK_INT_CASE_BLOCK(INT8)
-        GRN_TS_CONVERT_BULK_INT_CASE_BLOCK(INT16)
-        GRN_TS_CONVERT_BULK_INT_CASE_BLOCK(INT32)
-        GRN_TS_CONVERT_BULK_INT_CASE_BLOCK(UINT8)
-        GRN_TS_CONVERT_BULK_INT_CASE_BLOCK(UINT16)
-        GRN_TS_CONVERT_BULK_INT_CASE_BLOCK(UINT32)
-        GRN_TS_CONVERT_BULK_INT_CASE_BLOCK(UINT64)
-        default: {
-          return GRN_UNKNOWN_ERROR;
-        }
-      }
-      return GRN_SUCCESS;
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-}
-#undef GRN_TS_CONVERT_BULK_INT_CASE_BLOCK
-
-#define GRN_TS_CONVERT_UVECTOR_INT_CASE_BLOCK(type)\
-  case GRN_DB_ ## type: {\
-    for (size_t i = 0; i < size; ++i) {\
-      GRN_INT64_SET_AT(ctx, *new_obj, i, GRN_ ## type ## _VALUE_AT(obj, i));\
-    }\
-    break;\
-  }
-grn_rc ConstantNode::convert_uvector(grn_ctx *ctx, grn_obj *obj,
-                                     grn_obj **new_obj, grn_obj **buf) {
-  switch (obj->header.domain) {
-    case GRN_DB_BOOL:
-    case GRN_DB_INT64:
-    case GRN_DB_FLOAT:
-    case GRN_DB_TIME:
-    case GRN_DB_TOKYO_GEO_POINT:
-    case GRN_DB_WGS84_GEO_POINT: {
-      return GRN_SUCCESS;
-    }
-    case GRN_DB_INT8:
-    case GRN_DB_INT16:
-    case GRN_DB_INT32:
-    case GRN_DB_UINT8:
-    case GRN_DB_UINT16:
-    case GRN_DB_UINT32:
-    case GRN_DB_UINT64: {
-      *new_obj = grn_obj_open(ctx, GRN_UVECTOR, 0, GRN_DB_INT64);
-      if (!new_obj) {
-        return GRN_NO_MEMORY_AVAILABLE;
-      }
-      size_t size = grn_vector_size(ctx, obj);
-      switch (obj->header.domain) {
-        GRN_TS_CONVERT_UVECTOR_INT_CASE_BLOCK(INT8)
-        GRN_TS_CONVERT_UVECTOR_INT_CASE_BLOCK(INT16)
-        GRN_TS_CONVERT_UVECTOR_INT_CASE_BLOCK(INT32)
-        GRN_TS_CONVERT_UVECTOR_INT_CASE_BLOCK(UINT8)
-        GRN_TS_CONVERT_UVECTOR_INT_CASE_BLOCK(UINT16)
-        GRN_TS_CONVERT_UVECTOR_INT_CASE_BLOCK(UINT32)
-        GRN_TS_CONVERT_UVECTOR_INT_CASE_BLOCK(UINT64)
-        default: {
-          return GRN_UNKNOWN_ERROR;
-        }
-      }
-      return GRN_SUCCESS;
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-}
-#undef GRN_TS_CONVERT_UVECTOR_INT_CASE_BLOCK
-
-grn_rc ConstantNode::convert_vector(grn_ctx *ctx, grn_obj *obj,
-                                    grn_obj **new_obj, grn_obj **buf) {
-  switch (obj->header.domain) {
-    case GRN_DB_SHORT_TEXT:
-    case GRN_DB_TEXT:
-    case GRN_DB_LONG_TEXT: {
-      break;
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-  grn_rc rc = GRN_SUCCESS;
-  *new_obj = grn_obj_open(ctx, GRN_BULK, 0, GRN_DB_TEXT);
-  if (!*new_obj) {
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  size_t size = grn_vector_size(ctx, obj);
-  rc = grn_bulk_resize(ctx, *new_obj, sizeof(grn_ts_text) * size);
-  if (rc == GRN_SUCCESS) {
-    *buf = grn_obj_open(ctx, GRN_BULK, 0, GRN_DB_TEXT);
-    if (!*buf) {
-      grn_obj_close(ctx, *new_obj);
-      return GRN_NO_MEMORY_AVAILABLE;
-    }
-    grn_ts_text *values =
-      reinterpret_cast<grn_ts_text *>(GRN_BULK_HEAD(*new_obj));
-    for (size_t i = 0; i < size; ++i) {
-      const char *ptr;
-      values[i].size = grn_vector_get_element(ctx, obj, i, &ptr, NULL, NULL);
-      GRN_TEXT_PUT(ctx, *buf, ptr, values[i].size);
-      if (rc != GRN_SUCCESS) {
-        break;
-      }
-    }
-    if (rc == GRN_SUCCESS) {
-      const char *ptr = GRN_BULK_HEAD(*buf);
-      for (size_t i = 0; i < size; ++i) {
-        values[i].ptr = ptr;
-        ptr += values[i].size;
-      }
-    }
-  }
-  if (rc != GRN_SUCCESS) {
-    if (*new_obj) {
-      grn_obj_close(ctx, *new_obj);
-    }
-    if (*buf) {
-      grn_obj_close(ctx, *buf);
-    }
-  }
-  return GRN_SUCCESS;
-}
-
-// -- ColumnNode --
-
-class ColumnNode : public ExpressionNode {
- public:
-  ~ColumnNode() {
-    if (column_) {
-      grn_obj_unlink(ctx_, column_);
-    }
-    if (ref_table_) {
-      grn_obj_unlink(ctx_, ref_table_);
-    }
-    if (buf_) {
-      grn_obj_close(ctx_, buf_);
-    }
-    if (deep_buf_) {
-      grn_obj_close(ctx_, deep_buf_);
-    }
-  }
-
-  static grn_rc open(grn_ctx *ctx, grn_obj *column, ExpressionNode **node);
-
-  ExpressionNodeType type() const {
-    return GRN_TS_COLUMN_NODE;
-  }
-  DataKind data_kind() const {
-    return data_kind_;
-  }
-  DataType data_type() const {
-    return data_type_;
-  }
-  grn_obj *ref_table() const {
-    return NULL;
-  }
-  int dimension() const {
-    return dimension_;
-  }
-
-  grn_rc filter(Record *input, size_t input_size,
-                Record *output, size_t *output_size);
-  grn_rc adjust(Record *records, size_t num_records);
-  grn_rc evaluate(const Record *records, size_t num_records, void *results);
-
- private:
-  grn_ctx *ctx_;
-  grn_obj *column_;
-  grn_obj *buf_;
-  grn_obj *deep_buf_;
-  DataKind data_kind_;
-  DataType data_type_;
-  grn_obj *ref_table_;
-  int dimension_;
-
-  ColumnNode(grn_ctx *ctx, grn_obj *column, DataType data_type,
-             grn_obj *ref_table, int dimension)
-    : ExpressionNode(),
-      ctx_(ctx),
-      column_(column),
-      buf_(NULL),
-      deep_buf_(NULL),
-      data_kind_(grn_ts_data_type_to_kind(data_type)),
-      data_type_(data_type),
-      ref_table_(ref_table),
-      dimension_(dimension) {}
-
-  grn_rc evaluate_scalar(const Record *records, size_t num_records,
-                         void *results);
-  grn_rc evaluate_scalar_text(const Record *records, size_t num_records,
-                              void *results);
-  grn_rc evaluate_vector(const Record *records, size_t num_records,
-                         void *results);
-  grn_rc evaluate_vector_int(const Record *records, size_t num_records,
-                             void *results);
-  grn_rc evaluate_vector_text(const Record *records, size_t num_records,
-                              void *results);
-};
-
-grn_rc ColumnNode::open(grn_ctx *ctx, grn_obj *column, ExpressionNode **node) {
-  DataType data_type = GRN_DB_VOID;
-  grn_obj *ref_table = NULL;
-  int dimension = 0;
-  switch (column->header.type) {
-    case GRN_COLUMN_FIX_SIZE:
-    case GRN_COLUMN_VAR_SIZE: {
-      data_type = DB_OBJ(column)->range;
-      if (column->header.type == GRN_COLUMN_VAR_SIZE) {
-        grn_obj_flags column_type =
-          column->header.flags & GRN_OBJ_COLUMN_TYPE_MASK;
-        if (column_type == GRN_OBJ_COLUMN_VECTOR) {
-          dimension = 1;
-        }
-      }
-      break;
-    }
-    case GRN_ACCESSOR: {
-      grn_accessor *accessor = (grn_accessor *)column;
-      switch (accessor->action) {
-        case GRN_ACCESSOR_GET_ID: {
-          return IDNode::open(node);
-        }
-        case GRN_ACCESSOR_GET_KEY: {
-          data_type = accessor->obj->header.domain;
-          break;
-        }
-        case GRN_ACCESSOR_GET_VALUE: {
-          data_type = DB_OBJ(accessor->obj)->range;
-          break;
-        }
-        case GRN_ACCESSOR_GET_SCORE: {
-          return IDNode::open(node);
-        }
-        default: {
-          return GRN_INVALID_ARGUMENT;
-        }
-      }
-      break;
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-  if (data_type > GRN_TS_MAX_DATA_TYPE) {
-    ref_table = grn_ctx_at(ctx, column->header.domain);
-    if (!ref_table) {
-      if (ctx->rc != GRN_SUCCESS) {
-        return ctx->rc;
-      }
-      return GRN_UNKNOWN_ERROR;
-    } else if (!grn_obj_is_table(ctx, ref_table)) {
-      grn_obj_unlink(ctx, ref_table);
-      return GRN_UNKNOWN_ERROR;
-    }
-  }
-  ColumnNode *new_node = new (std::nothrow) ColumnNode(
-    ctx, column, data_type, ref_table, dimension);
-  if (!new_node) {
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  *node = new_node;
-  return GRN_SUCCESS;
-}
-
-grn_rc ColumnNode::filter(Record *input, size_t input_size,
-                          Record *output, size_t *output_size) {
-  if ((dimension() != 0) || (data_kind() != GRN_TS_BOOL)) {
-    return GRN_OPERATION_NOT_PERMITTED;
-  }
-  grn_obj value;
-  GRN_BOOL_INIT(&value, 0);
-  size_t count = 0;
-  for (size_t i = 0; i < input_size; ++i) {
-    GRN_BULK_REWIND(&value);
-    grn_obj_get_value(ctx_, column_, input[i].id, &value);
-    if (ctx_->rc != GRN_SUCCESS) {
-      return ctx_->rc;
-    }
-    if (GRN_BOOL_VALUE(&value)) {
-      output[count] = input[i];
-      ++count;
-    }
-  }
-  GRN_OBJ_FIN(ctx_, &value);
-  *output_size = count;
-  return GRN_SUCCESS;
-}
-
-grn_rc ColumnNode::adjust(Record *records, size_t num_records) {
-  if ((dimension() != 0) || (data_kind() != GRN_TS_FLOAT)) {
-    return GRN_OPERATION_NOT_PERMITTED;
-  }
-  grn_obj value;
-  GRN_FLOAT_INIT(&value, 0);
-  for (size_t i = 0; i < num_records; ++i) {
-    GRN_BULK_REWIND(&value);
-    grn_obj_get_value(ctx_, column_, records[i].id, &value);
-    records[i].score = GRN_FLOAT_VALUE(&value);
-  }
-  GRN_OBJ_FIN(ctx_, &value);
-  return GRN_SUCCESS;
-}
-
-grn_rc ColumnNode::evaluate(const Record *records, size_t num_records,
-                            void *results) {
-  if (num_records == 0) {
-    return GRN_SUCCESS;
-  }
-  if (dimension() == 0) {
-    return evaluate_scalar(records, num_records, results);
-  } else {
-    return evaluate_vector(records, num_records, results);
-  }
-}
-
-#define GRN_TS_EVALUATE_SCALAR_CASE_BLOCK(type, ts_type)\
-  case GRN_DB_ ## type: {\
-    GRN_ ## type ## _INIT(&value, 0);\
-    for (size_t i = 0; i < num_records; ++i) {\
-      GRN_BULK_REWIND(&value);\
-      grn_obj_get_value(ctx_, column_, records[i].id, &value);\
-      if (ctx_->rc != GRN_SUCCESS) {\
-        break;\
-      }\
-      static_cast<grn_ts_ ## ts_type *>(results)[i] =\
-        GRN_ ## type ## _VALUE(&value);\
-    }\
-    break;\
-  }
-grn_rc ColumnNode::evaluate_scalar(const Record *records, size_t num_records,
-                                   void *results) {
-  grn_obj value;
-  switch (data_type()) {
-    GRN_TS_EVALUATE_SCALAR_CASE_BLOCK(BOOL, bool)
-    GRN_TS_EVALUATE_SCALAR_CASE_BLOCK(INT8, int)
-    GRN_TS_EVALUATE_SCALAR_CASE_BLOCK(INT16, int)
-    GRN_TS_EVALUATE_SCALAR_CASE_BLOCK(INT32, int)
-    GRN_TS_EVALUATE_SCALAR_CASE_BLOCK(INT64, int)
-    GRN_TS_EVALUATE_SCALAR_CASE_BLOCK(UINT8, int)
-    GRN_TS_EVALUATE_SCALAR_CASE_BLOCK(UINT16, int)
-    GRN_TS_EVALUATE_SCALAR_CASE_BLOCK(UINT32, int)
-    GRN_TS_EVALUATE_SCALAR_CASE_BLOCK(UINT64, int)
-    GRN_TS_EVALUATE_SCALAR_CASE_BLOCK(FLOAT, float)
-    GRN_TS_EVALUATE_SCALAR_CASE_BLOCK(TIME, time)
-    case GRN_DB_SHORT_TEXT:
-    case GRN_DB_TEXT:
-    case GRN_DB_LONG_TEXT: {
-      return evaluate_scalar_text(records, num_records, results);
-    }
-    case GRN_DB_TOKYO_GEO_POINT:
-    case GRN_DB_WGS84_GEO_POINT: {
-      if (data_type() == GRN_DB_TOKYO_GEO_POINT) {
-        GRN_TOKYO_GEO_POINT_INIT(&value, 0);
-      } else {
-        GRN_WGS84_GEO_POINT_INIT(&value, 0);
-      }
-      for (size_t i = 0; i < num_records; ++i) {
-        GRN_BULK_REWIND(&value);
-        grn_obj_get_value(ctx_, column_, records[i].id, &value);
-        if (ctx_->rc != GRN_SUCCESS) {
-          break;
-        }
-        grn_ts_geo_point *ptr = &static_cast<grn_ts_geo_point *>(results)[i];
-        GRN_GEO_POINT_VALUE(&value, ptr->latitude, ptr->longitude);
-      }
-      break;
-    }
-    default: {
-      return GRN_UNKNOWN_ERROR;
-    }
-  }
-  GRN_OBJ_FIN(ctx_, &value);
-  if (ctx_->rc != GRN_SUCCESS) {
-    return ctx_->rc;
-  }
-  return GRN_SUCCESS;
-}
-#undef GRN_TS_EVALUATE_CASE_BLOCK
-
-grn_rc ColumnNode::evaluate_scalar_text(const Record *records,
-                                        size_t num_records, void *results) {
-  if (!buf_) {
-    buf_ = grn_obj_open(ctx_, GRN_BULK, 0, GRN_DB_TEXT);
-    if (!buf_) {
-      if (ctx_->rc != GRN_SUCCESS) {
-        return ctx_->rc;
-      }
-      return GRN_UNKNOWN_ERROR;
-    }
-  }
-  GRN_BULK_REWIND(buf_);
-  grn_ts_text *values = static_cast<grn_ts_text *>(results);
-  size_t offset = 0;
-  for (size_t i = 0; i < num_records; ++i) {
-    grn_obj_get_value(ctx_, column_, records[i].id, buf_);
-    if (ctx_->rc != GRN_SUCCESS) {
-      return ctx_->rc;
-    }
-    size_t size = GRN_TEXT_LEN(buf_);
-    values[i].size = size - offset;
-    offset = size;
-  }
-  const char *ptr = GRN_TEXT_VALUE(buf_);
-  for (size_t i = 0; i < num_records; ++i) {
-    values[i].ptr = ptr;
-    ptr += values[i].size;
-  }
-  return GRN_SUCCESS;
-}
-
-#define GRN_TS_EVALUATE_VECTOR_CASE_BLOCK(type, ts_type)\
-  case GRN_DB_ ## type: {\
-    if (!buf_) {\
-      buf_ = grn_obj_open(ctx_, GRN_UVECTOR, 0, GRN_DB_ ## type);\
-      if (!buf_) {\
-        if (ctx_->rc != GRN_SUCCESS) {\
-          return ctx_->rc;\
-        }\
-        return GRN_UNKNOWN_ERROR;\
-      }\
-    }\
-    GRN_BULK_REWIND(buf_);\
-    grn_ts_vector *vectors = static_cast<grn_ts_vector *>(results);\
-    size_t offset = 0;\
-    for (size_t i = 0; i < num_records; i++) {\
-      grn_obj_get_value(ctx_, column_, records[i].id, buf_);\
-      if (ctx_->rc != GRN_SUCCESS) {\
-        return ctx_->rc;\
-      }\
-      size_t size = grn_vector_size(ctx_, buf_);\
-      vectors[i].size = size - offset;\
-      offset = size;\
-    }\
-    grn_ts_ ## ts_type *ptr =\
-      reinterpret_cast<grn_ts_ ## ts_type *>(GRN_BULK_HEAD(buf_));\
-    for (size_t i = 0; i < num_records; i++) {\
-      vectors[i].ptr = ptr;\
-      ptr += vectors[i].size;\
-    }\
-    return GRN_SUCCESS;\
-  }
-grn_rc ColumnNode::evaluate_vector(const Record *records, size_t num_records,
-                                   void *results) {
-  switch (data_type()) {
-    case GRN_DB_INT8:
-    case GRN_DB_INT16:
-    case GRN_DB_INT32:
-    case GRN_DB_UINT8:
-    case GRN_DB_UINT16:
-    case GRN_DB_UINT32: {
-      return evaluate_vector_int(records, num_records, results);
-    }
-    GRN_TS_EVALUATE_VECTOR_CASE_BLOCK(BOOL, bool)
-    GRN_TS_EVALUATE_VECTOR_CASE_BLOCK(INT64, int)
-    GRN_TS_EVALUATE_VECTOR_CASE_BLOCK(UINT64, int)
-    GRN_TS_EVALUATE_VECTOR_CASE_BLOCK(FLOAT, float)
-    GRN_TS_EVALUATE_VECTOR_CASE_BLOCK(TIME, float)
-    case GRN_DB_SHORT_TEXT:
-    case GRN_DB_TEXT:
-    case GRN_DB_LONG_TEXT: {
-      return evaluate_vector_text(records, num_records, results);
-    }
-    case GRN_DB_TOKYO_GEO_POINT:
-    case GRN_DB_WGS84_GEO_POINT: {
-      if (!buf_) {
-        buf_ = grn_obj_open(ctx_, GRN_UVECTOR, 0, data_type());
-        if (!buf_) {
-          if (ctx_->rc != GRN_SUCCESS) {
-            return ctx_->rc;
-          }
-          return GRN_UNKNOWN_ERROR;
-        }
-      }
-      GRN_BULK_REWIND(buf_);
-      grn_ts_vector *vectors = static_cast<grn_ts_vector *>(results);
-      size_t offset = 0;
-      for (size_t i = 0; i < num_records; i++) {
-        grn_obj_get_value(ctx_, column_, records[i].id, buf_);
-        if (ctx_->rc != GRN_SUCCESS) {
-          return ctx_->rc;
-        }
-        size_t size = grn_vector_size(ctx_, buf_);
-        vectors[i].size = size - offset;
-        offset = size;
-      }
-      grn_ts_geo_point *ptr =
-        reinterpret_cast<grn_ts_geo_point *>(GRN_BULK_HEAD(buf_));
-      for (size_t i = 0; i < num_records; i++) {
-        vectors[i].ptr = ptr;
-        ptr += vectors[i].size;
-      }
-      return GRN_SUCCESS;
-    }
-    default: {
-      return GRN_UNKNOWN_ERROR;
-    }
-  }
-}
-
-#define GRN_TS_EVALUATE_VECTOR_INT_CASE_BLOCK(type)\
-  case GRN_DB_ ## type: {\
-    if (!deep_buf_) {\
-      deep_buf_ = grn_obj_open(ctx_, GRN_UVECTOR, 0, GRN_DB_ ## type);\
-      if (!deep_buf_) {\
-        if (ctx_->rc != GRN_SUCCESS) {\
-          return ctx_->rc;\
-        }\
-        return GRN_UNKNOWN_ERROR;\
-      }\
-    }\
-    GRN_BULK_REWIND(deep_buf_);\
-    grn_ts_vector *vectors = static_cast<grn_ts_vector *>(results);\
-    size_t offset = 0;\
-    for (size_t i = 0; i < num_records; i++) {\
-      grn_obj_get_value(ctx_, column_, records[i].id, deep_buf_);\
-      if (ctx_->rc != GRN_SUCCESS) {\
-        return ctx_->rc;\
-      }\
-      size_t size = grn_vector_size(ctx_, deep_buf_);\
-      vectors[i].size = size - offset;\
-      offset = size;\
-    }\
-    grn_rc rc = grn_bulk_resize(ctx_, buf_, sizeof(grn_ts_int) * offset);\
-    if (rc != GRN_SUCCESS) {\
-      return rc;\
-    }\
-    grn_ts_int *ptr = reinterpret_cast<grn_ts_int *>(GRN_BULK_HEAD(buf_));\
-    for (size_t i = 0; i < offset; ++i) {\
-      ptr[i] = GRN_ ## type ## _VALUE_AT(deep_buf_, i);\
-    }\
-    for (size_t i = 0; i < num_records; i++) {\
-      vectors[i].ptr = ptr;\
-      ptr += vectors[i].size;\
-    }\
-    return GRN_SUCCESS;\
-  }
-grn_rc ColumnNode::evaluate_vector_int(const Record *records,
-                                       size_t num_records, void *results) {
-  if (!buf_) {
-    buf_ = grn_obj_open(ctx_, GRN_UVECTOR, 0, GRN_DB_INT64);
-    if (!buf_) {
-      if (ctx_->rc != GRN_SUCCESS) {
-        return ctx_->rc;
-      }
-      return GRN_UNKNOWN_ERROR;
-    }
-  }
-  GRN_BULK_REWIND(buf_);
-  switch (data_type()) {
-    GRN_TS_EVALUATE_VECTOR_INT_CASE_BLOCK(INT8)
-    GRN_TS_EVALUATE_VECTOR_INT_CASE_BLOCK(INT16)
-    GRN_TS_EVALUATE_VECTOR_INT_CASE_BLOCK(INT32)
-    GRN_TS_EVALUATE_VECTOR_INT_CASE_BLOCK(UINT8)
-    GRN_TS_EVALUATE_VECTOR_INT_CASE_BLOCK(UINT16)
-    GRN_TS_EVALUATE_VECTOR_INT_CASE_BLOCK(UINT32)
-    default: {
-      return GRN_UNKNOWN_ERROR;
-    }
-  }
-}
-#undef GRN_TS_EVALUATE_VECTOR_INT_CASE_BLOCK
-
-grn_rc ColumnNode::evaluate_vector_text(const Record *records,
-                                        size_t num_records, void *results) {
-  if (!deep_buf_) {
-    deep_buf_ = grn_obj_open(ctx_, GRN_VECTOR, 0, GRN_DB_TEXT);
-    if (!deep_buf_) {
-      if (ctx_->rc != GRN_SUCCESS) {
-        return ctx_->rc;
-      }
-      return GRN_UNKNOWN_ERROR;
-    }
-  }
-  GRN_BULK_REWIND(deep_buf_);
-  grn_ts_vector *vectors = static_cast<grn_ts_vector *>(results);
-  size_t offset = 0;
-  for (size_t i = 0; i < num_records; ++i) {
-    grn_obj_get_value(ctx_, column_, records[i].id, deep_buf_);
-    if (ctx_->rc != GRN_SUCCESS) {
-      return ctx_->rc;
-    }
-    size_t size = grn_vector_size(ctx_, deep_buf_);
-    vectors[i].size = size - offset;
-    offset = size;
-  }
-  if (!buf_) {
-    buf_ = grn_obj_open(ctx_, GRN_BULK, 0, GRN_DB_TEXT);
-    if (!buf_) {
-      if (ctx_->rc != GRN_SUCCESS) {
-        return ctx_->rc;
-      }
-      return GRN_UNKNOWN_ERROR;
-    }
-  }
-  grn_rc rc = grn_bulk_resize(ctx_, buf_, sizeof(grn_ts_text) * offset);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  grn_ts_text *texts = reinterpret_cast<grn_ts_text *>(GRN_BULK_HEAD(buf_));
-  offset = 0;
-  for (size_t i = 0; i < num_records; ++i) {
-    for (size_t j = 0; j < vectors[i].size; ++j) {
-      texts[offset + j].size = grn_vector_get_element(
-        ctx_, deep_buf_, offset + j, &texts[offset + j].ptr, NULL, NULL);
-    }
-    vectors[i].ptr = &texts[offset];
-    offset += vectors[i].size;
-  }
-  return GRN_SUCCESS;
-}
-
-// -- OperatorNode --
-
-class OperatorNode : public ExpressionNode {
- public:
-  OperatorNode() : ExpressionNode() {}
-  virtual ~OperatorNode() {}
-
-  ExpressionNodeType type() const {
-    return GRN_TS_OPERATOR_NODE;
-  }
-};
-
-template <DataKind KIND> struct KindTraits;
-
-template <> struct KindTraits<GRN_TS_BOOL> {
-  typedef grn_ts_bool Type;
-
-  static grn_ts_bool equal(Type lhs, Type rhs) {
-    return lhs == rhs;
-  }
-  static grn_ts_bool not_equal(Type lhs, Type rhs) {
-    return lhs != rhs;
-  }
-};
-
-template <> struct KindTraits<GRN_TS_INT> {
-  typedef grn_ts_int Type;
-
-  static grn_ts_bool equal(Type lhs, Type rhs) {
-    return lhs == rhs;
-  }
-  static grn_ts_bool not_equal(Type lhs, Type rhs) {
-    return lhs != rhs;
-  }
-};
-
-template <> struct KindTraits<GRN_TS_FLOAT> {
-  typedef grn_ts_float Type;
-
-  static grn_ts_bool equal(Type lhs, Type rhs) {
-    return (lhs <= rhs) && (lhs >= rhs);
-  }
-  static grn_ts_bool not_equal(Type lhs, Type rhs) {
-    return (lhs < rhs) || (lhs > rhs);
-  }
-};
-
-template <> struct KindTraits<GRN_TS_TIME> {
-  typedef grn_ts_time Type;
-
-  static grn_ts_bool equal(Type lhs, Type rhs) {
-    return lhs == rhs;
-  }
-  static grn_ts_bool not_equal(Type lhs, Type rhs) {
-    return lhs != rhs;
-  }
-};
-
-template <> struct KindTraits<GRN_TS_TEXT> {
-  typedef grn_ts_text Type;
-
-  static grn_ts_bool equal(Type lhs, Type rhs) {
-    return (lhs.size == rhs.size) && !memcmp(lhs.ptr, rhs.ptr, lhs.size);
-  }
-  static grn_ts_bool not_equal(Type lhs, Type rhs) {
-    return (lhs.size != rhs.size) || memcmp(lhs.ptr, rhs.ptr, lhs.size);
-  }
-};
-
-template <> struct KindTraits<GRN_TS_GEO_POINT> {
-  typedef grn_ts_geo_point Type;
-
-  static grn_ts_bool equal(Type lhs, Type rhs) {
-    return (lhs.latitude == rhs.latitude) && (lhs.longitude == rhs.longitude);
-  }
-  static grn_ts_bool not_equal(Type lhs, Type rhs) {
-    return (lhs.latitude != rhs.latitude) || (lhs.longitude != rhs.longitude);
-  }
-};
-
-template <DataKind KIND>
-grn_rc operator_node_evaluate_arg(const Record *records, size_t num_records,
-                                  ExpressionNode *arg,
-                                  std::vector<char> *arg_values) {
-  typedef KindTraits<KIND> Traits;
-
-  size_t old_size = arg_values->size() / sizeof(typename Traits::Type);
-  if (old_size < num_records) try {
-    arg_values->resize(sizeof(typename Traits::Type) * num_records);
-  } catch (const std::bad_alloc &) {
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  return arg->evaluate(records, num_records, &*arg_values->begin());
-}
-
-grn_rc operator_node_fill_arg_values(const Record *records, size_t num_records,
-                                     ExpressionNode *arg,
-                                     std::vector<char> *arg_values) {
-  size_t value_size = 0;
-  switch (arg->data_kind()) {
-    case GRN_TS_BOOL: {
-      value_size = sizeof(grn_ts_bool);
-      break;
-    }
-    case GRN_TS_INT: {
-      value_size = sizeof(grn_ts_int);
-      break;
-    }
-    case GRN_TS_FLOAT: {
-      value_size = sizeof(grn_ts_float);
-      break;
-    }
-    case GRN_TS_TIME: {
-      value_size = sizeof(grn_ts_time);
-      break;
-    }
-    case GRN_TS_TEXT: {
-      value_size = sizeof(grn_ts_text);
-      break;
-    }
-    case GRN_TS_GEO_POINT: {
-      value_size = sizeof(grn_ts_geo_point);
-      break;
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-  size_t old_size = arg_values->size() / value_size;
-  if (old_size < num_records) try {
-    arg_values->resize(value_size * num_records);
-  } catch (const std::bad_alloc &) {
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  return arg->evaluate(records, num_records, &*arg_values->begin());
-}
-
-//// --- UnaryNode ---
-
-//template <typename T, typename U>
-//class UnaryNode : public OperatorNode<T> {
-// public:
-//  explicit UnaryNode(ExpressionNode *arg)
-//    : OperatorNode<T>(), arg_(static_cast<TypedNode<U> *>(arg)),
-//      arg_values_() {}
-//  virtual ~UnaryNode() {
-//    delete arg_;
-//  }
-
-// protected:
-//  TypedNode<U> *arg_;
-//  std::vector<U> arg_values_;
-
-//  grn_rc fill_arg_values(const Record *records, size_t num_records) {
-//    return operator_node_fill_arg_values(
-//      records, num_records, arg_, &arg_values_);
-//  }
-//};
-
-//// --- BinaryNode ---
-
-//template <typename T, typename U, typename V>
-//class BinaryNode : public OperatorNode<T> {
-// public:
-//  BinaryNode(ExpressionNode *arg1, ExpressionNode *arg2)
-//    : OperatorNode<T>(),
-//      arg1_(static_cast<TypedNode<U> *>(arg1)),
-//      arg2_(static_cast<TypedNode<V> *>(arg2)),
-//      arg1_values_(), arg2_values_() {}
-//  virtual ~BinaryNode() {
-//    delete arg1_;
-//    delete arg2_;
-//  }
-
-// protected:
-//  TypedNode<U> *arg1_;
-//  TypedNode<V> *arg2_;
-//  std::vector<U> arg1_values_;
-//  std::vector<V> arg2_values_;
-
-//  grn_rc fill_arg1_values(const Record *records, size_t num_records) {
-//    return operator_node_fill_arg_values(
-//      records, num_records, arg1_, &arg1_values_);
-//  }
-//  grn_rc fill_arg2_values(const Record *records, size_t num_records) {
-//    return operator_node_fill_arg_values(
-//      records, num_records, arg2_, &arg2_values_);
-//  }
-//};
-
-// ---- LogicalNotNode ----
-
-class LogicalNotNode : public OperatorNode {
- public:
-  ~LogicalNotNode() {
-    delete arg_;
-  }
-
-  static grn_rc open(ExpressionNode *arg, ExpressionNode **node) {
-    LogicalNotNode *new_node = new (std::nothrow) LogicalNotNode(arg);
-    if (!new_node) {
-      return GRN_NO_MEMORY_AVAILABLE;
-    }
-    *node = new_node;
-    return GRN_SUCCESS;
-  }
-
-  DataKind data_kind() const {
-    return GRN_TS_BOOL;
-  }
-  DataType data_type() const {
-    return GRN_DB_BOOL;
-  }
-  grn_obj *ref_table() const {
-    return NULL;
-  }
-  int dimension() const {
-    return 0;
-  }
-
-  grn_rc filter(Record *input, size_t input_size,
-                Record *output, size_t *output_size);
-
-  grn_rc evaluate(const Record *records, size_t num_records, void *results);
-
- private:
-  ExpressionNode *arg_;
-  std::vector<Record> temp_records_;
-
-  explicit LogicalNotNode(ExpressionNode *arg)
-    : OperatorNode(), arg_(arg), temp_records_() {}
-};
-
-grn_rc LogicalNotNode::filter(Record *input, size_t input_size,
-                              Record *output, size_t *output_size) {
-  if (temp_records_.size() <= input_size) {
-    try {
-      temp_records_.resize(input_size + 1);
-      temp_records_[input_size].id = GRN_ID_NIL;
-    } catch (const std::bad_alloc &) {
-      return GRN_NO_MEMORY_AVAILABLE;
-    }
-  }
-  size_t temp_size;
-  grn_rc rc =
-    arg_->filter(input, input_size, &*temp_records_.begin(), &temp_size);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  if (temp_size == 0) {
-    if (input != output) {
-      for (size_t i = 0; i < input_size; ++i) {
-        output[i] = input[i];
-      }
-    }
-    *output_size = input_size;
-    return GRN_SUCCESS;
-  } else if (temp_size == input_size) {
-    *output_size = 0;
-    return GRN_SUCCESS;
-  }
-
-  size_t count = 0;
-  for (size_t i = 0; i < input_size; ++i) {
-    if (input[i].id != temp_records_[i - count].id) {
-      output[count] = input[i];
-      ++count;
-    }
-  }
-  *output_size = count;
-  return GRN_SUCCESS;
-}
-
-grn_rc LogicalNotNode::evaluate(const Record *records, size_t num_records,
-                                void *results) {
-  grn_rc rc = arg_->evaluate(records, num_records, results);
-  if (rc == GRN_SUCCESS) {
-    for (size_t i = 0; i < num_records; ++i) {
-      ((grn_ts_bool *)results)[i] = !((grn_ts_bool *)results)[i];
-    }
-  }
-  return rc;
-}
-
-// ---- LogicalAndNode ----
-
-class LogicalAndNode : public OperatorNode {
- public:
-  ~LogicalAndNode() {}
-
-  static grn_rc open(ExpressionNode *arg1, ExpressionNode *arg2,
-                     ExpressionNode **node);
-
-  DataKind data_kind() const {
-    return GRN_TS_BOOL;
-  }
-  DataType data_type() const {
-    return GRN_DB_BOOL;
-  }
-  grn_obj *ref_table() const {
-    return NULL;
-  }
-  int dimension() const {
-    return 0;
-  }
-
-  grn_rc filter(Record *input, size_t input_size,
-                Record *output, size_t *output_size);
-  grn_rc evaluate(const Record *records, size_t num_records, void *results);
-
- private:
-  ExpressionNode *arg1_;
-  ExpressionNode *arg2_;
-  std::vector<grn_ts_bool> arg_values_;
-  std::vector<Record> temp_records_;
-
-  LogicalAndNode(ExpressionNode *arg1, ExpressionNode *arg2)
-    : OperatorNode(), arg1_(arg1), arg2_(arg2), arg_values_(),
-      temp_records_() {}
-};
-
-grn_rc LogicalAndNode::open(ExpressionNode *arg1, ExpressionNode *arg2,
-                            ExpressionNode **node) {
-  LogicalAndNode *new_node = new (std::nothrow) LogicalAndNode(arg1, arg2);
-  if (!new_node) {
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  *node = new_node;
-  return GRN_SUCCESS;
-}
-
-grn_rc LogicalAndNode::filter(Record *input, size_t input_size,
-                              Record *output, size_t *output_size) {
-  grn_rc rc = arg1_->filter(input, input_size, output, output_size);
-  if (rc == GRN_SUCCESS) {
-    rc = arg2_->filter(output, *output_size, output, output_size);
-  }
-  return rc;
-}
-
-grn_rc LogicalAndNode::evaluate(const Record *records, size_t num_records,
-                                void *results) {
-  // Evaluate "arg1" for all the records.
-  // Then, evaluate "arg2" for non-false records.
-  grn_rc rc = arg1_->evaluate(records, num_records, results);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  if (temp_records_.size() < num_records) try {
-    temp_records_.resize(num_records);
-  } catch (const std::bad_alloc &) {
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  size_t count = 0;
-  for (size_t i = 0; i < num_records; ++i) {
-    if (((grn_ts_bool *)results)[i]) {
-      temp_records_[count] = records[i];
-      ++count;
-    }
-  }
-  if (count == 0) {
-    // Nothing to do.
-    return GRN_SUCCESS;
-  }
-
-  size_t old_size = arg_values_.size() / sizeof(grn_ts_bool);
-  if (old_size < num_records) try {
-    arg_values_.resize(sizeof(grn_ts_bool) * num_records);
-  } catch (const std::bad_alloc &) {
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  rc = arg2_->evaluate(&*temp_records_.begin(), count, &*arg_values_.begin());
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-
-  // Merge the evaluation results.
-  count = 0;
-  for (size_t i = 0; i < num_records; ++i) {
-    if (((grn_ts_bool *)results)[i]) {
-      ((grn_ts_bool *)results)[i] = arg_values_[count];
-      ++count;
-    }
-  }
-  return GRN_SUCCESS;
-}
-
-// ---- LogicalOrNode ----
-
-class LogicalOrNode : public OperatorNode {
- public:
-  ~LogicalOrNode() {}
-
-  static grn_rc open(ExpressionNode *arg1, ExpressionNode *arg2,
-                     ExpressionNode **node);
-
-  DataKind data_kind() const {
-    return GRN_TS_BOOL;
-  }
-  DataType data_type() const {
-    return GRN_DB_BOOL;
-  }
-  grn_obj *ref_table() const {
-    return NULL;
-  }
-  int dimension() const {
-    return 0;
-  }
-
-  grn_rc filter(Record *input, size_t input_size,
-                Record *output, size_t *output_size);
-  grn_rc evaluate(const Record *records, size_t num_records, void *results);
-
- private:
-  ExpressionNode *arg1_;
-  ExpressionNode *arg2_;
-  std::vector<grn_ts_bool> arg1_values_;
-  std::vector<grn_ts_bool> arg2_values_;
-  std::vector<Record> temp_records_;
-
-  LogicalOrNode(ExpressionNode *arg1, ExpressionNode *arg2)
-    : OperatorNode(), arg1_(arg1), arg2_(arg2), arg1_values_(),
-      arg2_values_(), temp_records_() {}
-
-  grn_rc fill_arg_values(ExpressionNode *arg,
-                         const Record *records, size_t num_records,
-                         std::vector<grn_ts_bool> *arg_values);
-};
-
-grn_rc LogicalOrNode::open(ExpressionNode *arg1, ExpressionNode *arg2,
-                            ExpressionNode **node) {
-  LogicalOrNode *new_node = new (std::nothrow) LogicalOrNode(arg1, arg2);
-  if (!new_node) {
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  *node = new_node;
-  return GRN_SUCCESS;
-}
-
-grn_rc LogicalOrNode::filter(Record *input, size_t input_size,
-                             Record *output, size_t *output_size) {
-  // Evaluate "arg1" for all the records.
-  // Then, evaluate "arg2" for false records.
-  grn_rc rc = fill_arg_values(arg1_, input, input_size, &arg1_values_);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  if (temp_records_.size() < input_size) try {
-    temp_records_.resize(input_size);
-  } catch (const std::bad_alloc &) {
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  size_t count = 0;
-  for (size_t i = 0; i < input_size; ++i) {
-    if (!arg1_values_[i]) {
-      temp_records_[count] = input[i];
-      ++count;
-    }
-  }
-  if (count == 0) {
-    if (input != output) {
-      for (size_t i = 0; i < input_size; ++i) {
-        output[i] = input[i];
-      }
-    }
-    *output_size = input_size;
-    return GRN_SUCCESS;
-  }
-  rc = fill_arg_values(arg2_, &*temp_records_.begin(), count, &arg2_values_);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-
-  // Merge the evaluation results.
-  count = 0;
-  size_t output_count = 0;
-  for (size_t i = 0; i < input_size; ++i) {
-    if (arg1_values_[i]) {
-      output[output_count] = input[i];
-      ++output_count;
-    } else {
-      if (arg2_values_[count]) {
-        output[output_count] = input[i];
-        ++output_count;
-      }
-      ++count;
-    }
-  }
-  *output_size = output_count;
-  return GRN_SUCCESS;
-}
-
-grn_rc LogicalOrNode::evaluate(const Record *records, size_t num_records,
-                               void *results) {
-  // Evaluate "arg1" for all the records.
-  // Then, evaluate "arg2" for false records.
-  grn_rc rc = arg1_->evaluate(records, num_records, results);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  if (temp_records_.size() < num_records) try {
-    temp_records_.resize(num_records);
-  } catch (const std::bad_alloc &) {
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  size_t count = 0;
-  for (size_t i = 0; i < num_records; ++i) {
-    if (!static_cast<grn_ts_bool *>(results)[i]) {
-      temp_records_[count] = records[i];
-      ++count;
-    }
-  }
-  if (count == 0) {
-    // Nothing to do.
-    return GRN_SUCCESS;
-  }
-  rc = fill_arg_values(arg2_, &*temp_records_.begin(), count, &arg2_values_);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-
-  // Merge the evaluation results.
-  count = 0;
-  for (size_t i = 0; i < num_records; ++i) {
-    if (!static_cast<grn_ts_bool *>(results)[i]) {
-      static_cast<grn_ts_bool *>(results)[i] = arg2_values_[count];
-      ++count;
-    }
-  }
-  return GRN_SUCCESS;
-}
-
-grn_rc LogicalOrNode::fill_arg_values(ExpressionNode *arg,
-                                      const Record *records,
-                                      size_t num_records,
-                                      std::vector<grn_ts_bool> *arg_values) {
-  size_t old_size = arg_values->size() / sizeof(grn_ts_bool);
-  if (old_size < num_records) try {
-    arg_values->resize(sizeof(grn_ts_bool) * num_records);
-  } catch (const std::bad_alloc &) {
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  return arg->evaluate(records, num_records, &*arg_values->begin());
-}
-
-// ---- Comparer ----
-
-template <OperatorType OPERATOR, DataKind KIND> struct Comparer;
-
-template <> struct Comparer<GRN_TS_EQUAL, GRN_TS_BOOL> {
-  grn_bool operator()(grn_ts_bool lhs, grn_ts_bool rhs) const {
-    return lhs == rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_EQUAL, GRN_TS_INT> {
-  grn_bool operator()(grn_ts_int lhs, grn_ts_int rhs) const {
-    return lhs == rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_EQUAL, GRN_TS_FLOAT> {
-  grn_bool operator()(grn_ts_float lhs, grn_ts_float rhs) const {
-    return (lhs <= rhs) && (lhs >= rhs);
-  }
-};
-template <> struct Comparer<GRN_TS_EQUAL, GRN_TS_TIME> {
-  grn_bool operator()(grn_ts_time lhs, grn_ts_time rhs) const {
-    return lhs == rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_EQUAL, GRN_TS_TEXT> {
-  grn_bool operator()(grn_ts_text lhs, grn_ts_text rhs) const {
-    return (lhs.size == rhs.size) && !std::memcmp(lhs.ptr, rhs.ptr, lhs.size);
-  }
-};
-template <> struct Comparer<GRN_TS_EQUAL, GRN_TS_GEO_POINT> {
-  grn_bool operator()(grn_ts_geo_point lhs, grn_ts_geo_point rhs) const {
-    return (lhs.latitude == rhs.latitude) && (lhs.longitude == rhs.longitude);
-  }
-};
-
-template <> struct Comparer<GRN_TS_NOT_EQUAL, GRN_TS_BOOL> {
-  grn_bool operator()(grn_ts_bool lhs, grn_ts_bool rhs) const {
-    return lhs != rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_NOT_EQUAL, GRN_TS_INT> {
-  grn_bool operator()(grn_ts_int lhs, grn_ts_int rhs) const {
-    return lhs != rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_NOT_EQUAL, GRN_TS_FLOAT> {
-  grn_bool operator()(grn_ts_float lhs, grn_ts_float rhs) const {
-    return (lhs < rhs) || (lhs > rhs);
-  }
-};
-template <> struct Comparer<GRN_TS_NOT_EQUAL, GRN_TS_TIME> {
-  grn_bool operator()(grn_ts_time lhs, grn_ts_time rhs) const {
-    return lhs != rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_NOT_EQUAL, GRN_TS_TEXT> {
-  grn_bool operator()(grn_ts_text lhs, grn_ts_text rhs) const {
-    return (lhs.size != rhs.size) || !!std::memcmp(lhs.ptr, rhs.ptr, lhs.size);
-  }
-};
-template <> struct Comparer<GRN_TS_NOT_EQUAL, GRN_TS_GEO_POINT> {
-  grn_bool operator()(grn_ts_geo_point lhs, grn_ts_geo_point rhs) const {
-    return (lhs.latitude != rhs.latitude) || (lhs.longitude != rhs.longitude);
-  }
-};
-
-template <> struct Comparer<GRN_TS_LESS, GRN_TS_INT> {
-  grn_bool operator()(grn_ts_int lhs, grn_ts_int rhs) const {
-    return lhs < rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_LESS, GRN_TS_FLOAT> {
-  grn_bool operator()(grn_ts_float lhs, grn_ts_float rhs) const {
-    return lhs < rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_LESS, GRN_TS_TIME> {
-  grn_bool operator()(grn_ts_time lhs, grn_ts_time rhs) const {
-    return lhs < rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_LESS, GRN_TS_TEXT> {
-  grn_bool operator()(grn_ts_text lhs, grn_ts_text rhs) const {
-    size_t min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;
-    int result = std::memcmp(lhs.ptr, rhs.ptr, min_size);
-    return !result ? (lhs.size < rhs.size) : (result < 0);
-  }
-};
-
-template <> struct Comparer<GRN_TS_LESS_EQUAL, GRN_TS_INT> {
-  grn_bool operator()(grn_ts_int lhs, grn_ts_int rhs) const {
-    return lhs <= rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_LESS_EQUAL, GRN_TS_FLOAT> {
-  grn_bool operator()(grn_ts_float lhs, grn_ts_float rhs) const {
-    return lhs <= rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_LESS_EQUAL, GRN_TS_TIME> {
-  grn_bool operator()(grn_ts_time lhs, grn_ts_time rhs) const {
-    return lhs <= rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_LESS_EQUAL, GRN_TS_TEXT> {
-  grn_bool operator()(grn_ts_text lhs, grn_ts_text rhs) const {
-    size_t min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;
-    int result = std::memcmp(lhs.ptr, rhs.ptr, min_size);
-    return !result ? (lhs.size <= rhs.size) : (result <= 0);
-  }
-};
-
-template <> struct Comparer<GRN_TS_GREATER, GRN_TS_INT> {
-  grn_bool operator()(grn_ts_int lhs, grn_ts_int rhs) const {
-    return lhs > rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_GREATER, GRN_TS_FLOAT> {
-  grn_bool operator()(grn_ts_float lhs, grn_ts_float rhs) const {
-    return lhs > rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_GREATER, GRN_TS_TIME> {
-  grn_bool operator()(grn_ts_time lhs, grn_ts_time rhs) const {
-    return lhs > rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_GREATER, GRN_TS_TEXT> {
-  grn_bool operator()(grn_ts_text lhs, grn_ts_text rhs) const {
-    size_t min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;
-    int result = std::memcmp(lhs.ptr, rhs.ptr, min_size);
-    return !result ? (lhs.size > rhs.size) : (result > 0);
-  }
-};
-
-template <> struct Comparer<GRN_TS_GREATER_EQUAL, GRN_TS_INT> {
-  grn_bool operator()(grn_ts_int lhs, grn_ts_int rhs) const {
-    return lhs >= rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_GREATER_EQUAL, GRN_TS_FLOAT> {
-  grn_bool operator()(grn_ts_float lhs, grn_ts_float rhs) const {
-    return lhs >= rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_GREATER_EQUAL, GRN_TS_TIME> {
-  grn_bool operator()(grn_ts_time lhs, grn_ts_time rhs) const {
-    return lhs >= rhs;
-  }
-};
-template <> struct Comparer<GRN_TS_GREATER_EQUAL, GRN_TS_TEXT> {
-  grn_bool operator()(grn_ts_text lhs, grn_ts_text rhs) const {
-    size_t min_size = (lhs.size < rhs.size) ? lhs.size : rhs.size;
-    int result = std::memcmp(lhs.ptr, rhs.ptr, min_size);
-    return !result ? (lhs.size >= rhs.size) : (result >= 0);
-  }
-};
-
-// -- ComparerNode --
-
-class ComparerNode : public ExpressionNode {
- public:
-  ~ComparerNode() {}
-
-  static grn_rc open(OperatorType operator_type,
-                     ExpressionNode *arg1, ExpressionNode *arg2,
-                     ExpressionNode **node);
-
-  ExpressionNodeType type() const {
-    return GRN_TS_OPERATOR_NODE;
-  }
-  DataKind data_kind() const {
-    return GRN_TS_BOOL;
-  }
-  DataType data_type() const {
-    return GRN_DB_BOOL;
-  }
-  grn_obj *ref_table() const {
-    return NULL;
-  }
-  int dimension() const {
-    return 0;
-  }
-
-  grn_rc filter(Record *input, size_t input_size,
-                Record *output, size_t *output_size);
-  grn_rc evaluate(const Record *records, size_t num_records, void *results);
-
- private:
-  OperatorType operator_type_;
-  ExpressionNode *arg1_;
-  ExpressionNode *arg2_;
-  std::vector<char> arg1_values_;
-  std::vector<char> arg2_values_;
-
-  ComparerNode(OperatorType operator_type,
-               ExpressionNode *arg1, ExpressionNode *arg2)
-    : ExpressionNode(), operator_type_(operator_type),
-      arg1_(arg1), arg2_(arg2), arg1_values_(), arg2_values_() {}
-
-  grn_rc fill_args(const Record *input, size_t input_size);
-
-  template <OperatorType OPERATOR>
-  grn_rc filter_eq(Record *input, size_t input_size,
-                   Record *output, size_t *output_size);
-  template <OperatorType OPERATOR>
-  grn_rc filter_cmp(Record *input, size_t input_size,
-                    Record *output, size_t *output_size);
-  template <OperatorType OPERATOR, DataKind KIND>
-  grn_rc _filter(Record *input, size_t input_size,
-                 Record *output, size_t *output_size);
-
-  template <OperatorType OPERATOR>
-  grn_rc evaluate_eq(const Record *records, size_t num_records, void *results);
-  template <OperatorType OPERATOR>
-  grn_rc evaluate_cmp(const Record *records, size_t num_records, void *results);
-  template <OperatorType OPERATOR, DataKind KIND>
-  grn_rc _evaluate(const Record *records, size_t num_records, void *results);
-};
-
-grn_rc ComparerNode::open(OperatorType operator_type,
-                          ExpressionNode *arg1, ExpressionNode *arg2,
-                          ExpressionNode **node) {
-  if (arg1->data_kind() != arg2->data_kind()) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  switch (operator_type) {
-    case GRN_TS_EQUAL:
-    case GRN_TS_NOT_EQUAL: {
-      break;
-    }
-    case GRN_TS_LESS:
-    case GRN_TS_LESS_EQUAL:
-    case GRN_TS_GREATER:
-    case GRN_TS_GREATER_EQUAL: {
-      switch (arg1->data_kind()) {
-        case GRN_TS_INT:
-        case GRN_TS_FLOAT:
-        case GRN_TS_TIME:
-        case GRN_TS_TEXT: {
-          break;
-        }
-        default: {
-          return GRN_INVALID_ARGUMENT;
-        }
-      }
-      break;
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-  ComparerNode *new_node = new (std::nothrow) ComparerNode(operator_type,
-                                                           arg1, arg2);
-  if (!new_node) {
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  *node = new_node;
-  return GRN_SUCCESS;
-}
-
-grn_rc ComparerNode::filter(Record *input, size_t input_size,
-                            Record *output, size_t *output_size) {
-  grn_rc rc = fill_args(input, input_size);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  switch (operator_type_) {
-    case GRN_TS_EQUAL: {
-      return filter_eq<GRN_TS_EQUAL>(input, input_size, output, output_size);
-    }
-    case GRN_TS_NOT_EQUAL: {
-      return filter_eq<GRN_TS_NOT_EQUAL>(input, input_size,
-                                         output, output_size);
-    }
-    case GRN_TS_LESS: {
-      return filter_cmp<GRN_TS_LESS>(input, input_size, output, output_size);
-    }
-    case GRN_TS_LESS_EQUAL: {
-      return filter_cmp<GRN_TS_LESS_EQUAL>(input, input_size,
-                                           output, output_size);
-    }
-    case GRN_TS_GREATER: {
-      return filter_cmp<GRN_TS_GREATER>(input, input_size,
-                                        output, output_size);
-    }
-    case GRN_TS_GREATER_EQUAL: {
-      return filter_cmp<GRN_TS_GREATER_EQUAL>(input, input_size,
-                                              output, output_size);
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-}
-
-grn_rc ComparerNode::evaluate(const Record *records,
-                              size_t num_records, void *results) {
-  grn_rc rc = fill_args(records, num_records);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  switch (operator_type_) {
-    case GRN_TS_EQUAL: {
-      return evaluate_eq<GRN_TS_EQUAL>(records, num_records, results);
-    }
-    case GRN_TS_NOT_EQUAL: {
-      return evaluate_eq<GRN_TS_NOT_EQUAL>(records, num_records, results);
-    }
-    case GRN_TS_LESS: {
-      return evaluate_cmp<GRN_TS_LESS>(records, num_records, results);
-    }
-    case GRN_TS_LESS_EQUAL: {
-      return evaluate_cmp<GRN_TS_LESS_EQUAL>(records, num_records, results);
-    }
-    case GRN_TS_GREATER: {
-      return evaluate_cmp<GRN_TS_GREATER>(records, num_records, results);
-    }
-    case GRN_TS_GREATER_EQUAL: {
-      return evaluate_cmp<GRN_TS_GREATER_EQUAL>(records, num_records, results);
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-}
-
-grn_rc ComparerNode::fill_args(const Record *input, size_t input_size) {
-  grn_rc rc = operator_node_fill_arg_values(input, input_size,
-                                            arg1_, &arg1_values_);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  rc = operator_node_fill_arg_values(input, input_size,
-                                     arg2_, &arg2_values_);
-  if (rc != GRN_SUCCESS) {
-    return rc;
-  }
-  return GRN_SUCCESS;
-}
-
-template <OperatorType OPERATOR>
-grn_rc ComparerNode::filter_eq(Record *input, size_t input_size,
-                               Record *output, size_t *output_size) {
-  switch (arg1_->data_kind()) {
-    case GRN_TS_BOOL: {
-      return _filter<OPERATOR, GRN_TS_BOOL>(input, input_size,
-                                            output, output_size);
-    }
-    case GRN_TS_INT: {
-      return _filter<OPERATOR, GRN_TS_INT>(input, input_size,
-                                           output, output_size);
-    }
-    case GRN_TS_FLOAT: {
-      return _filter<OPERATOR, GRN_TS_FLOAT>(input, input_size,
-                                             output, output_size);
-    }
-    case GRN_TS_TIME: {
-      return _filter<OPERATOR, GRN_TS_TIME>(input, input_size,
-                                            output, output_size);
-    }
-    case GRN_TS_TEXT: {
-      return _filter<OPERATOR, GRN_TS_TEXT>(input, input_size,
-                                            output, output_size);
-    }
-    case GRN_TS_GEO_POINT: {
-      return _filter<OPERATOR, GRN_TS_GEO_POINT>(input, input_size,
-                                                 output, output_size);
-    }
-    default: {
-      return GRN_OPERATION_NOT_SUPPORTED;
-    }
-  }
-}
-
-template <OperatorType OPERATOR>
-grn_rc ComparerNode::filter_cmp(Record *input, size_t input_size,
-                                Record *output, size_t *output_size) {
-  switch (arg1_->data_kind()) {
-    case GRN_TS_INT: {
-      return _filter<OPERATOR, GRN_TS_INT>(input, input_size,
-                                           output, output_size);
-    }
-    case GRN_TS_FLOAT: {
-      return _filter<OPERATOR, GRN_TS_FLOAT>(input, input_size,
-                                             output, output_size);
-    }
-    case GRN_TS_TIME: {
-      return _filter<OPERATOR, GRN_TS_TIME>(input, input_size,
-                                            output, output_size);
-    }
-    case GRN_TS_TEXT: {
-      return _filter<OPERATOR, GRN_TS_TEXT>(input, input_size,
-                                            output, output_size);
-    }
-    default: {
-      return GRN_OPERATION_NOT_SUPPORTED;
-    }
-  }
-}
-
-template <OperatorType OPERATOR, DataKind KIND>
-grn_rc ComparerNode::_filter(Record *input, size_t input_size,
-                             Record *output, size_t *output_size) {
-  typedef KindTraits<KIND> Traits;
-  typedef typename Traits::Type Type;
-  Type *input1 = reinterpret_cast<Type *>(&*arg1_values_.begin());
-  Type *input2 = reinterpret_cast<Type *>(&*arg2_values_.begin());
-  size_t count = 0;
-  for (size_t i = 0; i < input_size; ++i) {
-    if (Comparer<OPERATOR, KIND>()(input1[i], input2[i])) {
-      output[count] = input[i];
-      ++count;
-    }
-  }
-  *output_size = count;
-  return GRN_SUCCESS;
-}
-
-template <OperatorType OPERATOR>
-grn_rc ComparerNode::evaluate_eq(const Record *records, size_t num_records,
-                                 void *results) {
-  switch (arg1_->data_kind()) {
-    case GRN_TS_BOOL: {
-      return _evaluate<OPERATOR, GRN_TS_BOOL>(records, num_records, results);
-    }
-    case GRN_TS_INT: {
-      return _evaluate<OPERATOR, GRN_TS_INT>(records, num_records, results);
-    }
-    case GRN_TS_FLOAT: {
-      return _evaluate<OPERATOR, GRN_TS_FLOAT>(records, num_records, results);
-    }
-    case GRN_TS_TIME: {
-      return _evaluate<OPERATOR, GRN_TS_TIME>(records, num_records, results);
-    }
-    case GRN_TS_TEXT: {
-      return _evaluate<OPERATOR, GRN_TS_TEXT>(records, num_records, results);
-    }
-    case GRN_TS_GEO_POINT: {
-      return _evaluate<OPERATOR, GRN_TS_GEO_POINT>(records, num_records,
-                                                   results);
-    }
-    default: {
-      return GRN_OPERATION_NOT_SUPPORTED;
-    }
-  }
-}
-
-template <OperatorType OPERATOR>
-grn_rc ComparerNode::evaluate_cmp(const Record *records, size_t num_records,
-                                  void *results) {
-  switch (arg1_->data_kind()) {
-    case GRN_TS_INT: {
-      return _evaluate<OPERATOR, GRN_TS_INT>(records, num_records, results);
-    }
-    case GRN_TS_FLOAT: {
-      return _evaluate<OPERATOR, GRN_TS_FLOAT>(records, num_records, results);
-    }
-    case GRN_TS_TIME: {
-      return _evaluate<OPERATOR, GRN_TS_TIME>(records, num_records, results);
-    }
-    case GRN_TS_TEXT: {
-      return _evaluate<OPERATOR, GRN_TS_TEXT>(records, num_records, results);
-    }
-    default: {
-      return GRN_OPERATION_NOT_SUPPORTED;
-    }
-  }
-}
-
-template <OperatorType OPERATOR, DataKind KIND>
-grn_rc ComparerNode::_evaluate(const Record *records, size_t num_records,
-                               void *results) {
-  typedef KindTraits<KIND> Traits;
-  typedef typename Traits::Type Type;
-  Type *input1 = reinterpret_cast<Type *>(&*arg1_values_.begin());
-  Type *input2 = reinterpret_cast<Type *>(&*arg2_values_.begin());
-  grn_ts_bool *output = static_cast<grn_ts_bool *>(results);
-  for (size_t i = 0; i < num_records; ++i) {
-    output[i] = Comparer<OPERATOR, KIND>()(input1[i], input2[i]);
-  }
-  return GRN_SUCCESS;
-}
-
-// -- ExpressionToken --
-
-enum ExpressionTokenType {
-  DUMMY_TOKEN,
-  CONSTANT_TOKEN,
-  NAME_TOKEN,
-  UNARY_OPERATOR_TOKEN,
-  BINARY_OPERATOR_TOKEN,
-  DEREFERENCE_TOKEN,
-  BRACKET_TOKEN
-};
-
-enum ExpressionBracketType {
-  LEFT_ROUND_BRACKET,
-  RIGHT_ROUND_BRACKET,
-  LEFT_SQUARE_BRACKET,
-  RIGHT_SQUARE_BRACKET
-};
-
-// TODO: std::string should not be used.
-class ExpressionToken {
- public:
-  ExpressionToken() : string_(), type_(DUMMY_TOKEN), dummy_(0), priority_(0) {}
-  ExpressionToken(const std::string &string, ExpressionTokenType token_type)
-    : string_(string), type_(token_type), dummy_(0), priority_(0) {}
-  ExpressionToken(const std::string &string,
-    ExpressionBracketType bracket_type)
-      : string_(string), type_(BRACKET_TOKEN), bracket_type_(bracket_type),
-        priority_(0) {}
-  ExpressionToken(const std::string &string, OperatorType operator_type)
-      : string_(string), type_(get_operator_token_type(operator_type)),
-        operator_type_(operator_type),
-        priority_(get_operator_priority(operator_type)) {}
-
-  const std::string &string() const {
-    return string_;
-  }
-  ExpressionTokenType type() const {
-    return type_;
-  }
-  ExpressionBracketType bracket_type() const {
-    return bracket_type_;
-  }
-  OperatorType operator_type() const {
-    return operator_type_;
-  }
-  int priority() const {
-    return priority_;
-  }
-
- private:
-  std::string string_;
-  ExpressionTokenType type_;
-  union {
-    int dummy_;
-    ExpressionBracketType bracket_type_;
-    OperatorType operator_type_;
-  };
-  int priority_;
-
-  static ExpressionTokenType get_operator_token_type(
-    OperatorType operator_type);
-  static int get_operator_priority(OperatorType operator_type);
-};
-
-ExpressionTokenType ExpressionToken::get_operator_token_type(
-  OperatorType operator_type) {
-  switch (operator_type) {
-    case GRN_TS_LOGICAL_NOT: {
-      return UNARY_OPERATOR_TOKEN;
-    }
-    case GRN_TS_LOGICAL_AND:
-    case GRN_TS_LOGICAL_OR:
-    case GRN_TS_EQUAL:
-    case GRN_TS_NOT_EQUAL:
-    case GRN_TS_LESS:
-    case GRN_TS_LESS_EQUAL:
-    case GRN_TS_GREATER:
-    case GRN_TS_GREATER_EQUAL: {
-      return BINARY_OPERATOR_TOKEN;
-    }
-    default: {
-      // TODO: ERROR_TOKEN or something should be used...?
-      //       Or, default should be removed?
-      return DUMMY_TOKEN;
-    }
-  }
-}
-
-int ExpressionToken::get_operator_priority(
-  OperatorType operator_type) {
-  switch (operator_type) {
-    case GRN_TS_LOGICAL_NOT: {
-//    case GRN_OP_BITWISE_NOT:
-//    case GRN_OP_POSITIVE:
-//    case GRN_OP_NEGATIVE:
-//    case GRN_OP_TO_INT:
-//    case GRN_OP_TO_FLOAT: {
-      return 3;
-    }
-    case GRN_TS_LOGICAL_AND: {
-      return 13;
-    }
-    case GRN_TS_LOGICAL_OR: {
-      return 14;
-    }
-    case GRN_TS_EQUAL:
-    case GRN_TS_NOT_EQUAL: {
-      return 9;
-    }
-    case GRN_TS_LESS:
-    case GRN_TS_LESS_EQUAL:
-    case GRN_TS_GREATER:
-    case GRN_TS_GREATER_EQUAL: {
-      return 8;
-    }
-//    case GRN_OP_BITWISE_AND: {
-//      return 10;
-//    }
-//    case GRN_OP_BITWISE_OR: {
-//      return 12;
-//    }
-//    case GRN_OP_BITWISE_XOR: {
-//      return 11;
-//    }
-//    case GRN_OP_PLUS:
-//    case GRN_OP_MINUS: {
-//      return 6;
-//    }
-//    case GRN_OP_MULTIPLICATION:
-//    case GRN_OP_DIVISION:
-//    case GRN_OP_MODULUS: {
-//      return 5;
-//    }
-//    case GRN_OP_STARTS_WITH:
-//    case GRN_OP_ENDS_WITH:
-//    case GRN_OP_CONTAINS: {
-//      return 7;
-//    }
-//    case GRN_OP_SUBSCRIPT: {
-//      return 2;
-//    }
-    default: {
-      return 100;
-    }
-  }
-}
-
-// -- ExpressionParser --
-
-class ExpressionParser {
- public:
-  static grn_rc parse(grn_ctx *ctx, grn_obj *table,
-    const char *query, size_t query_size, Expression **expression);
-
- private:
-  grn_ctx *ctx_;
-  grn_obj *table_;
-  std::vector<ExpressionToken> tokens_;
-  std::vector<ExpressionToken> stack_;
-  Expression *expression_;
-
-  ExpressionParser(grn_ctx *ctx, grn_obj *table)
-    : ctx_(ctx), table_(table), tokens_(), stack_(), expression_(NULL) {}
-  ~ExpressionParser() {
-    delete expression_;
-  }
-
-  grn_rc tokenize(const char *query, size_t query_size);
-  grn_rc compose();
-  grn_rc push_token(const ExpressionToken &token);
-};
-
-grn_rc ExpressionParser::parse(grn_ctx *ctx, grn_obj *table,
-  const char *query, size_t query_size, Expression **expression) {
-  ExpressionParser *parser = new (std::nothrow) ExpressionParser(ctx, table);
-  if (!parser) {
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  grn_rc rc = parser->tokenize(query, query_size);
-  if (rc == GRN_SUCCESS) {
-    rc = parser->compose();
-    if (rc == GRN_SUCCESS) {
-      *expression = parser->expression_;
-      parser->expression_ = NULL;
-    }
-  }
-  delete parser;
-  return rc;
-}
-
-grn_rc ExpressionParser::tokenize(const char *query, size_t query_size) {
-  const char *rest = query;
-  size_t rest_size = query_size;
-  while (rest_size != 0) {
-    // Ignore white-space characters.
-    size_t pos;
-    for (pos = 0; pos < rest_size; ++pos) {
-      if (!std::isspace(static_cast<uint8_t>(rest[pos]))) {
-        break;
-      }
-    }
-    rest += pos;
-    rest_size -= pos;
-    switch (rest[0]) {
-      case '!': {
-//        if ((rest_size >= 2) && (rest[1] == '=')) {
-//          tokens_.push_back(ExpressionToken("!=", GRN_TS_NOT_EQUAL));
-//          rest += 2;
-//          rest_size -= 2;
-//        } else {
-          tokens_.push_back(ExpressionToken("!", GRN_TS_LOGICAL_NOT));
-          ++rest;
-          --rest_size;
-//        }
-        break;
-      }
-////      case '~': {
-////        tokens_.push_back(ExpressionToken("~", GRN_OP_BITWISE_NOT));
-////        rest = rest.substring(1);
-////        break;
-////      }
-      case '=': {
-        if ((rest_size >= 2) && (rest[1] == '=')) {
-          tokens_.push_back(ExpressionToken("==", GRN_TS_EQUAL));
-          rest += 2;
-          rest_size -= 2;
-        } else {
-          return GRN_INVALID_ARGUMENT;
-        }
-        break;
-      }
-      case '<': {
-        if ((rest_size >= 2) && (rest[1] == '=')) {
-          tokens_.push_back(ExpressionToken("<=", GRN_TS_LESS_EQUAL));
-          rest += 2;
-          rest_size -= 2;
-        } else {
-          tokens_.push_back(ExpressionToken("<", GRN_TS_LESS));
-          ++rest;
-          --rest_size;
-        }
-        break;
-      }
-      case '>': {
-        if ((rest_size >= 2) && (rest[1] == '=')) {
-          tokens_.push_back(ExpressionToken(">=", GRN_TS_GREATER_EQUAL));
-          rest += 2;
-          rest_size -= 2;
-        } else {
-          tokens_.push_back(ExpressionToken(">", GRN_TS_GREATER));
-          ++rest;
-          --rest_size;
-        }
-        break;
-      }
-      case '&': {
-        if ((rest_size >= 2) && (rest[1] == '&')) {
-          tokens_.push_back(ExpressionToken("&&", GRN_TS_LOGICAL_AND));
-          rest += 2;
-          rest_size -= 2;
-        } else {
-//          tokens_.push_back(ExpressionToken("&", GRN_OP_BITWISE_AND));
-//          ++rest;
-//          --rest_size;
-          return GRN_INVALID_ARGUMENT;
-        }
-        break;
-      }
-      case '|': {
-        if ((rest_size >= 2) && (rest[1] == '|')) {
-          tokens_.push_back(ExpressionToken("||", GRN_TS_LOGICAL_OR));
-          rest += 2;
-          rest_size -= 2;
-        } else {
-//          tokens_.push_back(ExpressionToken("|", GRN_OP_BITWISE_OR));
-//          ++rest;
-//          --rest_size;
-          return GRN_INVALID_ARGUMENT;
-        }
-        break;
-      }
-//      case '^': {
-//        tokens_.push_back(ExpressionToken("^", GRN_OP_BITWISE_XOR));
-//        rest = rest.substring(1);
-//        break;
-//      }
-//      case '+': {
-//        tokens_.push_back(ExpressionToken("+", GRN_OP_PLUS));
-//        rest = rest.substring(1);
-//        break;
-//      }
-//      case '-': {
-//        tokens_.push_back(ExpressionToken("-", GRN_OP_MINUS));
-//        rest = rest.substring(1);
-//        break;
-//      }
-//      case '*': {
-//        tokens_.push_back(ExpressionToken("*", GRN_OP_MULTIPLICATION));
-//        rest = rest.substring(1);
-//        break;
-//      }
-//      case '/': {
-//        tokens_.push_back(ExpressionToken("/", GRN_OP_DIVISION));
-//        rest = rest.substring(1);
-//        break;
-//      }
-//      case '%': {
-//        tokens_.push_back(ExpressionToken("%", GRN_OP_MODULUS));
-//        rest = rest.substring(1);
-//        break;
-//      }
-//      case '@': {
-//        if ((rest_size >= 2) && (rest[1] == '^')) {
-//          tokens_.push_back(ExpressionToken("@^", GRN_OP_STARTS_WITH));
-//          rest = rest.substring(2);
-//        } else if ((rest_size >= 2) && (rest[1] == '$')) {
-//          tokens_.push_back(ExpressionToken("@$", GRN_OP_ENDS_WITH));
-//          rest = rest.substring(2);
-//        } else {
-//          tokens_.push_back(ExpressionToken("@", GRN_OP_CONTAINS));
-//          rest = rest.substring(1);
-//        }
-//        break;
-//      }
-//      case '.': {
-//        tokens_.push_back(ExpressionToken(".", DEREFERENCE_TOKEN));
-//        rest = rest.substring(1);
-//        break;
-//      }
-      case '(': {
-        tokens_.push_back(ExpressionToken("(", LEFT_ROUND_BRACKET));
-        ++rest;
-        --rest_size;
-        break;
-      }
-      case ')': {
-        tokens_.push_back(ExpressionToken(")", RIGHT_ROUND_BRACKET));
-        ++rest;
-        --rest_size;
-        break;
-      }
-//      case '[': {
-//        tokens_.push_back(ExpressionToken("[", LEFT_SQUARE_BRACKET));
-//        rest = rest.substring(1);
-//        break;
-//      }
-//      case ']': {
-//        tokens_.push_back(ExpressionToken("]", RIGHT_SQUARE_BRACKET));
-//        rest = rest.substring(1);
-//        break;
-//      }
-      case '"': {
-        for (pos = 1; pos < rest_size; ++pos) {
-          if (rest[pos] == '\\') {
-            if (pos == rest_size) {
-              break;
-            }
-            ++pos;
-          } else if (rest[pos] == '"') {
-            break;
-          }
-        }
-        if (pos == rest_size) {
-          return GRN_INVALID_ARGUMENT;
-        }
-        tokens_.push_back(
-          ExpressionToken(std::string(rest + 1, pos - 1), CONSTANT_TOKEN));
-        rest += pos + 1;
-        rest_size -= pos + 1;
-        break;
-      }
-      case '0' ... '9': {
-        // TODO: Improve this.
-        for (pos = 1; pos < rest_size; ++pos) {
-          if (!std::isdigit(static_cast<uint8_t>(rest[pos]))) {
-            break;
-          }
-        }
-        tokens_.push_back(
-          ExpressionToken(std::string(rest, pos), CONSTANT_TOKEN));
-        rest += pos;
-        rest_size -= pos;
-        break;
-      }
-      case '_':
-      case 'A' ... 'Z':
-      case 'a' ... 'z': {
-        // TODO: Improve this.
-        for (pos = 1; pos < rest_size; ++pos) {
-          if ((rest[pos] != '_') && (!std::isalnum(rest[pos]))) {
-            break;
-          }
-        }
-        std::string token(rest, pos);
-        if ((token == "true") || (token == "false")) {
-          tokens_.push_back(ExpressionToken(token, CONSTANT_TOKEN));
-        } else {
-          tokens_.push_back(ExpressionToken(token, NAME_TOKEN));
-        }
-        rest += pos;
-        rest_size -= pos;
-        break;
-      }
-      default: {
-        return GRN_INVALID_ARGUMENT;
-      }
-    }
-  }
-  return GRN_SUCCESS;
-}
-
-grn_rc ExpressionParser::compose() {
-  if (tokens_.size() == 0) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  expression_ = new (std::nothrow) Expression(ctx_, table_);
-  grn_rc rc = push_token(ExpressionToken("(", LEFT_ROUND_BRACKET));
-  if (rc == GRN_SUCCESS) {
-    for (size_t i = 0; i < tokens_.size(); ++i) {
-      rc = push_token(tokens_[i]);
-      if (rc != GRN_SUCCESS) {
-        break;
-      }
-    }
-    if (rc == GRN_SUCCESS) {
-      rc = push_token(ExpressionToken(")", RIGHT_ROUND_BRACKET));
-    }
-  }
-  return rc;
-}
-
-grn_rc ExpressionParser::push_token(const ExpressionToken &token) {
-  grn_rc rc = GRN_SUCCESS;
-  switch (token.type()) {
-    case DUMMY_TOKEN: {
-      if ((stack_.size() != 0) && (stack_.back().type() == DUMMY_TOKEN)) {
-        return GRN_INVALID_ARGUMENT;
-      }
-      stack_.push_back(token);
-      break;
-    }
-    case CONSTANT_TOKEN: {
-      grn_obj *obj;
-      const std::string string = token.string();
-      if (std::isdigit(static_cast<uint8_t>(string[0]))) {
-        if (string.find_first_of('.') == string.npos) {
-          obj = grn_obj_open(ctx_, GRN_BULK, 0, GRN_DB_INT64);
-          if (obj) {
-            GRN_INT64_SET(ctx_, obj, strtoll(string.c_str(), NULL, 10));
-          }
-        } else {
-          obj = grn_obj_open(ctx_, GRN_BULK, 0, GRN_DB_FLOAT);
-          if (obj) {
-            GRN_FLOAT_SET(ctx_, obj, strtod(string.c_str(), NULL));
-          }
-        }
-      } else if (string == "true") {
-        obj = grn_obj_open(ctx_, GRN_BULK, 0, GRN_DB_BOOL);
-        if (obj) {
-          GRN_BOOL_SET(ctx_, obj, GRN_TRUE);
-        }
-      } else if (string == "false") {
-        obj = grn_obj_open(ctx_, GRN_BULK, 0, GRN_DB_BOOL);
-        if (obj) {
-          GRN_BOOL_SET(ctx_, obj, GRN_FALSE);
-        }
-      } else {
-        obj = grn_obj_open(ctx_, GRN_BULK, 0, GRN_DB_TEXT);
-        if (obj) {
-          GRN_TEXT_SET(ctx_, obj, string.data(), string.size());
-        }
-      }
-      if (!obj) {
-        if (ctx_->rc != GRN_SUCCESS) {
-          return ctx_->rc;
-        }
-        return GRN_UNKNOWN_ERROR;
-      }
-      rc = push_token(ExpressionToken(string, DUMMY_TOKEN));
-      if (rc == GRN_SUCCESS) {
-        rc = expression_->push_object(obj);
-      }
-      if (rc != GRN_SUCCESS) {
-        grn_obj_close(ctx_, obj);
-      }
-      break;
-    }
-    case NAME_TOKEN: {
-      rc = push_token(ExpressionToken(token.string(), DUMMY_TOKEN));
-      if (rc == GRN_SUCCESS) {
-        grn_obj *column = grn_obj_column(
-          ctx_, table_, token.string().data(), token.string().size());
-        rc = expression_->push_object(column);
-      }
-      break;
-    }
-    case UNARY_OPERATOR_TOKEN: {
-      if ((stack_.size() != 0) && (stack_.back().type() == DUMMY_TOKEN)) {
-        // A unary operator must not follow an operand.
-        return GRN_INVALID_ARGUMENT;
-      }
-      stack_.push_back(token);
-      break;
-    }
-    case BINARY_OPERATOR_TOKEN: {
-      if ((stack_.size() == 0) || (stack_.back().type() != DUMMY_TOKEN)) {
-        // A binary operator must follow an operand.
-        return GRN_INVALID_ARGUMENT;
-      }
-      // Apply previous operators if those are prior to the new operator.
-      while (stack_.size() >= 2) {
-        ExpressionToken operator_token = stack_[stack_.size() - 2];
-//        if (operator_token.type() == DEREFERENCE_TOKEN) {
-//          expression_->end_subexpression();
-//          stack_.pop_back();
-//          stack_.pop_back();
-//          push_token(ExpressionToken("", DUMMY_TOKEN));
-//        } else if (operator_token.type() == UNARY_OPERATOR_TOKEN) {
-        if (operator_token.type() == UNARY_OPERATOR_TOKEN) {
-          rc = expression_->push_operator(operator_token.operator_type());
-          if (rc == GRN_SUCCESS) {
-            stack_.pop_back();
-            stack_.pop_back();
-            rc = push_token(ExpressionToken("", DUMMY_TOKEN));
-          }
-        } else if ((operator_token.type() == BINARY_OPERATOR_TOKEN) &&
-                   (operator_token.priority() <= token.priority())) {
-          rc = expression_->push_operator(operator_token.operator_type());
-          if (rc == GRN_SUCCESS) {
-            stack_.pop_back();
-            stack_.pop_back();
-            stack_.pop_back();
-            rc = push_token(ExpressionToken("", DUMMY_TOKEN));
-          }
-        } else {
-          break;
-        }
-        if (rc != GRN_SUCCESS) {
-          return rc;
-        }
-      }
-      stack_.push_back(token);
-      break;
-    }
-//    case DEREFERENCE_TOKEN: {
-//      builder_->begin_subexpression();
-//      stack_.pop_back();
-//      stack_.push_back(token);
-//      break;
-//    }
-    case BRACKET_TOKEN: {
-      if (token.bracket_type() == LEFT_ROUND_BRACKET) {
-        // A left round bracket must not follow a dummy.
-        if ((stack_.size() != 0) && (stack_.back().type() == DUMMY_TOKEN)) {
-          return GRN_INVALID_ARGUMENT;
-        }
-        stack_.push_back(token);
-      } else if (token.bracket_type() == RIGHT_ROUND_BRACKET) {
-        // A right round bracket must follow a dummy.
-        // A left round bracket must exist before a right round bracket.
-        if ((stack_.size() < 2) || (stack_.back().type() != DUMMY_TOKEN)) {
-          return GRN_INVALID_ARGUMENT;
-        }
-        // Apply operators in brackets.
-        while (stack_.size() >= 2) {
-          ExpressionToken operator_token = stack_[stack_.size() - 2];
-//          if (operator_token.type() == DEREFERENCE_TOKEN) {
-//            rc = expression_->end_subexpression();
-//            if (rc == GRN_SUCCESS) {
-//              stack_.pop_back();
-//              stack_.pop_back();
-//              rc = push_token(ExpressionToken("", DUMMY_TOKEN));
-//            }
-//          } else if (operator_token.type() == UNARY_OPERATOR_TOKEN) {
-          if (operator_token.type() == UNARY_OPERATOR_TOKEN) {
-            rc = expression_->push_operator(operator_token.operator_type());
-            if (rc == GRN_SUCCESS) {
-              stack_.pop_back();
-              stack_.pop_back();
-              rc = push_token(ExpressionToken("", DUMMY_TOKEN));
-            }
-          } else if (operator_token.type() == BINARY_OPERATOR_TOKEN) {
-            rc = expression_->push_operator(operator_token.operator_type());
-            if (rc == GRN_SUCCESS) {
-              stack_.pop_back();
-              stack_.pop_back();
-              stack_.pop_back();
-              rc = push_token(ExpressionToken("", DUMMY_TOKEN));
-            }
-          } else {
-            break;
-          }
-          if (rc != GRN_SUCCESS) {
-            return rc;
-          }
-        }
-        if ((stack_.size() < 2) ||
-            (stack_[stack_.size() - 2].type() != BRACKET_TOKEN) ||
-            (stack_[stack_.size() - 2].bracket_type() != LEFT_ROUND_BRACKET)) {
-          return GRN_INVALID_ARGUMENT;
-        }
-        stack_[stack_.size() - 2] = stack_.back();
-        stack_.pop_back();
-//      } else if (token.bracket_type() == LEFT_SQUARE_BRACKET) {
-//        // A left square bracket must follow a dummy.
-//        if ((stack_.size() == 0) || (stack_.back().type() != DUMMY_TOKEN)) {
-//          return GRN_INVALID_ARGUMENT;
-//        }
-//        stack_.push_back(token);
-//      } else if (token.bracket_type() == RIGHT_SQUARE_BRACKET) {
-//        // A right round bracket must follow a dummy.
-//        // A left round bracket must exist before a right round bracket.
-//        if ((stack_.size() < 2) || (stack_.back().type() != DUMMY_TOKEN)) {
-//          return GRN_INVALID_ARGUMENT;
-//        }
-//        // Apply operators in bracket.
-//        while (stack_.size() >= 2) {
-//          ExpressionToken operator_token = stack_[stack_.size() - 2];
-//          if (operator_token.type() == DEREFERENCE_TOKEN) {
-//            builder_->end_subexpression();
-//            stack_.pop_back();
-//            stack_.pop_back();
-//            push_token(ExpressionToken("", DUMMY_TOKEN));
-//          } else if (operator_token.type() == UNARY_OPERATOR_TOKEN) {
-//            builder_->push_operator(operator_token.operator_type());
-//            stack_.pop_back();
-//            stack_.pop_back();
-//            push_token(ExpressionToken("", DUMMY_TOKEN));
-//          } else if (operator_token.type() == BINARY_OPERATOR_TOKEN) {
-//            builder_->push_operator(operator_token.operator_type());
-//            stack_.pop_back();
-//            stack_.pop_back();
-//            stack_.pop_back();
-//            push_token(ExpressionToken("", DUMMY_TOKEN));
-//          } else {
-//            break;
-//          }
-//        }
-//        if ((stack_.size() < 2) ||
-//            (stack_[stack_.size() - 2].type() != BRACKET_TOKEN) ||
-//            (stack_[stack_.size() - 2].bracket_type() != LEFT_SQUARE_BRACKET)) {
-//          return GRN_INVALID_ARGUMENT;
-//        }
-//        stack_.pop_back();
-//        stack_.pop_back();
-//        builder_->push_operator(GRNXX_SUBSCRIPT);
-      } else {
-        return GRN_INVALID_ARGUMENT;
-      }
-      break;
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-  return rc;
-}
-
-// -- Expression --
-
-Expression::Expression(grn_ctx *ctx, grn_obj *table)
-  : ctx_(ctx), table_(table), type_(GRN_TS_INCOMPLETE),
-    output_type_(GRN_DB_VOID), stack_() {}
-
-Expression::~Expression() {
-  for (size_t i = 0; i < stack_.size(); ++i) {
-    delete stack_[i];
-  }
-}
-
-grn_rc Expression::open(
-  grn_ctx *ctx, grn_obj *table, Expression **expression) {
-  if (!ctx || !grn_obj_is_table(ctx, table) || !expression) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  Expression *new_expression = new (std::nothrow) Expression(ctx, table);
-  if (!new_expression) {
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  *expression = new_expression;
-  return GRN_SUCCESS;
-}
-
-grn_rc Expression::parse(grn_ctx *ctx, grn_obj *table,
-  const char *query, size_t query_size, Expression **expression) {
-  if (!ctx || !grn_obj_is_table(ctx, table) ||
-      !query || (query_size == 0) || !expression) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  return ExpressionParser::parse(ctx, table, query, query_size, expression);
-}
-
-DataKind Expression::data_kind() const {
-  ExpressionNode *root = this->root();
-  return root ? root->data_kind() : GRN_TS_VOID;
-}
-
-DataType Expression::data_type() const {
-  ExpressionNode *root = this->root();
-  return root ? root->data_type() : GRN_ID_NIL;
-}
-
-DataType Expression::output_type() const {
-  return output_type_;
-}
-
-int Expression::dimension() const {
-  ExpressionNode *root = this->root();
-  return root ? root->dimension() : 0;
-}
-
-grn_rc Expression::push_object(grn_obj *obj) {
-  if (!obj) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  grn_rc rc = GRN_UNKNOWN_ERROR;
-  switch (obj->header.type) {
-    case GRN_BULK:
-    case GRN_UVECTOR:
-    case GRN_VECTOR: {
-      rc = push_constant_object(obj);
-      break;
-    }
-    case GRN_ACCESSOR:
-    case GRN_COLUMN_FIX_SIZE:
-    case GRN_COLUMN_VAR_SIZE: {
-      rc = push_column_object(obj);
-      break;
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-  if (rc == GRN_SUCCESS) {
-    update_type();
-  }
-  return rc;
-}
-
-grn_rc Expression::push_operator(OperatorType operator_type) {
-  grn_rc rc = GRN_UNKNOWN_ERROR;
-  ExpressionNode *node;
-  switch (operator_type) {
-    case GRN_TS_LOGICAL_NOT: {
-      if (stack_.size() < 1) {
-        return GRN_INVALID_FORMAT;
-      }
-      ExpressionNode *arg = stack_[stack_.size() - 1];
-      rc = create_unary_node(operator_type, arg, &node);
-      if (rc == GRN_SUCCESS) {
-        stack_.resize(stack_.size() - 1);
-      }
-      break;
-    }
-    case GRN_TS_LOGICAL_AND:
-    case GRN_TS_LOGICAL_OR:
-    case GRN_TS_EQUAL:
-    case GRN_TS_NOT_EQUAL:
-    case GRN_TS_LESS:
-    case GRN_TS_LESS_EQUAL:
-    case GRN_TS_GREATER:
-    case GRN_TS_GREATER_EQUAL: {
-      if (stack_.size() < 2) {
-        return GRN_INVALID_FORMAT;
-      }
-      ExpressionNode *arg1 = stack_[stack_.size() - 2];
-      ExpressionNode *arg2 = stack_[stack_.size() - 1];
-      rc = create_binary_node(operator_type, arg1, arg2, &node);
-      if (rc == GRN_SUCCESS) {
-        stack_.resize(stack_.size() - 2);
-      }
-      break;
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-  if (rc == GRN_SUCCESS) {
-    stack_.push_back(node);
-    update_type();
-  }
-  return rc;
-}
-
-grn_rc Expression::filter(
-  Record *input, size_t input_size,
-  Record *output, size_t *output_size) {
-  if ((!input && (input_size != 0)) ||
-      ((output > input) && (output < (input + input_size))) || !output_size) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  ExpressionNode *root = this->root();
-  if (!root) {
-    return GRN_UNKNOWN_ERROR;
-  }
-  if (!output) {
-    output = input;
-  }
-  size_t total_output_size = 0;
-  while (input_size > 0) {
-    size_t batch_input_size = GRN_TS_MAX_BATCH_SIZE;
-    if (input_size < batch_input_size) {
-      batch_input_size = input_size;
-    }
-    size_t batch_output_size;
-    grn_rc rc = root->filter(
-      input, batch_input_size, output, &batch_output_size);
-    if (rc != GRN_SUCCESS) {
-      return rc;
-    }
-    input += batch_input_size;
-    input_size -= batch_input_size;
-    output += batch_output_size;
-    total_output_size += batch_output_size;
-  }
-  *output_size = total_output_size;
-  return GRN_SUCCESS;
-}
-
-grn_rc Expression::adjust(Record *records, size_t num_records) {
-  if (!records && (num_records != 0)) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  ExpressionNode *root = this->root();
-  if (!root) {
-    return GRN_UNKNOWN_ERROR;
-  }
-  while (num_records > 0) {
-    size_t batch_size = GRN_TS_MAX_BATCH_SIZE;
-    if (num_records < batch_size) {
-      batch_size = num_records;
-    }
-    grn_rc rc = root->adjust(records, batch_size);
-    if (rc != GRN_SUCCESS) {
-      return rc;
-    }
-    records += batch_size;
-    num_records -= batch_size;
-  }
-  return GRN_SUCCESS;
-}
-
-grn_rc Expression::evaluate(const Record *records, size_t num_records,
-                            void *results) {
-  if ((!records || !results) && (num_records != 0)) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  ExpressionNode *root = this->root();
-  if (!root) {
-    return GRN_UNKNOWN_ERROR;
-  }
-  // FIXME: Records should be processed per block.
-  //        However, the contents of old blocks will be lost.
-  return root->evaluate(records, num_records, results);
-}
-
-ExpressionNode *Expression::root() const {
-  if (stack_.size() != 1) {
-    return NULL;
-  }
-  return stack_.front();
-}
-
-void Expression::update_type() {
-  ExpressionNode *root = this->root();
-  if (!root) {
-    type_ = GRN_TS_INCOMPLETE;
-  } else {
-    switch (root->type()) {
-      case GRN_TS_ID_NODE: {
-        type_ = GRN_TS_ID;
-        break;
-      }
-      case GRN_TS_SCORE_NODE: {
-        type_ = GRN_TS_SCORE;
-        break;
-      }
-      case GRN_TS_CONSTANT_NODE: {
-        type_ = GRN_TS_CONSTANT;
-        break;
-      }
-      case GRN_TS_COLUMN_NODE:
-      case GRN_TS_OPERATOR_NODE: {
-        type_ = GRN_TS_VARIABLE;
-        break;
-      }
-      default: {
-        type_ = GRN_TS_INCOMPLETE;
-        break;
-      }
-    }
-    output_type_ = data_type();
-  }
-}
-
-grn_rc Expression::push_constant_object(grn_obj *obj) {
-  ExpressionNode *node;
-  grn_rc rc = ConstantNode::open(ctx_, obj, &node);
-  if (rc == GRN_SUCCESS) try {
-    stack_.push_back(node);
-  } catch (const std::bad_alloc &) {
-    delete node;
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  return rc;
-}
-
-grn_rc Expression::push_column_object(grn_obj *obj) {
-  if ((obj->header.type == GRN_COLUMN_FIX_SIZE) ||
-      (obj->header.type == GRN_COLUMN_VAR_SIZE)) {
-    grn_obj *owner_table = grn_column_table(ctx_, obj);
-    if (owner_table != table_) {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-  ExpressionNode *node;
-  grn_rc rc = ColumnNode::open(ctx_, obj, &node);
-  if (rc == GRN_SUCCESS) try {
-    stack_.push_back(node);
-  } catch (const std::bad_alloc &) {
-    delete node;
-    return GRN_NO_MEMORY_AVAILABLE;
-  }
-  return rc;
-}
-
-grn_rc Expression::create_unary_node(OperatorType operator_type,
-  ExpressionNode *arg, ExpressionNode **node) {
-  grn_rc rc = GRN_SUCCESS;
-  switch (operator_type) {
-    case GRN_TS_LOGICAL_NOT: {
-      if (arg->data_kind() != GRN_TS_BOOL) {
-        return GRN_UNKNOWN_ERROR;
-      }
-      rc = LogicalNotNode::open(arg, node);
-      break;
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-  return rc;
-}
-
-grn_rc Expression::create_binary_node(OperatorType operator_type,
-  ExpressionNode *arg1, ExpressionNode *arg2, ExpressionNode **node) {
-  switch (operator_type) {
-    case GRN_TS_LOGICAL_AND: {
-      if ((arg1->data_kind() != GRN_TS_BOOL) ||
-          (arg1->data_kind() != GRN_TS_BOOL)) {
-        return GRN_INVALID_FORMAT;
-      }
-      return LogicalAndNode::open(arg1, arg2, node);
-    }
-    case GRN_TS_LOGICAL_OR: {
-      if ((arg1->data_kind() != GRN_TS_BOOL) ||
-          (arg1->data_kind() != GRN_TS_BOOL)) {
-        return GRN_INVALID_FORMAT;
-      }
-      return LogicalOrNode::open(arg1, arg2, node);
-    }
-//    case GRN_TS_EQUAL: {
-//      if (arg1->data_kind() != arg2->data_kind()) {
-//        return GRN_INVALID_FORMAT;
-//      }
-//      return EqualNode::open(arg1, arg2, node);
-//    }
-    case GRN_TS_EQUAL:
-    case GRN_TS_NOT_EQUAL:
-    case GRN_TS_LESS:
-    case GRN_TS_LESS_EQUAL:
-    case GRN_TS_GREATER:
-    case GRN_TS_GREATER_EQUAL: {
-      return ComparerNode::open(operator_type, arg1, arg2, node);
-    }
-    default: {
-      return GRN_INVALID_ARGUMENT;
-    }
-  }
-}
-
-}  // namespace ts
-}  // namespace grn
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static grn_rc
-grn_ts_select_filter(grn_ctx *ctx, grn_obj *table,
-                     const char *filter, size_t filter_size,
-                     int offset, int limit,
-                     std::vector<grn_ts_record> *records,
-                     size_t *num_hits) {
-  if (offset < 0) {
-    offset = 0;
-  }
-  if (limit < 0) {
-    limit = std::numeric_limits<int>::max();
-  }
-  grn::ts::Cursor *cursor;
-  grn_rc rc = grn::ts::Cursor::open_table_cursor(ctx, table, &cursor);
-  if (rc == GRN_SUCCESS) {
-    grn::ts::Expression *expression;
-    rc = grn::ts::Expression::parse(
-      ctx, table, filter, filter_size, &expression);
-    if (rc == GRN_SUCCESS) {
-      size_t count = 0;
-      for ( ; ; ) {
-        size_t records_offset = records->size();
-        try {
-          records->resize(records->size() + GRN_TS_MAX_BATCH_SIZE);
-        } catch (const std::bad_alloc &) {
-          rc = GRN_NO_MEMORY_AVAILABLE;
-          break;
-        }
-        size_t batch_size;
-        rc = cursor->read(&(*records)[records_offset],
-                          GRN_TS_MAX_BATCH_SIZE, &batch_size);
-        if (rc != GRN_SUCCESS) {
-          break;
-        }
-        if (batch_size == 0) {
-          records->resize(records_offset);
-          break;
-        }
-        rc = expression->filter(&(*records)[records_offset], batch_size,
-                                NULL, &batch_size);
-        if (rc != GRN_SUCCESS) {
-          break;
-        }
-        count += batch_size;
-        if (offset > 0) {
-          if (offset >= batch_size) {
-            offset -= batch_size;
-            batch_size = 0;
-          } else {
-            std::memcpy(&(*records)[0], &(*records)[offset],
-                        sizeof(grn_ts_record) * (batch_size - offset));
-            batch_size -= offset;
-            offset = 0;
-          }
-        }
-        if (limit >= batch_size) {
-          limit -= batch_size;
-        } else {
-          batch_size = limit;
-          limit = 0;
-        }
-        records->resize(records_offset + batch_size);
-      }
-      delete expression;
-      *num_hits = count;
-    }
-    delete cursor;
-  }
-  return rc;
-}
-
-static grn_rc
-grn_ts_select_output(grn_ctx *ctx, grn_obj *table,
-                     const char *output_columns, size_t output_columns_size,
-                     const grn_ts_record *records, size_t num_records,
-                     size_t num_hits) {
-  grn_rc rc = GRN_SUCCESS;
-  std::vector<std::string> names;
-  std::vector<grn::ts::Expression *> expressions;
-
-  const char *rest = output_columns;
-  size_t rest_size = output_columns_size;
-  while (rest_size != 0) {
-    size_t pos;
-    for (pos = 0; pos < rest_size; ++pos) {
-      if ((rest[pos] != ',') &&
-          !std::isspace(static_cast<unsigned char>(rest[pos]))) {
-        break;
-      }
-    }
-    if (pos >= rest_size) {
-      break;
-    }
-    rest += pos;
-    rest_size -= pos;
-    for (pos = 0; pos < rest_size; ++pos) {
-      if ((rest[pos] == ',') ||
-          std::isspace(static_cast<unsigned char>(rest[pos]))) {
-        break;
-      }
-    }
-    // TODO: Error handling.
-    std::string name(rest, pos);
-    if (name == "*") {
-      grn_hash *columns;
-      if ((columns = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
-                                     GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
-        if (grn_table_columns(ctx, table, "", 0, (grn_obj *)columns)) {
-          grn_id *key;
-          GRN_HASH_EACH(ctx, columns, id, &key, NULL, NULL, {
-            grn_obj *column = grn_ctx_at(ctx, *key);
-            if (column) {
-              char name_buf[1024];
-              int name_size = grn_column_name(
-                ctx, column, name_buf, sizeof(name_buf));
-              grn::ts::Expression *expression;
-              grn_rc r = grn::ts::Expression::open(ctx, table, &expression);
-              if (r == GRN_SUCCESS) {
-                r = expression->push_object(column);
-                if (r == GRN_SUCCESS) {
-                  names.push_back(std::string(name_buf, name_size));
-                  expressions.push_back(expression);
-                }
-              }
-            }
-          });
-        }
-        grn_hash_close(ctx, columns);
-      }
-    } else {
-      grn::ts::Expression *expression;
-      grn_rc r = grn::ts::Expression::parse(
-        ctx, table, rest, pos, &expression);
-      if (r == GRN_SUCCESS) {
-        names.push_back(name);
-        expressions.push_back(expression);
-      }
-    }
-    if (pos >= rest_size) {
-      break;
-    }
-    rest += pos + 1;
-    rest_size -= pos + 1;
-  }
-
-  GRN_OUTPUT_ARRAY_OPEN("RESULT", 1);
-  GRN_OUTPUT_ARRAY_OPEN("RESULTSET", 2 + num_records);
-  GRN_OUTPUT_ARRAY_OPEN("NHITS", 1);
-  grn_text_ulltoa(ctx, ctx->impl->outbuf, num_hits);
-  GRN_OUTPUT_ARRAY_CLOSE();  // NHITS.
-  GRN_OUTPUT_ARRAY_OPEN("COLUMNS", expressions.size());
-  for (size_t i = 0; i < expressions.size(); ++i) {
-    GRN_OUTPUT_ARRAY_OPEN("COLUMN", 2);
-    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '"');
-    GRN_TEXT_PUT(ctx, ctx->impl->outbuf, names[i].data(), names[i].size());
-    GRN_TEXT_PUT(ctx, ctx->impl->outbuf, "\",\"", 3);
-    switch (expressions[i]->output_type()) {
-      case GRN_DB_BOOL: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "Bool");
-        break;
-      }
-      case GRN_DB_INT8: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "Int8");
-        break;
-      }
-      case GRN_DB_INT16: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "Int16");
-        break;
-      }
-      case GRN_DB_INT32: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "Int32");
-        break;
-      }
-      case GRN_DB_INT64: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "Int64");
-        break;
-      }
-      case GRN_DB_UINT8: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "UInt8");
-        break;
-      }
-      case GRN_DB_UINT16: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "UInt16");
-        break;
-      }
-      case GRN_DB_UINT32: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "UInt32");
-        break;
-      }
-      case GRN_DB_UINT64: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "UInt64");
-        break;
-      }
-      case GRN_DB_FLOAT: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "Float");
-        break;
-      }
-      case GRN_DB_TIME: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "Time");
-        break;
-      }
-      case GRN_DB_SHORT_TEXT: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "ShortText");
-        break;
-      }
-      case GRN_DB_TEXT: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "Text");
-        break;
-      }
-      case GRN_DB_LONG_TEXT: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "LongText");
-        break;
-      }
-      case GRN_DB_TOKYO_GEO_POINT: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "TokyoGeoPoint");
-        break;
-      }
-      case GRN_DB_WGS84_GEO_POINT: {
-        GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, "WGS84GeoPoint");
-        break;
-      }
-      default: {
-        grn_obj *obj = grn_ctx_at(ctx, expressions[i]->output_type());
-        if (obj && grn_obj_is_table(ctx, obj)) {
-          char name[GRN_TABLE_MAX_KEY_SIZE];
-          int len = grn_obj_name(ctx, obj, name, sizeof(name));
-          GRN_TEXT_PUT(ctx, ctx->impl->outbuf, name, len);
-          grn_obj_unlink(ctx, obj);
-        }
-        break;
-      }
-    }
-    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '"');
-    GRN_OUTPUT_ARRAY_CLOSE();
-  }
-  GRN_OUTPUT_ARRAY_CLOSE();  // COLUMNS.
-  if (num_records != 0) {
-    size_t count = 0;
-    std::vector<std::vector<char> > bufs(expressions.size());
-    while (count < num_records) {
-      size_t batch_size = GRN_TS_MAX_BATCH_SIZE;
-      if (batch_size > (num_records - count)) {
-        batch_size = num_records - count;
-      }
-      for (size_t i = 0; i < expressions.size(); ++i) {
-        if (expressions[i]->dimension() == 0) {
-          switch (expressions[i]->data_kind()) {
-            case GRN_TS_BOOL: {
-              bufs[i].resize(sizeof(grn_ts_bool) * batch_size);
-              expressions[i]->evaluate(records + count, batch_size,
-                                       (grn::ts::Bool *)&bufs[i][0]);
-              break;
-            }
-            case GRN_TS_INT: {
-              bufs[i].resize(sizeof(grn_ts_int) * batch_size);
-              expressions[i]->evaluate(records + count, batch_size,
-                                       (grn::ts::Int *)&bufs[i][0]);
-              break;
-            }
-            case GRN_TS_FLOAT: {
-              bufs[i].resize(sizeof(grn_ts_float) * batch_size);
-              expressions[i]->evaluate(records + count, batch_size,
-                                       (grn::ts::Float *)&bufs[i][0]);
-              break;
-            }
-            case GRN_TS_TIME: {
-              bufs[i].resize(sizeof(grn_ts_time) * batch_size);
-              expressions[i]->evaluate(records + count, batch_size,
-                                       (grn::ts::Time *)&bufs[i][0]);
-              break;
-            }
-            case GRN_TS_TEXT: {
-              bufs[i].resize(sizeof(grn_ts_text) * batch_size);
-              expressions[i]->evaluate(records + count, batch_size,
-                                       (grn::ts::Text *)&bufs[i][0]);
-              break;
-            }
-            case GRN_TS_GEO_POINT: {
-              bufs[i].resize(sizeof(grn_ts_geo_point) * batch_size);
-              expressions[i]->evaluate(records + count, batch_size,
-                                       (grn::ts::GeoPoint *)&bufs[i][0]);
-              break;
-            }
-            default: {
-              break;
-            }
-          }
-        } else {
-          switch (expressions[i]->data_kind()) {
-            case GRN_TS_BOOL: {
-              bufs[i].resize(sizeof(grn_ts_vector) * batch_size);
-              expressions[i]->evaluate(records + count, batch_size,
-                                       (grn_ts_vector *)&bufs[i][0]);
-              break;
-            }
-            case GRN_TS_INT: {
-              bufs[i].resize(sizeof(grn_ts_vector) * batch_size);
-              expressions[i]->evaluate(records + count, batch_size,
-                                       (grn_ts_vector *)&bufs[i][0]);
-              break;
-            }
-            case GRN_TS_FLOAT: {
-              bufs[i].resize(sizeof(grn_ts_vector) * batch_size);
-              expressions[i]->evaluate(records + count, batch_size,
-                                       (grn_ts_vector *)&bufs[i][0]);
-              break;
-            }
-            case GRN_TS_TIME: {
-              bufs[i].resize(sizeof(grn_ts_vector) * batch_size);
-              expressions[i]->evaluate(records + count, batch_size,
-                                       (grn_ts_vector *)&bufs[i][0]);
-              break;
-            }
-            case GRN_TS_TEXT: {
-              bufs[i].resize(sizeof(grn_ts_vector) * batch_size);
-              expressions[i]->evaluate(records + count, batch_size,
-                                       (grn_ts_vector *)&bufs[i][0]);
-              break;
-            }
-            case GRN_TS_GEO_POINT: {
-              bufs[i].resize(sizeof(grn_ts_vector) * batch_size);
-              expressions[i]->evaluate(records + count, batch_size,
-                                       (grn_ts_vector *)&bufs[i][0]);
-              break;
-            }
-            default: {
-              break;
-            }
-          }
-        }
-      }
-      for (size_t i = 0; i < batch_size; ++i) {
-        GRN_OUTPUT_ARRAY_OPEN("HIT", expressions.size());
-        for (size_t j = 0; j < expressions.size(); ++j) {
-          if (j != 0) {
-            GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ',');
-          }
-          if (expressions[j]->dimension() == 0) {
-            switch (expressions[j]->data_kind()) {
-              case GRN_TS_BOOL: {
-                if (((grn_ts_bool *)&bufs[j][0])[i]) {
-                  GRN_TEXT_PUT(ctx, ctx->impl->outbuf, "true", 4);
-                } else {
-                  GRN_TEXT_PUT(ctx, ctx->impl->outbuf, "false", 5);
-                }
-                break;
-              }
-              case GRN_TS_INT: {
-                grn_text_lltoa(ctx, ctx->impl->outbuf,
-                               ((grn_ts_int *)&bufs[j][0])[i]);
-                break;
-              }
-              case GRN_TS_FLOAT: {
-                grn_text_ftoa(ctx, ctx->impl->outbuf,
-                              ((grn_ts_float *)&bufs[j][0])[i]);
-                break;
-              }
-              case GRN_TS_TIME: {
-                grn_text_ftoa(ctx, ctx->impl->outbuf,
-                              ((grn_ts_time *)&bufs[j][0])[i] * 0.000001);
-                break;
-              }
-              case GRN_TS_TEXT: {
-                grn_ts_text text = ((grn_ts_text *)&bufs[j][0])[i];
-                grn_text_esc(ctx, ctx->impl->outbuf, text.ptr, text.size);
-                break;
-              }
-              case GRN_TS_GEO_POINT: {
-                grn_ts_geo_point geo_point =
-                  ((grn_ts_geo_point *)&bufs[j][0])[i];
-                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '"');
-                grn_text_itoa(ctx, ctx->impl->outbuf, geo_point.latitude);
-                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, 'x');
-                grn_text_itoa(ctx, ctx->impl->outbuf, geo_point.longitude);
-                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '"');
-                break;
-              }
-              default: {
-                break;
-              }
-            }
-          } else {
-            grn_ts_vector vector =
-              reinterpret_cast<grn_ts_vector *>(&bufs[j][0])[i];
-            switch (expressions[j]->data_kind()) {
-              case GRN_TS_BOOL: {
-                const grn_ts_bool *ptr =
-                  static_cast<const grn_ts_bool *>(vector.ptr);
-                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '[');
-                for (size_t k = 0; k < vector.size; ++k) {
-                  if (k != 0) {
-                    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ',');
-                  }
-                  if (ptr[k]) {
-                    GRN_TEXT_PUT(ctx, ctx->impl->outbuf, "true", 4);
-                  } else {
-                    GRN_TEXT_PUT(ctx, ctx->impl->outbuf, "false", 5);
-                  }
-                }
-                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ']');
-                break;
-              }
-              case GRN_TS_INT: {
-                const grn_ts_int *ptr =
-                  static_cast<const grn_ts_int *>(vector.ptr);
-                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '[');
-                for (size_t k = 0; k < vector.size; ++k) {
-                  if (k != 0) {
-                    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ',');
-                  }
-                  grn_text_lltoa(ctx, ctx->impl->outbuf, ptr[k]);
-                }
-                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ']');
-                break;
-              }
-              case GRN_TS_FLOAT: {
-                const grn_ts_float *ptr =
-                  static_cast<const grn_ts_float *>(vector.ptr);
-                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '[');
-                for (size_t k = 0; k < vector.size; ++k) {
-                  if (k != 0) {
-                    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ',');
-                  }
-                  grn_text_ftoa(ctx, ctx->impl->outbuf, ptr[k]);
-                }
-                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ']');
-                break;
-              }
-              case GRN_TS_TIME: {
-                const grn_ts_time *ptr =
-                  static_cast<const grn_ts_time *>(vector.ptr);
-                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '[');
-                for (size_t k = 0; k < vector.size; ++k) {
-                  if (k != 0) {
-                    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ',');
-                  }
-                  grn_text_ftoa(ctx, ctx->impl->outbuf, ptr[k] * 0.000001);
-                }
-                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ']');
-                break;
-              }
-              case GRN_TS_TEXT: {
-                const grn_ts_text *ptr =
-                  static_cast<const grn_ts_text *>(vector.ptr);
-                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '[');
-                for (size_t k = 0; k < vector.size; ++k) {
-                  if (k != 0) {
-                    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ',');
-                  }
-                  grn_text_esc(ctx, ctx->impl->outbuf, ptr[k].ptr, ptr[k].size);
-                }
-                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ']');
-                break;
-              }
-              case GRN_TS_GEO_POINT: {
-                const grn_ts_geo_point *ptr =
-                  static_cast<const grn_ts_geo_point *>(vector.ptr);
-                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '[');
-                for (size_t k = 0; k < vector.size; ++k) {
-                  if (k != 0) {
-                    GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ',');
-                  }
-                  GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '"');
-                  grn_text_itoa(ctx, ctx->impl->outbuf, ptr[k].latitude);
-                  GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, 'x');
-                  grn_text_itoa(ctx, ctx->impl->outbuf, ptr[k].longitude);
-                  GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, '"');
-                }
-                GRN_TEXT_PUTC(ctx, ctx->impl->outbuf, ']');
-                break;
-              }
-              default: {
-                break;
-              }
-            }
-          }
-        }
-        GRN_OUTPUT_ARRAY_CLOSE();  // HITS.
-      }
-      count += batch_size;
-    }
-  }
-  GRN_OUTPUT_ARRAY_CLOSE();  // RESULTSET.
-  GRN_OUTPUT_ARRAY_CLOSE();  // RESET.
-  for (size_t i = 0; i < expressions.size(); ++i) {
-    delete expressions[i];
-  }
-  return rc;
-}
-
-grn_rc
-grn_ts_select(grn_ctx *ctx, grn_obj *table,
-              const char *filter, size_t filter_size,
-              const char *output_columns, size_t output_columns_size,
-              int offset, int limit) {
-  if (!ctx || !grn_obj_is_table(ctx, table) ||
-      (!filter && (filter_size != 0)) ||
-      (!output_columns && (output_columns_size != 0))) {
-    return GRN_INVALID_ARGUMENT;
-  }
-  std::vector<grn_ts_record> records;
-  size_t num_hits;
-  grn_rc rc = grn_ts_select_filter(ctx, table, filter, filter_size,
-                                   offset, limit, &records, &num_hits);
-  if (rc == GRN_SUCCESS) {
-    rc = grn_ts_select_output(ctx, table, output_columns, output_columns_size,
-                              &*records.begin(), records.size(), num_hits);
-  }
-  return rc;
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif  // GRN_WITH_TS




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