<commit_before>/* -*- 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_dat.h"
#include "grn_db.h"
#include "grn_geo.h"
#include "grn_hash.h"
#include "grn_output.h"
#include "grn_pat.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_ja_get_value() appends a value into buf. */
static grn_rc
grn_ts_ja_get_value(grn_ctx *ctx, grn_ja *ja, grn_id id,
                    grn_obj *buf, size_t *value_size) {
  grn_rc rc, tmp_rc;
  uint32_t size;
  grn_io_win iw;
  char *ptr = (char *)grn_ja_ref(ctx, ja, id, &iw, &size);
  if (!ptr) {
    if (value_size) {
      *value_size = 0;
    }
    return GRN_SUCCESS;
  }
  rc = grn_bulk_write(ctx, buf, ptr, size);
  tmp_rc = grn_ja_unref(ctx, &iw);
  if (rc == GRN_SUCCESS) {
    if (tmp_rc == GRN_SUCCESS) {
      if (value_size) {
        *value_size = size;
      }
    } else {
      /* Discard the value read. */
      grn_bulk_resize(ctx, buf, GRN_BULK_VSIZE(buf) - size);
      rc = tmp_rc;
    }
  }
  return rc;
}

/* 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;
}

#define GRN_TS_TABLE_GET_KEY_CASE_BLOCK(TYPE, type)\
  case GRN_TABLE_ ## TYPE ## _KEY: {\
    uint32_t key_size;\
    grn_ ## type *type = (grn_ ## type *)table;\
    const void *key = _grn_ ## type ## _key(ctx, type, id, &key_size);\
    if (size) {\
      *size = key_size;\
    }\
    return key;\
  }
/* grn_ts_table_get_key() gets a reference to a key (_key). */
static const void *
grn_ts_table_get_key(grn_ctx *ctx, grn_obj *table, grn_id id, size_t *size) {
  switch (table->header.type) {
    GRN_TS_TABLE_GET_KEY_CASE_BLOCK(HASH, hash)
    GRN_TS_TABLE_GET_KEY_CASE_BLOCK(PAT, pat)
    GRN_TS_TABLE_GET_KEY_CASE_BLOCK(DAT, dat)
    /* GRN_TABLE_NO_KEY does not support _key. */
    default: {
      return NULL;
    }
  }
}
#undef GRN_TS_TABLE_GET_KEY_CASE_BLOCK

/* grn_ts_table_get_value() gets a reference to a value (_value). */
static const void *
grn_ts_table_get_value(grn_ctx *ctx, grn_obj *table, grn_id id) {
  switch (table->header.type) {
    case GRN_TABLE_HASH_KEY: {
      uint32_t size;
      return grn_hash_get_value_(ctx, (grn_hash *)table, id, &size);
    }
    case GRN_TABLE_PAT_KEY: {
      uint32_t size;
      return grn_pat_get_value_(ctx, (grn_pat *)table, id, &size);
    }
    /* GRN_TABLE_DAT_KEY does not support _value. */
    case GRN_TABLE_NO_KEY: {
      return _grn_array_get_value(ctx, (grn_array *)table, id);
    }
    default: {
      return NULL;
    }
  }
}

/*-------------------------------------------------------------
 * 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;
}

#define GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(TYPE)\
  case GRN_DB_ ## TYPE: {\
    GRN_ ## TYPE ## _INIT(&new_node->buf, GRN_OBJ_VECTOR);\
    break;\
  }
/* 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;
  if (new_node->data_kind & GRN_TS_VECTOR_FLAG) {
    switch (new_node->data_type) {
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(BOOL)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(INT8)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(INT16)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(INT32)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(INT64)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(UINT8)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(UINT16)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(UINT32)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(UINT64)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(FLOAT)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(TIME)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(SHORT_TEXT)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(TEXT)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(LONG_TEXT)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(TOKYO_GEO_POINT)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(WGS84_GEO_POINT)
      default: {
        break;
      }
    }
  } else {
    GRN_TEXT_INIT(&new_node->buf, 0);
  }
  new_node->body.buf = NULL;
  *node = (grn_ts_expr_node *)new_node;
  return GRN_SUCCESS;
}
#undef GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK

/* 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++) {\
      const grn_ts_ ## kind *key;\
      size_t key_size;\
      key = (const grn_ts_ ## kind *)grn_ts_table_get_key(ctx, node->table,\
                                                          in[i].id,\
                                                          &key_size);\
      if (key && (key_size == sizeof(*key))) {\
        out_ptr[i] = *key;\
      } else {\
        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: {\
    size_t i;\
    grn_ts_int *out_ptr = (grn_ts_int *)out;\
    for (i = 0; i < n_in; i++) {\
      const type ## _t *key;\
      size_t key_size;\
      key = (const type ## _t *)grn_ts_table_get_key(ctx, node->table,\
                                                     in[i].id, &key_size);\
      if (key && (key_size == sizeof(*key))) {\
        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. */
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: {
      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++) {
        const char *key;
        size_t key_size;
        key = (const char *)grn_ts_table_get_key(ctx, node->table, in[i].id,
                                                 &key_size);
        if (key && key_size) {
          grn_rc rc = grn_bulk_write(ctx, &node->buf, key, key_size);
          if (rc == GRN_SUCCESS) {
            out_ptr[i].size = key_size;
          }
        }
      }
      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++) {
        const grn_ts_id *id;
        size_t key_size;
        id = (const grn_ts_id *)grn_ts_table_get_key(ctx, node->table,
                                                     in[i].id, &key_size);
        if (id && (key_size == sizeof(*id))) {
          out_ptr[i].id = *id;
          out_ptr[i].score = in[i].score;
        } else {
          out_ptr[i] = grn_ts_ref_zero();
        }
      }
      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++) {\
      const grn_ts_ ## kind *value;\
      value = (const grn_ts_ ## kind *)grn_ts_table_get_value(ctx,\
                                                              node->table,\
                                                              in[i].id);\
      out_ptr[i] = value ? *value : grn_ts_ ## kind ## _zero();\
    }\
    return GRN_SUCCESS;\
  }
#define GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE_BLOCK(TYPE, type)\
  case GRN_DB_ ## TYPE: {\
    size_t i;\
    grn_ts_int *out_ptr = (grn_ts_int *)out;\
    for (i = 0; i < n_in; i++) {\
      const type ## _t *value;\
      value = (const type ## _t *)grn_ts_table_get_value(ctx, node->table,\
                                                         in[i].id);\
      out_ptr[i] = value ? (grn_ts_int)*value : grn_ts_int_zero();\
    }\
    return GRN_SUCCESS;\
  }
/* grn_ts_expr_value_node_evaluate() outputs values. */
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: {
      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++) {
        const grn_ts_id *value;
        value = (const grn_ts_id *)grn_ts_table_get_value(ctx, node->table,
                                                          in[i].id);
        if (value) {
          out_ptr[i].id = *value;
          out_ptr[i].score = in[i].score;
        } else {
          out_ptr[i] = grn_ts_ref_zero();
        }
      }
      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. */
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, size;
      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++) {
        grn_rc rc = grn_ts_ja_get_value(ctx, ja, in[i].id, &node->buf, &size);
        out_ptr[i].size = (rc == GRN_SUCCESS) ? 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;
    }<commit_msg>grn_ts: use grn_ts_ja_get_value() instead of grn_obj_get_value()<commit_after>/* -*- 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_dat.h"
#include "grn_db.h"
#include "grn_geo.h"
#include "grn_hash.h"
#include "grn_output.h"
#include "grn_pat.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_ja_get_value() appends a value into buf. */
static grn_rc
grn_ts_ja_get_value(grn_ctx *ctx, grn_ja *ja, grn_id id,
                    grn_obj *buf, size_t *value_size) {
  grn_rc rc, tmp_rc;
  uint32_t size;
  grn_io_win iw;
  char *ptr = (char *)grn_ja_ref(ctx, ja, id, &iw, &size);
  if (!ptr) {
    if (value_size) {
      *value_size = 0;
    }
    return GRN_SUCCESS;
  }
  rc = grn_bulk_write(ctx, buf, ptr, size);
  tmp_rc = grn_ja_unref(ctx, &iw);
  if (rc == GRN_SUCCESS) {
    if (tmp_rc == GRN_SUCCESS) {
      if (value_size) {
        *value_size = size;
      }
    } else {
      /* Discard the value read. */
      grn_bulk_resize(ctx, buf, GRN_BULK_VSIZE(buf) - size);
      rc = tmp_rc;
    }
  }
  return rc;
}

/* 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;
}

#define GRN_TS_TABLE_GET_KEY_CASE_BLOCK(TYPE, type)\
  case GRN_TABLE_ ## TYPE ## _KEY: {\
    uint32_t key_size;\
    grn_ ## type *type = (grn_ ## type *)table;\
    const void *key = _grn_ ## type ## _key(ctx, type, id, &key_size);\
    if (size) {\
      *size = key_size;\
    }\
    return key;\
  }
/* grn_ts_table_get_key() gets a reference to a key (_key). */
static const void *
grn_ts_table_get_key(grn_ctx *ctx, grn_obj *table, grn_id id, size_t *size) {
  switch (table->header.type) {
    GRN_TS_TABLE_GET_KEY_CASE_BLOCK(HASH, hash)
    GRN_TS_TABLE_GET_KEY_CASE_BLOCK(PAT, pat)
    GRN_TS_TABLE_GET_KEY_CASE_BLOCK(DAT, dat)
    /* GRN_TABLE_NO_KEY does not support _key. */
    default: {
      return NULL;
    }
  }
}
#undef GRN_TS_TABLE_GET_KEY_CASE_BLOCK

/* grn_ts_table_get_value() gets a reference to a value (_value). */
static const void *
grn_ts_table_get_value(grn_ctx *ctx, grn_obj *table, grn_id id) {
  switch (table->header.type) {
    case GRN_TABLE_HASH_KEY: {
      uint32_t size;
      return grn_hash_get_value_(ctx, (grn_hash *)table, id, &size);
    }
    case GRN_TABLE_PAT_KEY: {
      uint32_t size;
      return grn_pat_get_value_(ctx, (grn_pat *)table, id, &size);
    }
    /* GRN_TABLE_DAT_KEY does not support _value. */
    case GRN_TABLE_NO_KEY: {
      return _grn_array_get_value(ctx, (grn_array *)table, id);
    }
    default: {
      return NULL;
    }
  }
}

/*-------------------------------------------------------------
 * 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;
}

#define GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(TYPE)\
  case GRN_DB_ ## TYPE: {\
    GRN_ ## TYPE ## _INIT(&new_node->buf, GRN_OBJ_VECTOR);\
    break;\
  }
/* 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;
  if (new_node->data_kind & GRN_TS_VECTOR_FLAG) {
    switch (new_node->data_type) {
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(BOOL)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(INT8)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(INT16)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(INT32)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(INT64)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(UINT8)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(UINT16)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(UINT32)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(UINT64)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(FLOAT)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(TIME)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(SHORT_TEXT)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(TEXT)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(LONG_TEXT)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(TOKYO_GEO_POINT)
      GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK(WGS84_GEO_POINT)
      default: {
        break;
      }
    }
  } else {
    GRN_TEXT_INIT(&new_node->buf, 0);
  }
  new_node->body.buf = NULL;
  *node = (grn_ts_expr_node *)new_node;
  return GRN_SUCCESS;
}
#undef GRN_TS_EXPR_COLUMN_NODE_OPEN_CASE_BLOCK

/* 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++) {\
      const grn_ts_ ## kind *key;\
      size_t key_size;\
      key = (const grn_ts_ ## kind *)grn_ts_table_get_key(ctx, node->table,\
                                                          in[i].id,\
                                                          &key_size);\
      if (key && (key_size == sizeof(*key))) {\
        out_ptr[i] = *key;\
      } else {\
        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: {\
    size_t i;\
    grn_ts_int *out_ptr = (grn_ts_int *)out;\
    for (i = 0; i < n_in; i++) {\
      const type ## _t *key;\
      size_t key_size;\
      key = (const type ## _t *)grn_ts_table_get_key(ctx, node->table,\
                                                     in[i].id, &key_size);\
      if (key && (key_size == sizeof(*key))) {\
        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. */
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: {
      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++) {
        const char *key;
        size_t key_size;
        key = (const char *)grn_ts_table_get_key(ctx, node->table, in[i].id,
                                                 &key_size);
        if (key && key_size) {
          grn_rc rc = grn_bulk_write(ctx, &node->buf, key, key_size);
          if (rc == GRN_SUCCESS) {
            out_ptr[i].size = key_size;
          }
        }
      }
      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++) {
        const grn_ts_id *id;
        size_t key_size;
        id = (const grn_ts_id *)grn_ts_table_get_key(ctx, node->table,
                                                     in[i].id, &key_size);
        if (id && (key_size == sizeof(*id))) {
          out_ptr[i].id = *id;
          out_ptr[i].score = in[i].score;
        } else {
          out_ptr[i] = grn_ts_ref_zero();
        }
      }
      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++) {\
      const grn_ts_ ## kind *value;\
      value = (const grn_ts_ ## kind *)grn_ts_table_get_value(ctx,\
                                                              node->table,\
                                                              in[i].id);\
      out_ptr[i] = value ? *value : grn_ts_ ## kind ## _zero();\
    }\
    return GRN_SUCCESS;\
  }
#define GRN_TS_EXPR_VALUE_NODE_EVALUATE_INT_CASE_BLOCK(TYPE, type)\
  case GRN_DB_ ## TYPE: {\
    size_t i;\
    grn_ts_int *out_ptr = (grn_ts_int *)out;\
    for (i = 0; i < n_in; i++) {\
      const type ## _t *value;\
      value = (const type ## _t *)grn_ts_table_get_value(ctx, node->table,\
                                                         in[i].id);\
      out_ptr[i] = value ? (grn_ts_int)*value : grn_ts_int_zero();\
    }\
    return GRN_SUCCESS;\
  }
/* grn_ts_expr_value_node_evaluate() outputs values. */
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: {
      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++) {
        const grn_ts_id *value;
        value = (const grn_ts_id *)grn_ts_table_get_value(ctx, node->table,
                                                          in[i].id);
        if (value) {
          out_ptr[i].id = *value;
          out_ptr[i].score = in[i].score;
        } else {
          out_ptr[i] = grn_ts_ref_zero();
        }
      }
      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. */
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, size;
      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++) {
        grn_rc rc = grn_ts_ja_get_value(ctx, ja, in[i].id, &node->buf, &size);
        out_ptr[i].size = (rc == GRN_SUCCESS) ? 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;\
    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++) {\
      size_t size;\
      grn_rc rc = grn_ts_ja_get_value(ctx, (grn_ja *)node->column, in[i].id,\
                                      &node->buf, &size);\
      if (rc == GRN_SUCCESS) {\
        out_ptr[i].size = size / sizeof(grn_ts_ ## kind);\
      } else {\
        out_ptr[i].size = 0;\
      }\
    }\
    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_rc rc;\
      size_t size;\
      GRN_BULK_REWIND(&src_buf);\
      rc = grn_ts_ja_get_value(ctx, (grn_ja *)node->column, in[i].id,\
                               &src_buf, &size);\
      if (rc == GRN_SUCCESS) {\
        type ## _t *src_ptr = (type ## _t *)GRN_BULK_HEAD(&src_buf);\
        out_ptr[i].size = size / 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 {\
        out_ptr[i].size = 0;\
      }\
    }\
    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++) {
        // FIXME: Errors are ignored.
        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++) {
        size_t size;
        grn_rc rc;
        rc = grn_ts_ja_get_value(ctx, (grn_ja *)node->column, in[i].id,
                                 &node->buf, &size);
        if (rc == GRN_SUCCESS) {
          out_ptr[i].size = size / sizeof(grn_ts_id);
          offset += out_ptr[i].size;
        } else {
          out_ptr[i].size = 0;
        }
      }
      /* 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) {
          for (i = 0; i < n_in; i++) {
            out_ptr[i] = grn_ts_ref_vector_zero();
          }
          return GRN_SUCCESS;
        }
        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;
    }
{'fraction_non_alphanumeric': 0.10857302742025741, 'fraction_numerical': 0.005763850027979855, 'mean_word_length': 2.95358407079646, 'pattern_counts': {'":': 0, '<': 78, '<?xml version=': 0, '>': 295, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 68, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2379806', 'n_tokens_mistral': 36514, 'n_tokens_neox': 36146, 'n_words': 6101}