/* -*- c-basic-offset: 2 -*- */
/*
  Copyright(C) 2010-2018 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
*/

#include "grn.h"
#include "grn_ctx_impl.h"
#include "grn_expr.h"
#include "grn_expr_code.h"
#include "grn_expr_executor.h"
#include "grn_geo.h"

#ifdef GRN_SUPPORT_REGEXP
# include "grn_normalizer.h"
# include "grn_onigmo.h"
#endif

static void
grn_expr_executor_init_general(grn_ctx *ctx,
                               grn_expr_executor *executor)
{
}

#define EXPRVP(x) ((x)->header.impl_flags & GRN_OBJ_EXPRVALUE)

#define WITH_SPSAVE(block) do {\
  ctx->impl->stack_curr = sp - ctx->impl->stack;\
  e->values_curr = vp - e->values;\
  block\
  vp = e->values + e->values_curr;\
  sp = ctx->impl->stack + ctx->impl->stack_curr;\
  s_ = ctx->impl->stack;\
  s0 = (sp > s_) ? sp[-1] : NULL;\
  s1 = (sp > s_ + 1) ? sp[-2] : NULL;\
} while (0)

#define GEO_RESOLUTION   3600000
#define GEO_RADIOUS      6357303
#define GEO_BES_C1       6334834
#define GEO_BES_C2       6377397
#define GEO_BES_C3       0.006674
#define GEO_GRS_C1       6335439
#define GEO_GRS_C2       6378137
#define GEO_GRS_C3       0.006694

#define VAR_SET_VALUE(ctx,var,value) do {\
  if (GRN_DB_OBJP(value)) {\
    (var)->header.type = GRN_PTR;\
    (var)->header.domain = DB_OBJ(value)->id;\
    GRN_PTR_SET(ctx, (var), (value));\
  } else {\
    (var)->header.type = (value)->header.type;\
    (var)->header.domain = (value)->header.domain;\
    GRN_TEXT_SET(ctx, (var), GRN_TEXT_VALUE(value), GRN_TEXT_LEN(value));\
  }\
} while (0)

#define PUSH1(v) do {\
  if (EXPRVP(v)) {\
    vp++;\
    if (vp - e->values > e->values_tail) { e->values_tail = vp - e->values; }\
  }\
  s1 = s0;\
  if (sp >= s_ + ctx->impl->stack_size) {\
    if (grn_ctx_expand_stack(ctx) != GRN_SUCCESS) {\
      goto exit;\
    }\
    sp += (ctx->impl->stack - s_);\
    s_ = ctx->impl->stack;\
  }\
  *sp++ = s0 = v;\
} while (0)

#define POP1(v) do {\
  if (EXPRVP(s0)) { vp--; }\
  v = s0;\
  s0 = s1;\
  sp--;\
  if (sp < s_) { ERR(GRN_INVALID_ARGUMENT, "stack underflow"); goto exit; }\
  s1 = (sp > s_ - 1) ? sp[-2] : NULL;\
} while (0)

#define ALLOC1(value) do {\
  s1 = s0;\
  if (sp >= s_ + ctx->impl->stack_size) {\
    if (grn_ctx_expand_stack(ctx) != GRN_SUCCESS) {\
      goto exit;\
    }\
    sp += (ctx->impl->stack - s_);\
    s_ = ctx->impl->stack;\
  }\
  *sp++ = s0 = value = vp++;\
  if (vp - e->values > e->values_tail) { e->values_tail = vp - e->values; }\
} while (0)

#define POP1ALLOC1(arg,value) do {\
  arg = s0;\
  if (EXPRVP(s0)) {\
    value = s0;\
  } else {\
    if (sp < s_ + 1) { ERR(GRN_INVALID_ARGUMENT, "stack underflow"); goto exit; }\
    sp[-1] = s0 = value = vp++;\
    if (vp - e->values > e->values_tail) { e->values_tail = vp - e->values; }\
    s0->header.impl_flags |= GRN_OBJ_EXPRVALUE;\
  }\
} while (0)

#define POP2ALLOC1(arg1,arg2,value) do {\
  if (EXPRVP(s0)) { vp--; }\
  if (EXPRVP(s1)) { vp--; }\
  arg2 = s0;\
  arg1 = s1;\
  sp--;\
  if (sp < s_ + 1) { ERR(GRN_INVALID_ARGUMENT, "stack underflow"); goto exit; }\
  s1 = (sp > s_ + 1) ? sp[-2] : NULL;\
  sp[-1] = s0 = value = vp++;\
  if (vp - e->values > e->values_tail) { e->values_tail = vp - e->values; }\
  s0->header.impl_flags |= GRN_OBJ_EXPRVALUE;\
} while (0)

#define INTEGER_ARITHMETIC_OPERATION_PLUS(x, y) ((x) + (y))
#define FLOAT_ARITHMETIC_OPERATION_PLUS(x, y) ((double)(x) + (double)(y))
#define INTEGER_ARITHMETIC_OPERATION_MINUS(x, y) ((x) - (y))
#define FLOAT_ARITHMETIC_OPERATION_MINUS(x, y) ((double)(x) - (double)(y))
#define INTEGER_ARITHMETIC_OPERATION_STAR(x, y) ((x) * (y))
#define FLOAT_ARITHMETIC_OPERATION_STAR(x, y) ((double)(x) * (double)(y))
#define INTEGER_ARITHMETIC_OPERATION_SLASH(x, y) ((x) / (y))
#define FLOAT_ARITHMETIC_OPERATION_SLASH(x, y) ((double)(x) / (double)(y))
#define INTEGER_ARITHMETIC_OPERATION_MOD(x, y) ((x) % (y))
#define FLOAT_ARITHMETIC_OPERATION_MOD(x, y) (fmod((x), (y)))
#define INTEGER_ARITHMETIC_OPERATION_SHIFTL(x, y) ((x) << (y))
#define FLOAT_ARITHMETIC_OPERATION_SHIFTL(x, y)                         \
  ((long long int)(x) << (long long int)(y))
#define INTEGER_ARITHMETIC_OPERATION_SHIFTR(x, y) ((x) >> (y))
#define FLOAT_ARITHMETIC_OPERATION_SHIFTR(x, y)                         \
  ((long long int)(x) >> (long long int)(y))
#define INTEGER8_ARITHMETIC_OPERATION_SHIFTRR(x, y)             \
  ((uint8_t)(x) >> (y))
#define INTEGER16_ARITHMETIC_OPERATION_SHIFTRR(x, y)            \
  ((uint16_t)(x) >> (y))
#define INTEGER32_ARITHMETIC_OPERATION_SHIFTRR(x, y)            \
  ((unsigned int)(x) >> (y))
#define INTEGER64_ARITHMETIC_OPERATION_SHIFTRR(x, y)            \
  ((long long unsigned int)(x) >> (y))
#define FLOAT_ARITHMETIC_OPERATION_SHIFTRR(x, y)                \
  ((long long unsigned int)(x) >> (long long unsigned int)(y))

#define INTEGER_ARITHMETIC_OPERATION_BITWISE_OR(x, y) ((x) | (y))
#define FLOAT_ARITHMETIC_OPERATION_BITWISE_OR(x, y)                \
  ((long long int)(x) | (long long int)(y))
#define INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR(x, y) ((x) ^ (y))
#define FLOAT_ARITHMETIC_OPERATION_BITWISE_XOR(x, y)                \
  ((long long int)(x) ^ (long long int)(y))
#define INTEGER_ARITHMETIC_OPERATION_BITWISE_AND(x, y) ((x) & (y))
#define FLOAT_ARITHMETIC_OPERATION_BITWISE_AND(x, y)                \
  ((long long int)(x) & (long long int)(y))

#define INTEGER_UNARY_ARITHMETIC_OPERATION_MINUS(x) (-(x))
#define FLOAT_UNARY_ARITHMETIC_OPERATION_MINUS(x) (-(x))
#define INTEGER_UNARY_ARITHMETIC_OPERATION_BITWISE_NOT(x) (~(x))
#define FLOAT_UNARY_ARITHMETIC_OPERATION_BITWISE_NOT(x) \
  (~((long long int)(x)))

#define TEXT_ARITHMETIC_OPERATION(operator) do {                        \
  long long int x_;                                                     \
  long long int y_;                                                     \
                                                                        \
  res->header.domain = GRN_DB_INT64;                                    \
                                                                        \
  GRN_INT64_SET(ctx, res, 0);                                           \
  grn_obj_cast(ctx, x, res, GRN_FALSE);                                 \
  x_ = GRN_INT64_VALUE(res);                                            \
                                                                        \
  GRN_INT64_SET(ctx, res, 0);                                           \
  grn_obj_cast(ctx, y, res, GRN_FALSE);                                 \
  y_ = GRN_INT64_VALUE(res);                                            \
                                                                        \
  GRN_INT64_SET(ctx, res, x_ operator y_);                              \
} while (0)

#define TEXT_UNARY_ARITHMETIC_OPERATION(unary_operator) do {    \
  grn_obj buffer;                                               \
  int64_t value;                                                \
  GRN_INT64_INIT(&buffer, 0);                                   \
  grn_obj_cast(ctx, x, &buffer, GRN_FALSE);                     \
  grn_obj_reinit(ctx, res, GRN_DB_INT64, 0);                    \
  value = GRN_INT64_VALUE(&buffer);                             \
  GRN_INT64_SET(ctx, res, unary_operator value);                \
  GRN_OBJ_FIN(ctx, &buffer);                                    \
} while (0)

#define ARITHMETIC_OPERATION_NO_CHECK(y) do {} while (0)
#define ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y) do {        \
  if ((long long int)y == 0) {                                  \
    ERR(GRN_INVALID_ARGUMENT, "divisor should not be 0");       \
    goto exit;                                                  \
  }                                                             \
} while (0)


#define NUMERIC_ARITHMETIC_OPERATION_DISPATCH(set, get, x_, y,          \
                                              res, res_domain,          \
                                              integer_operation,        \
                                              float_operation,          \
                                              right_expression_check,   \
                                              invalid_type_error) do {  \
  switch (y->header.domain) {                                           \
  case GRN_DB_BOOL :                                                    \
    {                                                                   \
      uint8_t y_;                                                       \
      y_ = GRN_BOOL_VALUE(y) ? 1 : 0;                                   \
      right_expression_check(y_);                                       \
      grn_obj_reinit(ctx, res, res_domain, 0);                          \
      set(ctx, res, integer_operation(x_, y_));                         \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT8 :                                                    \
    {                                                                   \
      int8_t y_;                                                        \
      y_ = GRN_INT8_VALUE(y);                                           \
      right_expression_check(y_);                                       \
      grn_obj_reinit(ctx, res, res_domain, 0);                          \
      set(ctx, res, integer_operation(x_, y_));                         \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT8 :                                                   \
    {                                                                   \
      uint8_t y_;                                                       \
      y_ = GRN_UINT8_VALUE(y);                                          \
      right_expression_check(y_);                                       \
      grn_obj_reinit(ctx, res, res_domain, 0);                          \
      set(ctx, res, integer_operation(x_, y_));                         \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT16 :                                                   \
    {                                                                   \
      int16_t y_;                                                       \
      y_ = GRN_INT16_VALUE(y);                                          \
      right_expression_check(y_);                                       \
      grn_obj_reinit(ctx, res, res_domain, 0);                          \
      set(ctx, res, integer_operation(x_, y_));                         \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT16 :                                                  \
    {                                                                   \
      uint16_t y_;                                                      \
      y_ = GRN_UINT16_VALUE(y);                                         \
      right_expression_check(y_);                                       \
      grn_obj_reinit(ctx, res, res_domain, 0);                          \
      set(ctx, res, integer_operation(x_, y_));                         \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT32 :                                                   \
    {                                                                   \
      int y_;                                                           \
      y_ = GRN_INT32_VALUE(y);                                          \
      right_expression_check(y_);                                       \
      grn_obj_reinit(ctx, res, res_domain, 0);                          \
      set(ctx, res, integer_operation(x_, y_));                         \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT32 :                                                  \
    {                                                                   \
      unsigned int y_;                                                  \
      y_ = GRN_UINT32_VALUE(y);                                         \
      right_expression_check(y_);                                       \
      grn_obj_reinit(ctx, res, res_domain, 0);                          \
      set(ctx, res, integer_operation(x_, y_));                         \
    }                                                                   \
    break;                                                              \
  case GRN_DB_TIME :                                                    \
    {                                                                   \
      long long int y_;                                                 \
      y_ = GRN_TIME_VALUE(y);                                           \
      right_expression_check(y_);                                       \
      grn_obj_reinit(ctx, res, res_domain, 0);                          \
      set(ctx, res, integer_operation(x_, y_));                         \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT64 :                                                   \
    {                                                                   \
      long long int y_;                                                 \
      y_ = GRN_INT64_VALUE(y);                                          \
      right_expression_check(y_);                                       \
      grn_obj_reinit(ctx, res, res_domain, 0);                          \
      set(ctx, res, integer_operation(x_, y_));                         \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT64 :                                                  \
    {                                                                   \
      long long unsigned int y_;                                        \
      y_ = GRN_UINT64_VALUE(y);                                         \
      right_expression_check(y_);                                       \
      grn_obj_reinit(ctx, res, res_domain, 0);                          \
      set(ctx, res, integer_operation(x_, y_));                         \
    }                                                                   \
    break;                                                              \
  case GRN_DB_FLOAT :                                                   \
    {                                                                   \
      double y_;                                                        \
      y_ = GRN_FLOAT_VALUE(y);                                          \
      right_expression_check(y_);                                       \
      grn_obj_reinit(ctx, res, GRN_DB_FLOAT, 0);                        \
      GRN_FLOAT_SET(ctx, res, float_operation(x_, y_));                 \
    }                                                                   \
    break;                                                              \
  case GRN_DB_SHORT_TEXT :                                              \
  case GRN_DB_TEXT :                                                    \
  case GRN_DB_LONG_TEXT :                                               \
    grn_obj_reinit(ctx, res, res_domain, 0);                            \
    if (grn_obj_cast(ctx, y, res, GRN_FALSE)) {                         \
      ERR(GRN_INVALID_ARGUMENT,                                         \
          "not a numerical format: <%.*s>",                             \
          (int)GRN_TEXT_LEN(y), GRN_TEXT_VALUE(y));                     \
      goto exit;                                                        \
    }                                                                   \
    set(ctx, res, integer_operation(x_, get(res)));                     \
    break;                                                              \
  default :                                                             \
    invalid_type_error;                                                 \
    break;                                                              \
  }                                                                     \
} while (0)


#define ARITHMETIC_OPERATION_DISPATCH(x, y, res,                        \
                                      integer8_operation,               \
                                      integer16_operation,              \
                                      integer32_operation,              \
                                      integer64_operation,              \
                                      float_operation,                  \
                                      left_expression_check,            \
                                      right_expression_check,           \
                                      text_operation,                   \
                                      invalid_type_error) do {          \
  switch (x->header.domain) {                                           \
  case GRN_DB_BOOL :                                                    \
    {                                                                   \
      uint8_t x_;                                                       \
      x_ = GRN_BOOL_VALUE(x) ? 1 : 0;                                   \
      left_expression_check(x_);                                        \
      NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_UINT8_SET,              \
                                            GRN_UINT8_VALUE,            \
                                            x_, y, res, GRN_DB_UINT8,   \
                                            integer8_operation,         \
                                            float_operation,            \
                                            right_expression_check,     \
                                            invalid_type_error);        \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT8 :                                                    \
    {                                                                   \
      int8_t x_;                                                        \
      x_ = GRN_INT8_VALUE(x);                                           \
      left_expression_check(x_);                                        \
      NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_INT8_SET,               \
                                            GRN_INT8_VALUE,             \
                                            x_, y, res, GRN_DB_INT8,    \
                                            integer8_operation,         \
                                            float_operation,            \
                                            right_expression_check,     \
                                            invalid_type_error);        \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT8 :                                                   \
    {                                                                   \
      uint8_t x_;                                                       \
      x_ = GRN_UINT8_VALUE(x);                                          \
      left_expression_check(x_);                                        \
      NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_UINT8_SET,              \
                                            GRN_UINT8_VALUE,            \
                                            x_, y, res, GRN_DB_UINT8,   \
                                            integer8_operation,         \
                                            float_operation,            \
                                            right_expression_check,     \
                                            invalid_type_error);        \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT16 :                                                   \
    {                                                                   \
      int16_t x_;                                                       \
      x_ = GRN_INT16_VALUE(x);                                          \
      left_expression_check(x_);                                        \
      NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_INT16_SET,              \
                                            GRN_INT16_VALUE,            \
                                            x_, y, res, GRN_DB_INT16,   \
                                            integer16_operation,        \
                                            float_operation,            \
                                            right_expression_check,     \
                                            invalid_type_error);        \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT16 :                                                  \
    {                                                                   \
      uint16_t x_;                                                      \
      x_ = GRN_UINT16_VALUE(x);                                         \
      left_expression_check(x_);                                        \
      NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_UINT16_SET,             \
                                            GRN_UINT16_VALUE,           \
                                            x_, y, res, GRN_DB_UINT16,  \
                                            integer16_operation,        \
                                            float_operation,            \
                                            right_expression_check,     \
                                            invalid_type_error);        \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT32 :                                                   \
    {                                                                   \
      int x_;                                                           \
      x_ = GRN_INT32_VALUE(x);                                          \
      left_expression_check(x_);                                        \
      NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_INT32_SET,              \
                                            GRN_INT32_VALUE,            \
                                            x_, y, res, GRN_DB_INT32,   \
                                            integer32_operation,        \
                                            float_operation,            \
                                            right_expression_check,     \
                                            invalid_type_error);        \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT32 :                                                  \
    {                                                                   \
      unsigned int x_;                                                  \
      x_ = GRN_UINT32_VALUE(x);                                         \
      left_expression_check(x_);                                        \
      NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_UINT32_SET,             \
                                            GRN_UINT32_VALUE,           \
                                            x_, y, res, GRN_DB_UINT32,  \
                                            integer32_operation,        \
                                            float_operation,            \
                                            right_expression_check,     \
                                            invalid_type_error);        \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT64 :                                                   \
    {                                                                   \
      long long int x_;                                                 \
      x_ = GRN_INT64_VALUE(x);                                          \
      left_expression_check(x_);                                        \
      NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_INT64_SET,              \
                                            GRN_INT64_VALUE,            \
                                            x_, y, res, GRN_DB_INT64,   \
                                            integer64_operation,        \
                                            float_operation,            \
                                            right_expression_check,     \
                                            invalid_type_error);        \
    }                                                                   \
    break;                                                              \
  case GRN_DB_TIME :                                                    \
    {                                                                   \
      long long int x_;                                                 \
      x_ = GRN_TIME_VALUE(x);                                           \
      left_expression_check(x_);                                        \
      NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_TIME_SET,               \
                                            GRN_TIME_VALUE,             \
                                            x_, y, res, GRN_DB_TIME,    \
                                            integer64_operation,        \
                                            float_operation,            \
                                            right_expression_check,     \
                                            invalid_type_error);        \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT64 :                                                  \
    {                                                                   \
      long long unsigned int x_;                                        \
      x_ = GRN_UINT64_VALUE(x);                                         \
      left_expression_check(x_);                                        \
      NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_UINT64_SET,             \
                                            GRN_UINT64_VALUE,           \
                                            x_, y, res, GRN_DB_UINT64,  \
                                            integer64_operation,        \
                                            float_operation,            \
                                            right_expression_check,     \
                                            invalid_type_error);        \
    }                                                                   \
    break;                                                              \
  case GRN_DB_FLOAT :                                                   \
    {                                                                   \
      double x_;                                                        \
      x_ = GRN_FLOAT_VALUE(x);                                          \
      left_expression_check(x_);                                        \
      NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_FLOAT_SET,              \
                                            GRN_FLOAT_VALUE,            \
                                            x_, y, res, GRN_DB_FLOAT,   \
                                            float_operation,            \
                                            float_operation,            \
                                            right_expression_check,     \
                                            invalid_type_error);        \
    }                                                                   \
    break;                                                              \
  case GRN_DB_SHORT_TEXT :                                              \
  case GRN_DB_TEXT :                                                    \
  case GRN_DB_LONG_TEXT :                                               \
    text_operation;                                                     \
    break;                                                              \
  default:                                                              \
    invalid_type_error;                                                 \
    break;                                                              \
  }                                                                     \
  code++;                                                               \
} while (0)

#define ARITHMETIC_BINARY_OPERATION_DISPATCH(operator,                  \
                                             integer8_operation,        \
                                             integer16_operation,       \
                                             integer32_operation,       \
                                             integer64_operation,       \
                                             float_operation,           \
                                             left_expression_check,     \
                                             right_expression_check,    \
                                             text_operation,            \
                                             invalid_type_error) do {   \
  grn_obj *x = NULL;                                                    \
  grn_obj *y = NULL;                                                    \
                                                                        \
  POP2ALLOC1(x, y, res);                                                \
  if (x->header.type == GRN_VECTOR || y->header.type == GRN_VECTOR) {   \
    grn_obj inspected_x;                                                \
    grn_obj inspected_y;                                                \
    GRN_TEXT_INIT(&inspected_x, 0);                                     \
    GRN_TEXT_INIT(&inspected_y, 0);                                     \
    grn_inspect(ctx, &inspected_x, x);                                  \
    grn_inspect(ctx, &inspected_y, y);                                  \
    ERR(GRN_INVALID_ARGUMENT,                                           \
        "<%s> doesn't support vector: <%.*s> %s <%.*s>",                \
        operator,                                                       \
        (int)GRN_TEXT_LEN(&inspected_x), GRN_TEXT_VALUE(&inspected_x),  \
        operator,                                                       \
        (int)GRN_TEXT_LEN(&inspected_y), GRN_TEXT_VALUE(&inspected_y)); \
    GRN_OBJ_FIN(ctx, &inspected_x);                                     \
    GRN_OBJ_FIN(ctx, &inspected_y);                                     \
    goto exit;                                                          \
  }                                                                     \
  ARITHMETIC_OPERATION_DISPATCH(x, y, res,                              \
                                integer8_operation,                     \
                                integer16_operation,                    \
                                integer32_operation,                    \
                                integer64_operation,                    \
                                float_operation,                        \
                                left_expression_check,                  \
                                right_expression_check,                 \
                                text_operation,                         \
                                invalid_type_error);                    \
} while (0)

#define SIGNED_INTEGER_DIVISION_OPERATION_SLASH(x, y)                   \
  ((y == -1) ? -(x) : (x) / (y))
#define UNSIGNED_INTEGER_DIVISION_OPERATION_SLASH(x, y) ((x) / (y))
#define FLOAT_DIVISION_OPERATION_SLASH(x, y) ((double)(x) / (double)(y))
#define SIGNED_INTEGER_DIVISION_OPERATION_MOD(x, y) ((y == -1) ? 0 : (x) % (y))
#define UNSIGNED_INTEGER_DIVISION_OPERATION_MOD(x, y) ((x) % (y))
#define FLOAT_DIVISION_OPERATION_MOD(x, y) (fmod((x), (y)))

#define DIVISION_OPERATION_DISPATCH_RIGHT(set, get, x_, y, res,         \
                                          signed_integer_operation,     \
                                          unsigned_integer_operation,   \
                                          float_operation) do {         \
  switch (y->header.domain) {                                           \
  case GRN_DB_INT8 :                                                    \
    {                                                                   \
      int y_;                                                           \
      y_ = GRN_INT8_VALUE(y);                                           \
      ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_);                     \
      set(ctx, res, signed_integer_operation(x_, y_));                  \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT8 :                                                   \
    {                                                                   \
      int y_;                                                           \
      y_ = GRN_UINT8_VALUE(y);                                          \
      ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_);                     \
      set(ctx, res, unsigned_integer_operation(x_, y_));                \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT16 :                                                   \
    {                                                                   \
      int y_;                                                           \
      y_ = GRN_INT16_VALUE(y);                                          \
      ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_);                     \
      set(ctx, res, signed_integer_operation(x_, y_));                  \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT16 :                                                  \
    {                                                                   \
      int y_;                                                           \
      y_ = GRN_UINT16_VALUE(y);                                         \
      ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_);                     \
      set(ctx, res, unsigned_integer_operation(x_, y_));                \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT32 :                                                   \
    {                                                                   \
      int y_;                                                           \
      y_ = GRN_INT32_VALUE(y);                                          \
      ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_);                     \
      set(ctx, res, signed_integer_operation(x_, y_));                  \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT32 :                                                  \
    {                                                                   \
      unsigned int y_;                                                  \
      y_ = GRN_UINT32_VALUE(y);                                         \
      ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_);                     \
      set(ctx, res, unsigned_integer_operation(x_, y_));                \
    }                                                                   \
    break;                                                              \
  case GRN_DB_TIME :                                                    \
    {                                                                   \
      long long int y_;                                                 \
      y_ = GRN_TIME_VALUE(y);                                           \
      ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_);                     \
      set(ctx, res, signed_integer_operation(x_, y_));                  \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT64 :                                                   \
    {                                                                   \
      long long int y_;                                                 \
      y_ = GRN_INT64_VALUE(y);                                          \
      ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_);                     \
      set(ctx, res, signed_integer_operation(x_, y_));                  \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT64 :                                                  \
    {                                                                   \
      long long unsigned int y_;                                        \
      y_ = GRN_UINT64_VALUE(y);                                         \
      ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_);                     \
      set(ctx, res, unsigned_integer_operation(x_, y_));                \
    }                                                                   \
    break;                                                              \
  case GRN_DB_FLOAT :                                                   \
    {                                                                   \
      double y_;                                                        \
      y_ = GRN_FLOAT_VALUE(y);                                          \
      ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_);                     \
      res->header.domain = GRN_DB_FLOAT;                                \
      GRN_FLOAT_SET(ctx, res, float_operation(x_, y_));                 \
    }                                                                   \
    break;                                                              \
  case GRN_DB_SHORT_TEXT :                                              \
  case GRN_DB_TEXT :                                                    \
  case GRN_DB_LONG_TEXT :                                               \
    set(ctx, res, 0);                                                   \
    if (grn_obj_cast(ctx, y, res, GRN_FALSE)) {                         \
      ERR(GRN_INVALID_ARGUMENT,                                         \
          "not a numerical format: <%.*s>",                             \
          (int)GRN_TEXT_LEN(y), GRN_TEXT_VALUE(y));                     \
      goto exit;                                                        \
    }                                                                   \
    /* The following "+ 0" is needed to suppress warnings that say */   \
    /* comparison is always false due to limited range of data type */  \
    set(ctx, res, signed_integer_operation(x_, (get(res) + 0)));        \
    break;                                                              \
  default :                                                             \
    break;                                                              \
  }                                                                     \
} while (0)

#define DIVISION_OPERATION_DISPATCH_LEFT(x, y, res,                     \
                                         signed_integer_operation,      \
                                         unsigned_integer_operation,    \
                                         float_operation,               \
                                         invalid_type_error) do {       \
  switch (x->header.domain) {                                           \
  case GRN_DB_INT8 :                                                    \
    {                                                                   \
      int x_;                                                           \
      x_ = GRN_INT8_VALUE(x);                                           \
      DIVISION_OPERATION_DISPATCH_RIGHT(GRN_INT8_SET,                   \
                                        GRN_INT8_VALUE,                 \
                                        x_, y, res,                     \
                                        signed_integer_operation,       \
                                        unsigned_integer_operation,     \
                                        float_operation);               \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT8 :                                                   \
    {                                                                   \
      int x_;                                                           \
      x_ = GRN_UINT8_VALUE(x);                                          \
      DIVISION_OPERATION_DISPATCH_RIGHT(GRN_UINT8_SET,                  \
                                        (int)GRN_UINT8_VALUE,           \
                                        x_, y, res,                     \
                                        signed_integer_operation,       \
                                        unsigned_integer_operation,     \
                                        float_operation);               \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT16 :                                                   \
    {                                                                   \
      int x_;                                                           \
      x_ = GRN_INT16_VALUE(x);                                          \
      DIVISION_OPERATION_DISPATCH_RIGHT(GRN_INT16_SET,                  \
                                        GRN_INT16_VALUE,                \
                                        x_, y, res,                     \
                                        signed_integer_operation,       \
                                        unsigned_integer_operation,     \
                                        float_operation);               \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT16 :                                                  \
    {                                                                   \
      int x_;                                                           \
      x_ = GRN_UINT16_VALUE(x);                                         \
      DIVISION_OPERATION_DISPATCH_RIGHT(GRN_UINT16_SET,                 \
                                        (int)GRN_UINT16_VALUE,          \
                                        x_, y, res,                     \
                                        signed_integer_operation,       \
                                        unsigned_integer_operation,     \
                                        float_operation);               \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT32 :                                                   \
    {                                                                   \
      int x_;                                                           \
      x_ = GRN_INT32_VALUE(x);                                          \
      DIVISION_OPERATION_DISPATCH_RIGHT(GRN_INT32_SET,                  \
                                        GRN_INT32_VALUE,                \
                                        x_, y, res,                     \
                                        signed_integer_operation,       \
                                        unsigned_integer_operation,     \
                                        float_operation);               \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT32 :                                                  \
    {                                                                   \
      unsigned int x_;                                                  \
      x_ = GRN_UINT32_VALUE(x);                                         \
      DIVISION_OPERATION_DISPATCH_RIGHT(GRN_UINT32_SET,                 \
                                        GRN_UINT32_VALUE,               \
                                        x_, y, res,                     \
                                        unsigned_integer_operation,     \
                                        unsigned_integer_operation,     \
                                        float_operation);               \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT64 :                                                   \
    {                                                                   \
      long long int x_;                                                 \
      x_ = GRN_INT64_VALUE(x);                                          \
      DIVISION_OPERATION_DISPATCH_RIGHT(GRN_INT64_SET,                  \
                                        GRN_INT64_VALUE,                \
                                        x_, y, res,                     \
                                        signed_integer_operation,       \
                                        unsigned_integer_operation,     \
                                        float_operation);               \
    }                                                                   \
    break;                                                              \
  case GRN_DB_TIME :                                                    \
    {                                                                   \
      long long int x_;                                                 \
      x_ = GRN_TIME_VALUE(x);                                           \
      DIVISION_OPERATION_DISPATCH_RIGHT(GRN_TIME_SET,                   \
                                        GRN_TIME_VALUE,                 \
                                        x_, y, res,                     \
                                        signed_integer_operation,       \
                                        unsigned_integer_operation,     \
                                        float_operation);               \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT64 :                                                  \
    {                                                                   \
      long long unsigned int x_;                                        \
      x_ = GRN_UINT64_VALUE(x);                                         \
      DIVISION_OPERATION_DISPATCH_RIGHT(GRN_UINT64_SET,                 \
                                        GRN_UINT64_VALUE,               \
                                        x_, y, res,                     \
                                        unsigned_integer_operation,     \
                                        unsigned_integer_operation,     \
                                        float_operation);               \
    }                                                                   \
    break;                                                              \
  case GRN_DB_FLOAT :                                                   \
    {                                                                   \
      double x_;                                                        \
      x_ = GRN_FLOAT_VALUE(x);                                          \
      DIVISION_OPERATION_DISPATCH_RIGHT(GRN_FLOAT_SET,                  \
                                        GRN_FLOAT_VALUE,                \
                                        x_, y, res,                     \
                                        float_operation,                \
                                        float_operation,                \
                                        float_operation);               \
    }                                                                   \
    break;                                                              \
  case GRN_DB_SHORT_TEXT :                                              \
  case GRN_DB_TEXT :                                                    \
  case GRN_DB_LONG_TEXT :                                               \
    invalid_type_error;                                                 \
    break;                                                              \
  default:                                                              \
    break;                                                              \
  }                                                                     \
  code++;                                                               \
} while (0)

#define DIVISION_OPERATION_DISPATCH(signed_integer_operation,           \
                                    unsigned_integer_operation,         \
                                    float_operation,                    \
                                    invalid_type_error) do {            \
  grn_obj *x = NULL;                                                    \
  grn_obj *y = NULL;                                                    \
                                                                        \
  POP2ALLOC1(x, y, res);                                                \
  if (y != res) {                                                       \
    res->header.domain = x->header.domain;                              \
  }                                                                     \
  DIVISION_OPERATION_DISPATCH_LEFT(x, y, res,                           \
                                   signed_integer_operation,            \
                                   unsigned_integer_operation,          \
                                   float_operation,                     \
                                   invalid_type_error);                 \
  if (y == res) {                                                       \
    res->header.domain = x->header.domain;                              \
  }                                                                     \
} while (0)

#define ARITHMETIC_UNARY_OPERATION_DISPATCH(integer_operation,          \
                                            float_operation,            \
                                            left_expression_check,      \
                                            right_expression_check,     \
                                            text_operation,             \
                                            invalid_type_error) do {    \
  grn_obj *x = NULL;                                                    \
  POP1ALLOC1(x, res);                                                   \
  switch (x->header.domain) {                                           \
  case GRN_DB_INT8 :                                                    \
    {                                                                   \
      int8_t x_;                                                        \
      x_ = GRN_INT8_VALUE(x);                                           \
      left_expression_check(x_);                                        \
      grn_obj_reinit(ctx, res, GRN_DB_INT8, 0);                         \
      GRN_INT8_SET(ctx, res, integer_operation(x_));                    \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT8 :                                                   \
    {                                                                   \
      int16_t x_;                                                       \
      x_ = GRN_UINT8_VALUE(x);                                          \
      left_expression_check(x_);                                        \
      grn_obj_reinit(ctx, res, GRN_DB_INT16, 0);                        \
      GRN_INT16_SET(ctx, res, integer_operation(x_));                   \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT16 :                                                   \
    {                                                                   \
      int16_t x_;                                                       \
      x_ = GRN_INT16_VALUE(x);                                          \
      left_expression_check(x_);                                        \
      grn_obj_reinit(ctx, res, GRN_DB_INT16, 0);                        \
      GRN_INT16_SET(ctx, res, integer_operation(x_));                   \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT16 :                                                  \
    {                                                                   \
      int32_t x_;                                                       \
      x_ = GRN_UINT16_VALUE(x);                                         \
      left_expression_check(x_);                                        \
      grn_obj_reinit(ctx, res, GRN_DB_INT32, 0);                        \
      GRN_INT32_SET(ctx, res, integer_operation(x_));                   \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT32 :                                                   \
    {                                                                   \
      int32_t x_;                                                       \
      x_ = GRN_INT32_VALUE(x);                                          \
      left_expression_check(x_);                                        \
      grn_obj_reinit(ctx, res, GRN_DB_INT32, 0);                        \
      GRN_INT32_SET(ctx, res, integer_operation(x_));                   \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT32 :                                                  \
    {                                                                   \
      int64_t x_;                                                       \
      x_ = GRN_UINT32_VALUE(x);                                         \
      left_expression_check(x_);                                        \
      grn_obj_reinit(ctx, res, GRN_DB_INT64, 0);                        \
      GRN_INT64_SET(ctx, res, integer_operation(x_));                   \
    }                                                                   \
    break;                                                              \
  case GRN_DB_INT64 :                                                   \
    {                                                                   \
      int64_t x_;                                                       \
      x_ = GRN_INT64_VALUE(x);                                          \
      left_expression_check(x_);                                        \
      grn_obj_reinit(ctx, res, GRN_DB_INT64, 0);                        \
      GRN_INT64_SET(ctx, res, integer_operation(x_));                   \
    }                                                                   \
    break;                                                              \
  case GRN_DB_TIME :                                                    \
    {                                                                   \
      int64_t x_;                                                       \
      x_ = GRN_TIME_VALUE(x);                                           \
      left_expression_check(x_);                                        \
      grn_obj_reinit(ctx, res, GRN_DB_TIME, 0);                         \
      GRN_TIME_SET(ctx, res, integer_operation(x_));                    \
    }                                                                   \
    break;                                                              \
  case GRN_DB_UINT64 :                                                  \
    {                                                                   \
      uint64_t x_;                                                      \
      x_ = GRN_UINT64_VALUE(x);                                         \
      left_expression_check(x_);                                        \
      if (x_ > (uint64_t)INT64_MAX) {                                   \
        ERR(GRN_INVALID_ARGUMENT,                                       \
            "too large UInt64 value to inverse sign: "                  \
            "<%" GRN_FMT_INT64U ">",                                    \
            x_);                                                        \
        goto exit;                                                      \
      } else {                                                          \
        int64_t signed_x_;                                              \
        signed_x_ = x_;                                                 \
        grn_obj_reinit(ctx, res, GRN_DB_INT64, 0);                      \
        GRN_INT64_SET(ctx, res, integer_operation(signed_x_));          \
      }                                                                 \
    }                                                                   \
    break;                                                              \
  case GRN_DB_FLOAT :                                                   \
    {                                                                   \
      double x_;                                                        \
      x_ = GRN_FLOAT_VALUE(x);                                          \
      left_expression_check(x_);                                        \
      grn_obj_reinit(ctx, res, GRN_DB_FLOAT, 0);                        \
      GRN_FLOAT_SET(ctx, res, float_operation(x_));                     \
    }                                                                   \
    break;                                                              \
  case GRN_DB_SHORT_TEXT :                                              \
  case GRN_DB_TEXT :                                                    \
  case GRN_DB_LONG_TEXT :                                               \
    text_operation;                                                     \
    break;                                                              \
  default:                                                              \
    invalid_type_error;                                                 \
    break;                                                              \
  }                                                                     \
  code++;                                                               \
} while (0)

#define EXEC_OPERATE(operate_sentence, assign_sentence)   \
  operate_sentence                                        \
  assign_sentence

#define EXEC_OPERATE_POST(operate_sentence, assign_sentence)    \
  assign_sentence                                               \
  operate_sentence

#define UNARY_OPERATE_AND_ASSIGN_DISPATCH(exec_operate, delta,          \
                                          set_flags) do {               \
  grn_obj *var = NULL;                                                  \
  grn_obj *col = NULL;                                                  \
  grn_obj value;                                                        \
  grn_id rid;                                                           \
                                                                        \
  POP1ALLOC1(var, res);                                                 \
  if (var->header.type != GRN_PTR) {                                    \
    ERR(GRN_INVALID_ARGUMENT, "invalid variable type: 0x%0x",           \
        var->header.type);                                              \
    goto exit;                                                          \
  }                                                                     \
  if (GRN_BULK_VSIZE(var) != (sizeof(grn_obj *) + sizeof(grn_id))) {    \
    ERR(GRN_INVALID_ARGUMENT,                                           \
        "invalid variable size: "                                       \
        "expected: %" GRN_FMT_SIZE                                      \
        "actual: %" GRN_FMT_SIZE,                                       \
        (sizeof(grn_obj *) + sizeof(grn_id)), GRN_BULK_VSIZE(var));     \
    goto exit;                                                          \
  }                                                                     \
  col = GRN_PTR_VALUE(var);                                             \
  rid = *(grn_id *)(GRN_BULK_HEAD(var) + sizeof(grn_obj *));            \
  res->header.type = GRN_VOID;                                          \
  res->header.domain = DB_OBJ(col)->range;                              \
  switch (DB_OBJ(col)->range) {                                         \
  case GRN_DB_INT32 :                                                   \
    GRN_INT32_INIT(&value, 0);                                          \
    GRN_INT32_SET(ctx, &value, delta);                                  \
    break;                                                              \
  case GRN_DB_UINT32 :                                                  \
    GRN_UINT32_INIT(&value, 0);                                         \
    GRN_UINT32_SET(ctx, &value, delta);                                 \
    break;                                                              \
  case GRN_DB_INT64 :                                                   \
    GRN_INT64_INIT(&value, 0);                                          \
    GRN_INT64_SET(ctx, &value, delta);                                  \
    break;                                                              \
  case GRN_DB_UINT64 :                                                  \
    GRN_UINT64_INIT(&value, 0);                                         \
    GRN_UINT64_SET(ctx, &value, delta);                                 \
    break;                                                              \
  case GRN_DB_FLOAT :                                                   \
    GRN_FLOAT_INIT(&value, 0);                                          \
    GRN_FLOAT_SET(ctx, &value, delta);                                  \
    break;                                                              \
  case GRN_DB_TIME :                                                    \
    GRN_TIME_INIT(&value, 0);                                           \
    GRN_TIME_SET(ctx, &value, GRN_TIME_PACK(delta, 0));                 \
    break;                                                              \
  default:                                                              \
    ERR(GRN_INVALID_ARGUMENT,                                           \
        "invalid increment target type: %d "                            \
        "(FIXME: type name is needed)", DB_OBJ(col)->range);            \
    goto exit;                                                          \
    break;                                                              \
  }                                                                     \
  exec_operate(grn_obj_set_value(ctx, col, rid, &value, set_flags);,    \
               grn_obj_get_value(ctx, col, rid, res););                 \
  code++;                                                               \
} while (0)

#define ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(integer8_operation,    \
                                                 integer16_operation,   \
                                                 integer32_operation,   \
                                                 integer64_operation,   \
                                                 float_operation,       \
                                                 left_expression_check, \
                                                 right_expression_check,\
                                                 text_operation) do {   \
  grn_obj *value = NULL;                                                \
  grn_obj *var = NULL;                                                  \
  grn_obj *res = NULL;                                                  \
  if (code->value) {                                                    \
    value = code->value;                                                \
    POP1ALLOC1(var, res);                                               \
  } else {                                                              \
    POP2ALLOC1(var, value, res);                                        \
  }                                                                     \
  if (var->header.type == GRN_PTR &&                                    \
      GRN_BULK_VSIZE(var) == (sizeof(grn_obj *) + sizeof(grn_id))) {    \
    grn_obj *col = GRN_PTR_VALUE(var);                                  \
    grn_id rid = *(grn_id *)(GRN_BULK_HEAD(var) + sizeof(grn_obj *));   \
    grn_obj variable_value, casted_value;                               \
    grn_id domain;                                                      \
                                                                        \
    value = GRN_OBJ_RESOLVE(ctx, value);                                \
                                                                        \
    domain = grn_obj_get_range(ctx, col);                               \
    GRN_OBJ_INIT(&variable_value, GRN_BULK, 0, domain);                 \
    grn_obj_get_value(ctx, col, rid, &variable_value);                  \
                                                                        \
    GRN_OBJ_INIT(&casted_value, GRN_BULK, 0, domain);                   \
    if (grn_obj_cast(ctx, value, &casted_value, GRN_FALSE)) {           \
      ERR(GRN_INVALID_ARGUMENT, "invalid value: string");               \
      GRN_OBJ_FIN(ctx, &variable_value);                                \
      GRN_OBJ_FIN(ctx, &casted_value);                                  \
      POP1(res);                                                        \
      goto exit;                                                        \
    }                                                                   \
    ARITHMETIC_OPERATION_DISPATCH((&variable_value), (&casted_value),   \
                                  res,                                  \
                                  integer8_operation,                   \
                                  integer16_operation,                  \
                                  integer32_operation,                  \
                                  integer64_operation,                  \
                                  float_operation,                      \
                                  left_expression_check,                \
                                  right_expression_check,               \
                                  text_operation,);                     \
    grn_obj_set_value(ctx, col, rid, res, GRN_OBJ_SET);                 \
    GRN_OBJ_FIN(ctx, (&variable_value));                                \
    GRN_OBJ_FIN(ctx, (&casted_value));                                  \
  } else {                                                              \
    ERR(GRN_INVALID_ARGUMENT, "left hand expression isn't column.");    \
    POP1(res);                                                          \
  }                                                                     \
} while (0)

grn_inline static void
grn_expr_exec_get_member_vector(grn_ctx *ctx,
                                grn_obj *expr,
                                grn_obj *column_and_record_id,
                                grn_obj *index,
                                grn_obj *result)
{
  grn_obj *column;
  grn_id record_id;
  grn_obj values;
  int i;

  column = GRN_PTR_VALUE(column_and_record_id);
  record_id = *((grn_id *)(&(GRN_PTR_VALUE_AT(column_and_record_id, 1))));
  GRN_TEXT_INIT(&values, 0);
  grn_obj_get_value(ctx, column, record_id, &values);

  i = GRN_UINT32_VALUE(index);
  if (values.header.type == GRN_UVECTOR) {
    int n_elements = 0;
    grn_obj *range;
    grn_id range_id = DB_OBJ(column)->range;

    grn_obj_reinit(ctx, result, range_id, 0);
    range = grn_ctx_at(ctx, range_id);
    if (range) {
      switch (range->header.type) {
      case GRN_TYPE :
        n_elements = GRN_BULK_VSIZE(&values) / grn_type_size(ctx, range);
        break;
      case GRN_TABLE_HASH_KEY :
      case GRN_TABLE_PAT_KEY :
      case GRN_TABLE_DAT_KEY :
      case GRN_TABLE_NO_KEY :
        n_elements = GRN_BULK_VSIZE(&values) / sizeof(grn_id);
        break;
      }
    }
    if (n_elements > i) {
#define GET_UVECTOR_ELEMENT_AS(type) do {                               \
        GRN_ ## type ## _SET(ctx,                                       \
                             result,                                    \
                             GRN_ ## type ## _VALUE_AT(&values, i));    \
      } while (GRN_FALSE)
      switch (values.header.domain) {
      case GRN_DB_BOOL :
        GET_UVECTOR_ELEMENT_AS(BOOL);
        break;
      case GRN_DB_INT8 :
        GET_UVECTOR_ELEMENT_AS(INT8);
        break;
      case GRN_DB_UINT8 :
        GET_UVECTOR_ELEMENT_AS(UINT8);
        break;
      case GRN_DB_INT16 :
        GET_UVECTOR_ELEMENT_AS(INT16);
        break;
      case GRN_DB_UINT16 :
        GET_UVECTOR_ELEMENT_AS(UINT16);
        break;
      case GRN_DB_INT32 :
        GET_UVECTOR_ELEMENT_AS(INT32);
        break;
      case GRN_DB_UINT32 :
        GET_UVECTOR_ELEMENT_AS(UINT32);
        break;
      case GRN_DB_INT64 :
        GET_UVECTOR_ELEMENT_AS(INT64);
        break;
      case GRN_DB_UINT64 :
        GET_UVECTOR_ELEMENT_AS(UINT64);
        break;
      case GRN_DB_FLOAT :
        GET_UVECTOR_ELEMENT_AS(FLOAT);
        break;
      case GRN_DB_TIME :
        GET_UVECTOR_ELEMENT_AS(TIME);
        break;
      default :
        GET_UVECTOR_ELEMENT_AS(RECORD);
        break;
      }
#undef GET_UVECTOR_ELEMENT_AS
    }
  } else {
    if (values.u.v.n_sections > i) {
      const char *content;
      unsigned int content_length;
      grn_id domain;

      content_length = grn_vector_get_element(ctx, &values, i,
                                              &content, NULL, &domain);
      grn_obj_reinit(ctx, result, domain, 0);
      grn_bulk_write(ctx, result, content, content_length);
    }
  }

  GRN_OBJ_FIN(ctx, &values);
}

grn_inline static void
grn_expr_exec_get_member_table(grn_ctx *ctx,
                               grn_obj *expr,
                               grn_obj *table,
                               grn_obj *key,
                               grn_obj *result)
{
  grn_id id;

  if (table->header.domain == key->header.domain) {
    id = grn_table_get(ctx, table, GRN_BULK_HEAD(key), GRN_BULK_VSIZE(key));
  } else {
    grn_obj casted_key;
    GRN_OBJ_INIT(&casted_key, GRN_BULK, 0, table->header.domain);
    if (grn_obj_cast(ctx, key, &casted_key, GRN_FALSE) == GRN_SUCCESS) {
      id = grn_table_get(ctx, table,
                         GRN_BULK_HEAD(&casted_key),
                         GRN_BULK_VSIZE(&casted_key));
    } else {
      id = GRN_ID_NIL;
    }
    GRN_OBJ_FIN(ctx, &casted_key);
  }

  grn_obj_reinit(ctx, result, DB_OBJ(table)->id, 0);
  GRN_RECORD_SET(ctx, result, id);
}

static grn_obj *
expr_exec(grn_ctx *ctx, grn_obj *expr)
{
  grn_obj *val = NULL;
  uint32_t stack_curr = ctx->impl->stack_curr;
  grn_expr *e = (grn_expr *)expr;
  register grn_obj **s_ = ctx->impl->stack;
  register grn_obj *s0 = NULL;
  register grn_obj *s1 = NULL;
  register grn_obj **sp;
  register grn_obj *vp = e->values;
  grn_obj *res = NULL, *v0 = grn_expr_get_var_by_offset(ctx, expr, 0);
  grn_expr_code *code = e->codes, *ce = &e->codes[e->codes_curr];
  sp = s_ + stack_curr;
  while (code < ce) {
    switch (code->op) {
    case GRN_OP_NOP :
      code++;
      break;
    case GRN_OP_PUSH :
      PUSH1(code->value);
      code++;
      break;
    case GRN_OP_POP :
      {
        grn_obj *obj = NULL;
        POP1(obj);
        code++;
      }
      break;
    case GRN_OP_GET_REF :
      {
        grn_obj *col = NULL;
        grn_obj *rec = NULL;
        if (code->nargs == 1) {
          rec = v0;
          if (code->value) {
            col = code->value;
            ALLOC1(res);
          } else {
            POP1ALLOC1(col, res);
          }
        } else {
          if (code->value) {
            col = code->value;
            POP1ALLOC1(rec, res);
          } else {
            POP2ALLOC1(rec, col, res);
          }
        }
        if (col->header.type == GRN_BULK) {
          grn_obj *table = grn_ctx_at(ctx, GRN_OBJ_GET_DOMAIN(rec));
          col = grn_obj_column(ctx, table, GRN_BULK_HEAD(col), GRN_BULK_VSIZE(col));
          if (col) { grn_expr_take_obj(ctx, (grn_obj *)e, col); }
        }
        if (col) {
          res->header.type = GRN_PTR;
          res->header.domain = GRN_ID_NIL;
          GRN_PTR_SET(ctx, res, col);
          GRN_UINT32_PUT(ctx, res, GRN_RECORD_VALUE(rec));
        } else {
          ERR(GRN_INVALID_ARGUMENT, "col resolve failed");
          goto exit;
        }
        code++;
      }
      break;
    case GRN_OP_CALL :
      {
        grn_obj *proc = NULL;
        if (code->value) {
          if (sp < s_ + code->nargs - 1) {
            ERR(GRN_INVALID_ARGUMENT, "stack error");
            goto exit;
          }
          proc = code->value;
          WITH_SPSAVE({
            grn_proc_call(ctx, proc, code->nargs - 1, expr);
          });
        } else {
          int offset = code->nargs;
          if (sp < s_ + offset) {
            ERR(GRN_INVALID_ARGUMENT, "stack error");
            goto exit;
          }
          proc = sp[-offset];
          if (grn_obj_is_window_function_proc(ctx, proc)) {
            grn_obj inspected;
            GRN_TEXT_INIT(&inspected, 0);
            grn_inspect(ctx, &inspected, proc);
            ERR(GRN_INVALID_ARGUMENT,
                "window function can't be executed for each record: %.*s",
                (int)GRN_TEXT_LEN(&inspected),
                GRN_TEXT_VALUE(&inspected));
            GRN_OBJ_FIN(ctx, &inspected);
            goto exit;
          } else {
            WITH_SPSAVE({
              grn_proc_call(ctx, proc, code->nargs - 1, expr);
            });
          }
          if (ctx->rc) {
            goto exit;
          }
          POP1(res);
          {
            grn_obj *proc_;
            POP1(proc_);
            if (proc != proc_) {
              GRN_LOG(ctx, GRN_LOG_WARNING, "stack may be corrupt");
            }
          }
          PUSH1(res);
        }
      }
      code++;
      break;
    case GRN_OP_INTERN :
      {
        grn_obj *obj = NULL;
        POP1(obj);
        obj = GRN_OBJ_RESOLVE(ctx, obj);
        res = grn_expr_get_var(ctx, expr, GRN_TEXT_VALUE(obj), GRN_TEXT_LEN(obj));
        if (!res) { res = grn_ctx_get(ctx, GRN_TEXT_VALUE(obj), GRN_TEXT_LEN(obj)); }
        if (!res) {
          ERR(GRN_INVALID_ARGUMENT, "intern failed");
          goto exit;
        }
        PUSH1(res);
      }
      code++;
      break;
    case GRN_OP_TABLE_CREATE :
      {
        grn_obj *value_type, *key_type, *flags, *name;
        POP1(value_type);
        value_type = GRN_OBJ_RESOLVE(ctx, value_type);
        POP1(key_type);
        key_type = GRN_OBJ_RESOLVE(ctx, key_type);
        POP1(flags);
        flags = GRN_OBJ_RESOLVE(ctx, flags);
        POP1(name);
        name = GRN_OBJ_RESOLVE(ctx, name);
        res = grn_table_create(ctx, GRN_TEXT_VALUE(name), GRN_TEXT_LEN(name),
                               NULL, GRN_UINT32_VALUE(flags),
                               key_type, value_type);
        PUSH1(res);
      }
      code++;
      break;
    case GRN_OP_EXPR_GET_VAR :
      {
        grn_obj *name = NULL;
        grn_obj *expr = NULL;
        POP1(name);
        name = GRN_OBJ_RESOLVE(ctx, name);
        POP1(expr);
        expr = GRN_OBJ_RESOLVE(ctx, expr);
        switch (name->header.domain) {
        case GRN_DB_INT32 :
          res = grn_expr_get_var_by_offset(ctx, expr, (unsigned int) GRN_INT32_VALUE(name));
          break;
        case GRN_DB_UINT32 :
          res = grn_expr_get_var_by_offset(ctx, expr, (unsigned int) GRN_UINT32_VALUE(name));
          break;
        case GRN_DB_INT64 :
          res = grn_expr_get_var_by_offset(ctx, expr, (unsigned int) GRN_INT64_VALUE(name));
          break;
        case GRN_DB_UINT64 :
          res = grn_expr_get_var_by_offset(ctx, expr, (unsigned int) GRN_UINT64_VALUE(name));
          break;
        case GRN_DB_SHORT_TEXT :
        case GRN_DB_TEXT :
        case GRN_DB_LONG_TEXT :
          res = grn_expr_get_var(ctx, expr, GRN_TEXT_VALUE(name), GRN_TEXT_LEN(name));
          break;
        default :
          ERR(GRN_INVALID_ARGUMENT, "invalid type");
          goto exit;
        }
        PUSH1(res);
      }
      code++;
      break;
    case GRN_OP_ASSIGN :
      {
        grn_obj *value = NULL;
        grn_obj *var = NULL;
        if (code->value) {
          value = code->value;
        } else {
          POP1(value);
        }
        value = GRN_OBJ_RESOLVE(ctx, value);
        POP1(var);
        // var = GRN_OBJ_RESOLVE(ctx, var);
        if (var->header.type == GRN_PTR &&
            GRN_BULK_VSIZE(var) == (sizeof(grn_obj *) + sizeof(grn_id))) {
          grn_obj *col = GRN_PTR_VALUE(var);
          grn_id rid = *(grn_id *)(GRN_BULK_HEAD(var) + sizeof(grn_obj *));
          grn_obj_set_value(ctx, col, rid, value, GRN_OBJ_SET);
        } else {
          VAR_SET_VALUE(ctx, var, value);
        }
        PUSH1(value);
      }
      code++;
      break;
    case GRN_OP_STAR_ASSIGN :
      ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
        INTEGER_ARITHMETIC_OPERATION_STAR,
        INTEGER_ARITHMETIC_OPERATION_STAR,
        INTEGER_ARITHMETIC_OPERATION_STAR,
        INTEGER_ARITHMETIC_OPERATION_STAR,
        FLOAT_ARITHMETIC_OPERATION_STAR,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        {
          ERR(GRN_INVALID_ARGUMENT, "variable *= \"string\" isn't supported");
          goto exit;
        });
      break;
    case GRN_OP_SLASH_ASSIGN :
      ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
        INTEGER_ARITHMETIC_OPERATION_SLASH,
        INTEGER_ARITHMETIC_OPERATION_SLASH,
        INTEGER_ARITHMETIC_OPERATION_SLASH,
        INTEGER_ARITHMETIC_OPERATION_SLASH,
        FLOAT_ARITHMETIC_OPERATION_SLASH,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        {
          ERR(GRN_INVALID_ARGUMENT, "variable /= \"string\" isn't supported");
          goto exit;
        });
      break;
    case GRN_OP_MOD_ASSIGN :
      ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
        INTEGER_ARITHMETIC_OPERATION_MOD,
        INTEGER_ARITHMETIC_OPERATION_MOD,
        INTEGER_ARITHMETIC_OPERATION_MOD,
        INTEGER_ARITHMETIC_OPERATION_MOD,
        FLOAT_ARITHMETIC_OPERATION_MOD,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        {
          ERR(GRN_INVALID_ARGUMENT, "variable %%= \"string\" isn't supported");
          goto exit;
        });
      break;
    case GRN_OP_PLUS_ASSIGN :
      ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
        INTEGER_ARITHMETIC_OPERATION_PLUS,
        INTEGER_ARITHMETIC_OPERATION_PLUS,
        INTEGER_ARITHMETIC_OPERATION_PLUS,
        INTEGER_ARITHMETIC_OPERATION_PLUS,
        FLOAT_ARITHMETIC_OPERATION_PLUS,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        {
          ERR(GRN_INVALID_ARGUMENT, "variable += \"string\" isn't supported");
          goto exit;
        });
      break;
    case GRN_OP_MINUS_ASSIGN :
      ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
        INTEGER_ARITHMETIC_OPERATION_MINUS,
        INTEGER_ARITHMETIC_OPERATION_MINUS,
        INTEGER_ARITHMETIC_OPERATION_MINUS,
        INTEGER_ARITHMETIC_OPERATION_MINUS,
        FLOAT_ARITHMETIC_OPERATION_MINUS,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        {
          ERR(GRN_INVALID_ARGUMENT, "variable -= \"string\" isn't supported");
          goto exit;
        });
      break;
    case GRN_OP_SHIFTL_ASSIGN :
      ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
        INTEGER_ARITHMETIC_OPERATION_SHIFTL,
        INTEGER_ARITHMETIC_OPERATION_SHIFTL,
        INTEGER_ARITHMETIC_OPERATION_SHIFTL,
        INTEGER_ARITHMETIC_OPERATION_SHIFTL,
        FLOAT_ARITHMETIC_OPERATION_SHIFTL,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        {
          ERR(GRN_INVALID_ARGUMENT, "variable <<= \"string\" isn't supported");
          goto exit;
        });
      break;
    case GRN_OP_SHIFTR_ASSIGN :
      ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
        INTEGER_ARITHMETIC_OPERATION_SHIFTR,
        INTEGER_ARITHMETIC_OPERATION_SHIFTR,
        INTEGER_ARITHMETIC_OPERATION_SHIFTR,
        INTEGER_ARITHMETIC_OPERATION_SHIFTR,
        FLOAT_ARITHMETIC_OPERATION_SHIFTR,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        {
          ERR(GRN_INVALID_ARGUMENT, "variable >>= \"string\" isn't supported");
          goto exit;
        });
      break;
    case GRN_OP_SHIFTRR_ASSIGN :
      ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
        INTEGER8_ARITHMETIC_OPERATION_SHIFTRR,
        INTEGER16_ARITHMETIC_OPERATION_SHIFTRR,
        INTEGER32_ARITHMETIC_OPERATION_SHIFTRR,
        INTEGER64_ARITHMETIC_OPERATION_SHIFTRR,
        FLOAT_ARITHMETIC_OPERATION_SHIFTRR,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        {
          ERR(GRN_INVALID_ARGUMENT,
              "variable >>>= \"string\" isn't supported");
          goto exit;
        });
      break;
    case GRN_OP_AND_ASSIGN :
      ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
        INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
        FLOAT_ARITHMETIC_OPERATION_BITWISE_AND,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        {
          ERR(GRN_INVALID_ARGUMENT, "variable &= \"string\" isn't supported");
          goto exit;
        });
      break;
    case GRN_OP_OR_ASSIGN :
      ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
        INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
        FLOAT_ARITHMETIC_OPERATION_BITWISE_OR,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        {
          ERR(GRN_INVALID_ARGUMENT, "variable |= \"string\" isn't supported");
          goto exit;
        });
      break;
    case GRN_OP_XOR_ASSIGN :
      ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
        INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
        FLOAT_ARITHMETIC_OPERATION_BITWISE_XOR,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        {
          ERR(GRN_INVALID_ARGUMENT, "variable ^= \"string\" isn't supported");
          goto exit;
        });
      break;
    case GRN_OP_JUMP :
      code += code->nargs + 1;
      break;
    case GRN_OP_CJUMP :
      {
        grn_obj *v = NULL;
        POP1(v);
        if (!grn_obj_is_true(ctx, v)) {
          code += code->nargs;
        }
      }
      code++;
      break;
    case GRN_OP_GET_VALUE :
      {
        grn_obj *col = NULL;
        grn_obj *rec = NULL;
        do {
          if (code->nargs == 1) {
            rec = v0;
            if (code->value) {
              col = code->value;
              ALLOC1(res);
            } else {
              POP1ALLOC1(col, res);
            }
          } else {
            if (code->value) {
              col = code->value;
              POP1ALLOC1(rec, res);
            } else {
              POP2ALLOC1(rec, col, res);
            }
          }
          if (col->header.type == GRN_BULK) {
            grn_obj *table = grn_ctx_at(ctx, GRN_OBJ_GET_DOMAIN(rec));
            col = grn_obj_column(ctx, table, GRN_BULK_HEAD(col), GRN_BULK_VSIZE(col));
            if (col) { grn_expr_take_obj(ctx, (grn_obj *)expr, col); }
          }
          if (!col) {
            ERR(GRN_INVALID_ARGUMENT, "col resolve failed");
            goto exit;
          }
          grn_obj_reinit_for(ctx, res, col);
          grn_obj_get_value(ctx, col, GRN_RECORD_VALUE(rec), res);
          code++;
        } while (code < ce && code->op == GRN_OP_GET_VALUE);
      }
      break;
    case GRN_OP_OBJ_SEARCH :
      {
        grn_obj *op = NULL;
        grn_obj *query = NULL;
        grn_obj *index = NULL;
        // todo : grn_search_optarg optarg;
        POP1(op);
        op = GRN_OBJ_RESOLVE(ctx, op);
        POP1(res);
        res = GRN_OBJ_RESOLVE(ctx, res);
        POP1(query);
        query = GRN_OBJ_RESOLVE(ctx, query);
        POP1(index);
        index = GRN_OBJ_RESOLVE(ctx, index);
        grn_obj_search(ctx, index, query, res,
                       (grn_operator)GRN_UINT32_VALUE(op), NULL);
      }
      code++;
      break;
    case GRN_OP_TABLE_SELECT :
      {
        grn_obj *op = NULL;
        grn_obj *res = NULL;
        grn_obj *expr = NULL;
        grn_obj *table = NULL;
        POP1(op);
        op = GRN_OBJ_RESOLVE(ctx, op);
        POP1(res);
        res = GRN_OBJ_RESOLVE(ctx, res);
        POP1(expr);
        expr = GRN_OBJ_RESOLVE(ctx, expr);
        POP1(table);
        table = GRN_OBJ_RESOLVE(ctx, table);
        WITH_SPSAVE({
          grn_table_select(ctx, table, expr, res, (grn_operator)GRN_UINT32_VALUE(op));
        });
        PUSH1(res);
      }
      code++;
      break;
    case GRN_OP_TABLE_SORT :
      {
        grn_obj *keys_ = NULL;
        grn_obj *res = NULL;
        grn_obj *limit = NULL;
        grn_obj *table = NULL;
        POP1(keys_);
        keys_ = GRN_OBJ_RESOLVE(ctx, keys_);
        POP1(res);
        res = GRN_OBJ_RESOLVE(ctx, res);
        POP1(limit);
        limit = GRN_OBJ_RESOLVE(ctx, limit);
        POP1(table);
        table = GRN_OBJ_RESOLVE(ctx, table);
        {
          grn_table_sort_key *keys;
          const char *p = GRN_BULK_HEAD(keys_), *tokbuf[256];
          int n = grn_str_tok(p, GRN_BULK_VSIZE(keys_), ' ', tokbuf, 256, NULL);
          if ((keys = GRN_MALLOCN(grn_table_sort_key, n))) {
            int i, n_keys = 0;
            for (i = 0; i < n; i++) {
              uint32_t len = (uint32_t) (tokbuf[i] - p);
              grn_obj *col = grn_obj_column(ctx, table, p, len);
              if (col) {
                keys[n_keys].key = col;
                keys[n_keys].flags = GRN_TABLE_SORT_ASC;
                keys[n_keys].offset = 0;
                n_keys++;
              } else {
                if (p[0] == ':' && p[1] == 'd' && len == 2 && n_keys) {
                  keys[n_keys - 1].flags |= GRN_TABLE_SORT_DESC;
                }
              }
              p = tokbuf[i] + 1;
            }
            WITH_SPSAVE({
              grn_table_sort(ctx, table, 0, GRN_INT32_VALUE(limit), res, keys, n_keys);
            });
            for (i = 0; i < n_keys; i++) {
              grn_obj_unlink(ctx, keys[i].key);
            }
            GRN_FREE(keys);
          }
        }
      }
      code++;
      break;
    case GRN_OP_TABLE_GROUP :
      {
        grn_obj *res = NULL;
        grn_obj *keys_ = NULL;
        grn_obj *table = NULL;
        POP1(res);
        res = GRN_OBJ_RESOLVE(ctx, res);
        POP1(keys_);
        keys_ = GRN_OBJ_RESOLVE(ctx, keys_);
        POP1(table);
        table = GRN_OBJ_RESOLVE(ctx, table);
        {
          grn_table_sort_key *keys;
          grn_table_group_result results;
          const char *p = GRN_BULK_HEAD(keys_), *tokbuf[256];
          int n = grn_str_tok(p, GRN_BULK_VSIZE(keys_), ' ', tokbuf, 256, NULL);
          if ((keys = GRN_MALLOCN(grn_table_sort_key, n))) {
            int i, n_keys = 0;
            for (i = 0; i < n; i++) {
              uint32_t len = (uint32_t) (tokbuf[i] - p);
              grn_obj *col = grn_obj_column(ctx, table, p, len);
              if (col) {
                keys[n_keys].key = col;
                keys[n_keys].flags = GRN_TABLE_SORT_ASC;
                keys[n_keys].offset = 0;
                n_keys++;
              } else if (n_keys) {
                if (p[0] == ':' && p[1] == 'd' && len == 2) {
                  keys[n_keys - 1].flags |= GRN_TABLE_SORT_DESC;
                } else {
                  keys[n_keys - 1].offset = grn_atoi(p, p + len, NULL);
                }
              }
              p = tokbuf[i] + 1;
            }
            /* todo : support multi-results */
            results.table = res;
            results.key_begin = 0;
            results.key_end = 0;
            results.limit = 0;
            results.flags = 0;
            results.op = GRN_OP_OR;
            WITH_SPSAVE({
              grn_table_group(ctx, table, keys, n_keys, &results, 1);
            });
            for (i = 0; i < n_keys; i++) {
              grn_obj_unlink(ctx, keys[i].key);
            }
            GRN_FREE(keys);
          }
        }
      }
      code++;
      break;
    case GRN_OP_JSON_PUT :
      {
        grn_obj_format format;
        grn_obj *res = NULL;
        grn_obj *str = NULL;
        grn_obj *table = NULL;
        POP1(res);
        res = GRN_OBJ_RESOLVE(ctx, res);
        POP1(str);
        str = GRN_OBJ_RESOLVE(ctx, str);
        POP1(table);
        table = GRN_OBJ_RESOLVE(ctx, table);
        GRN_OBJ_FORMAT_INIT(&format, grn_table_size(ctx, table), 0, -1, 0);
        format.flags = 0;
        grn_obj_columns(ctx, table,
                        GRN_TEXT_VALUE(str), GRN_TEXT_LEN(str), &format.columns);
        grn_text_otoj(ctx, res, table, &format);
        GRN_OBJ_FORMAT_FIN(ctx, &format);
      }
      code++;
      break;
    case GRN_OP_AND :
      {
        grn_obj *x = NULL;
        grn_obj *y = NULL;
        grn_obj *result = NULL;
        POP2ALLOC1(x, y, res);
        if (grn_obj_is_true(ctx, x)) {
          if (grn_obj_is_true(ctx, y)) {
            result = y;
          }
        }
        if (result) {
          if (res != result) {
            grn_obj_reinit(ctx, res, result->header.domain, 0);
            grn_obj_cast(ctx, result, res, GRN_FALSE);
          }
        } else {
          grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
          GRN_BOOL_SET(ctx, res, GRN_FALSE);
        }
      }
      code++;
      break;
    case GRN_OP_OR :
      {
        grn_obj *x = NULL;
        grn_obj *y = NULL;
        grn_obj *result = NULL;
        POP2ALLOC1(x, y, res);
        if (grn_obj_is_true(ctx, x)) {
          result = x;
        } else {
          if (grn_obj_is_true(ctx, y)) {
            result = y;
          } else {
            result = NULL;
          }
        }
        if (result) {
          if (res != result) {
            grn_obj_reinit(ctx, res, result->header.domain, 0);
            grn_obj_cast(ctx, result, res, GRN_FALSE);
          }
        } else {
          grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
          GRN_BOOL_SET(ctx, res, GRN_FALSE);
        }
      }
      code++;
      break;
    case GRN_OP_AND_NOT :
      {
        grn_obj *x = NULL;
        grn_obj *y = NULL;
        grn_bool is_true;
        POP2ALLOC1(x, y, res);
        if (!grn_obj_is_true(ctx, x) || grn_obj_is_true(ctx, y)) {
          is_true = GRN_FALSE;
        } else {
          is_true = GRN_TRUE;
        }
        grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
        GRN_BOOL_SET(ctx, res, is_true);
      }
      code++;
      break;
    case GRN_OP_ADJUST :
      {
        /* todo */
      }
      code++;
      break;
    case GRN_OP_MATCH :
      {
        grn_obj *x = NULL;
        grn_obj *y = NULL;
        grn_bool matched;
        POP1(y);
        POP1(x);
        WITH_SPSAVE({
          matched = grn_operator_exec_match(ctx, x, y);
        });
        ALLOC1(res);
        grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
        GRN_BOOL_SET(ctx, res, matched);
      }
      code++;
      break;
    case GRN_OP_EQUAL :
      {
        grn_bool is_equal;
        grn_obj *x = NULL;
        grn_obj *y = NULL;
        POP2ALLOC1(x, y, res);
        is_equal = grn_operator_exec_equal(ctx, x, y);
        grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
        GRN_BOOL_SET(ctx, res, is_equal);
      }
      code++;
      break;
    case GRN_OP_NOT_EQUAL :
      {
        grn_bool is_not_equal;
        grn_obj *x = NULL;
        grn_obj *y = NULL;
        POP2ALLOC1(x, y, res);
        is_not_equal = grn_operator_exec_not_equal(ctx, x, y);
        grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
        GRN_BOOL_SET(ctx, res, is_not_equal);
      }
      code++;
      break;
    case GRN_OP_PREFIX :
      {
        grn_obj *x = NULL;
        grn_obj *y = NULL;
        grn_bool matched;
        POP1(y);
        POP1(x);
        WITH_SPSAVE({
          matched = grn_operator_exec_prefix(ctx, x, y);
        });
        ALLOC1(res);
        grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
        GRN_BOOL_SET(ctx, res, matched);
      }
      code++;
      break;
    case GRN_OP_SUFFIX :
      {
        grn_obj *x = NULL;
        grn_obj *y = NULL;
        grn_bool matched = GRN_FALSE;
        POP2ALLOC1(x, y, res);
        if (GRN_TEXT_LEN(x) >= GRN_TEXT_LEN(y) &&
            !memcmp(GRN_TEXT_VALUE(x) + GRN_TEXT_LEN(x) - GRN_TEXT_LEN(y),
                    GRN_TEXT_VALUE(y), GRN_TEXT_LEN(y))) {
          matched = GRN_TRUE;
        }
        grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
        GRN_BOOL_SET(ctx, res, matched);
      }
      code++;
      break;
    case GRN_OP_LESS :
      {
        grn_bool r;
        grn_obj *x = NULL;
        grn_obj *y = NULL;
        POP2ALLOC1(x, y, res);
        r = grn_operator_exec_less(ctx, x, y);
        grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
        GRN_BOOL_SET(ctx, res, r);
      }
      code++;
      break;
    case GRN_OP_GREATER :
      {
        grn_bool r;
        grn_obj *x = NULL;
        grn_obj *y = NULL;
        POP2ALLOC1(x, y, res);
        r = grn_operator_exec_greater(ctx, x, y);
        grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
        GRN_BOOL_SET(ctx, res, r);
      }
      code++;
      break;
    case GRN_OP_LESS_EQUAL :
      {
        grn_bool r;
        grn_obj *x = NULL;
        grn_obj *y = NULL;
        POP2ALLOC1(x, y, res);
        r = grn_operator_exec_less_equal(ctx, x, y);
        grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
        GRN_BOOL_SET(ctx, res, r);
      }
      code++;
      break;
    case GRN_OP_GREATER_EQUAL :
      {
        grn_bool r;
        grn_obj *x = NULL;
        grn_obj *y = NULL;
        POP2ALLOC1(x, y, res);
        r = grn_operator_exec_greater_equal(ctx, x, y);
        grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
        GRN_BOOL_SET(ctx, res, r);
      }
      code++;
      break;
    case GRN_OP_GEO_DISTANCE1 :
      {
        grn_obj *value = NULL;
        double lng1, lat1, lng2, lat2, x, y, d;
        POP1(value);
        lng1 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1(value);
        lat1 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1(value);
        lng2 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1ALLOC1(value, res);
        lat2 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        x = (lng2 - lng1) * cos((lat1 + lat2) * 0.5);
        y = (lat2 - lat1);
        d = sqrt((x * x) + (y * y)) * GEO_RADIOUS;
        res->header.type = GRN_BULK;
        res->header.domain = GRN_DB_FLOAT;
        GRN_FLOAT_SET(ctx, res, d);
      }
      code++;
      break;
    case GRN_OP_GEO_DISTANCE2 :
      {
        grn_obj *value = NULL;
        double lng1, lat1, lng2, lat2, x, y, d;
        POP1(value);
        lng1 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1(value);
        lat1 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1(value);
        lng2 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1ALLOC1(value, res);
        lat2 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        x = sin(fabs(lng2 - lng1) * 0.5);
        y = sin(fabs(lat2 - lat1) * 0.5);
        d = asin(sqrt((y * y) + cos(lat1) * cos(lat2) * x * x)) * 2 * GEO_RADIOUS;
        res->header.type = GRN_BULK;
        res->header.domain = GRN_DB_FLOAT;
        GRN_FLOAT_SET(ctx, res, d);
      }
      code++;
      break;
    case GRN_OP_GEO_DISTANCE3 :
      {
        grn_obj *value = NULL;
        double lng1, lat1, lng2, lat2, p, q, m, n, x, y, d;
        POP1(value);
        lng1 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1(value);
        lat1 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1(value);
        lng2 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1ALLOC1(value, res);
        lat2 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        p = (lat1 + lat2) * 0.5;
        q = (1 - GEO_BES_C3 * sin(p) * sin(p));
        m = GEO_BES_C1 / sqrt(q * q * q);
        n = GEO_BES_C2 / sqrt(q);
        x = n * cos(p) * fabs(lng1 - lng2);
        y = m * fabs(lat1 - lat2);
        d = sqrt((x * x) + (y * y));
        res->header.type = GRN_BULK;
        res->header.domain = GRN_DB_FLOAT;
        GRN_FLOAT_SET(ctx, res, d);
      }
      code++;
      break;
    case GRN_OP_GEO_DISTANCE4 :
      {
        grn_obj *value = NULL;
        double lng1, lat1, lng2, lat2, p, q, m, n, x, y, d;
        POP1(value);
        lng1 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1(value);
        lat1 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1(value);
        lng2 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1ALLOC1(value, res);
        lat2 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        p = (lat1 + lat2) * 0.5;
        q = (1 - GEO_GRS_C3 * sin(p) * sin(p));
        m = GEO_GRS_C1 / sqrt(q * q * q);
        n = GEO_GRS_C2 / sqrt(q);
        x = n * cos(p) * fabs(lng1 - lng2);
        y = m * fabs(lat1 - lat2);
        d = sqrt((x * x) + (y * y));
        res->header.type = GRN_BULK;
        res->header.domain = GRN_DB_FLOAT;
        GRN_FLOAT_SET(ctx, res, d);
      }
      code++;
      break;
    case GRN_OP_GEO_WITHINP5 :
      {
        int r;
        grn_obj *value = NULL;
        double lng0, lat0, lng1, lat1, x, y, d;
        POP1(value);
        lng0 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1(value);
        lat0 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1(value);
        lng1 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1(value);
        lat1 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1ALLOC1(value, res);
        x = (lng1 - lng0) * cos((lat0 + lat1) * 0.5);
        y = (lat1 - lat0);
        d = sqrt((x * x) + (y * y)) * GEO_RADIOUS;
        switch (value->header.domain) {
        case GRN_DB_INT32 :
          r = d <= GRN_INT32_VALUE(value);
          break;
        case GRN_DB_FLOAT :
          r = d <= GRN_FLOAT_VALUE(value);
          break;
        default :
          r = 0;
          break;
        }
        GRN_INT32_SET(ctx, res, r);
        res->header.type = GRN_BULK;
        res->header.domain = GRN_DB_INT32;
      }
      code++;
      break;
    case GRN_OP_GEO_WITHINP6 :
      {
        int r;
        grn_obj *value = NULL;
        double lng0, lat0, lng1, lat1, lng2, lat2, x, y, d;
        POP1(value);
        lng0 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1(value);
        lat0 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1(value);
        lng1 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1(value);
        lat1 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1(value);
        lng2 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        POP1ALLOC1(value, res);
        lat2 = GRN_GEO_INT2RAD(GRN_INT32_VALUE(value));
        x = (lng1 - lng0) * cos((lat0 + lat1) * 0.5);
        y = (lat1 - lat0);
        d = (x * x) + (y * y);
        x = (lng2 - lng1) * cos((lat1 + lat2) * 0.5);
        y = (lat2 - lat1);
        r = d <= (x * x) + (y * y);
        GRN_INT32_SET(ctx, res, r);
        res->header.type = GRN_BULK;
        res->header.domain = GRN_DB_INT32;
      }
      code++;
      break;
    case GRN_OP_GEO_WITHINP8 :
      {
        int r;
        grn_obj *value = NULL;
        int64_t ln0, la0, ln1, la1, ln2, la2, ln3, la3;
        POP1(value);
        ln0 = GRN_INT32_VALUE(value);
        POP1(value);
        la0 = GRN_INT32_VALUE(value);
        POP1(value);
        ln1 = GRN_INT32_VALUE(value);
        POP1(value);
        la1 = GRN_INT32_VALUE(value);
        POP1(value);
        ln2 = GRN_INT32_VALUE(value);
        POP1(value);
        la2 = GRN_INT32_VALUE(value);
        POP1(value);
        ln3 = GRN_INT32_VALUE(value);
        POP1ALLOC1(value, res);
        la3 = GRN_INT32_VALUE(value);
        r = ((ln2 <= ln0) && (ln0 <= ln3) && (la2 <= la0) && (la0 <= la3));
        GRN_INT32_SET(ctx, res, r);
        res->header.type = GRN_BULK;
        res->header.domain = GRN_DB_INT32;
      }
      code++;
      break;
    case GRN_OP_PLUS :
      ARITHMETIC_BINARY_OPERATION_DISPATCH(
        "+",
        INTEGER_ARITHMETIC_OPERATION_PLUS,
        INTEGER_ARITHMETIC_OPERATION_PLUS,
        INTEGER_ARITHMETIC_OPERATION_PLUS,
        INTEGER_ARITHMETIC_OPERATION_PLUS,
        FLOAT_ARITHMETIC_OPERATION_PLUS,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        {
          if (x == res) {
            grn_obj_cast(ctx, y, res, GRN_FALSE);
          } else if (y == res) {
            grn_obj buffer;
            GRN_TEXT_INIT(&buffer, 0);
            grn_obj_cast(ctx, x, &buffer, GRN_FALSE);
            grn_obj_cast(ctx, y, &buffer, GRN_FALSE);
            GRN_BULK_REWIND(res);
            grn_obj_reinit(ctx, res, GRN_DB_TEXT, 0);
            grn_obj_cast(ctx, &buffer, res, GRN_FALSE);
            GRN_OBJ_FIN(ctx, &buffer);
          } else {
            grn_obj_reinit(ctx, res, GRN_DB_TEXT, 0);
            grn_obj_cast(ctx, x, res, GRN_FALSE);
            grn_obj_cast(ctx, y, res, GRN_FALSE);
          }
        }
        ,);
      break;
    case GRN_OP_MINUS :
      if (code->nargs == 1) {
        ARITHMETIC_UNARY_OPERATION_DISPATCH(
          INTEGER_UNARY_ARITHMETIC_OPERATION_MINUS,
          FLOAT_UNARY_ARITHMETIC_OPERATION_MINUS,
          ARITHMETIC_OPERATION_NO_CHECK,
          ARITHMETIC_OPERATION_NO_CHECK,
          TEXT_UNARY_ARITHMETIC_OPERATION(-),);
      } else {
        ARITHMETIC_BINARY_OPERATION_DISPATCH(
          "-",
          INTEGER_ARITHMETIC_OPERATION_MINUS,
          INTEGER_ARITHMETIC_OPERATION_MINUS,
          INTEGER_ARITHMETIC_OPERATION_MINUS,
          INTEGER_ARITHMETIC_OPERATION_MINUS,
          FLOAT_ARITHMETIC_OPERATION_MINUS,
          ARITHMETIC_OPERATION_NO_CHECK,
          ARITHMETIC_OPERATION_NO_CHECK,
          {
            ERR(GRN_INVALID_ARGUMENT,
                "\"string\" - \"string\" "
                "isn't supported");
            goto exit;
          }
          ,);
      }
      break;
    case GRN_OP_STAR :
      ARITHMETIC_BINARY_OPERATION_DISPATCH(
        "*",
        INTEGER_ARITHMETIC_OPERATION_STAR,
        INTEGER_ARITHMETIC_OPERATION_STAR,
        INTEGER_ARITHMETIC_OPERATION_STAR,
        INTEGER_ARITHMETIC_OPERATION_STAR,
        FLOAT_ARITHMETIC_OPERATION_STAR,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        {
          ERR(GRN_INVALID_ARGUMENT,
              "\"string\" * \"string\" "
              "isn't supported");
          goto exit;
        }
        ,);
      break;
    case GRN_OP_SLASH :
      DIVISION_OPERATION_DISPATCH(
        SIGNED_INTEGER_DIVISION_OPERATION_SLASH,
        UNSIGNED_INTEGER_DIVISION_OPERATION_SLASH,
        FLOAT_DIVISION_OPERATION_SLASH,
        {
          ERR(GRN_INVALID_ARGUMENT,
              "\"string\" / \"string\" "
              "isn't supported");
          goto exit;
        });
      break;
    case GRN_OP_MOD :
      DIVISION_OPERATION_DISPATCH(
        SIGNED_INTEGER_DIVISION_OPERATION_MOD,
        UNSIGNED_INTEGER_DIVISION_OPERATION_MOD,
        FLOAT_DIVISION_OPERATION_MOD,
        {
          ERR(GRN_INVALID_ARGUMENT,
              "\"string\" %% \"string\" "
              "isn't supported");
          goto exit;
        });
      break;
    case GRN_OP_BITWISE_NOT :
      ARITHMETIC_UNARY_OPERATION_DISPATCH(
        INTEGER_UNARY_ARITHMETIC_OPERATION_BITWISE_NOT,
        FLOAT_UNARY_ARITHMETIC_OPERATION_BITWISE_NOT,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        TEXT_UNARY_ARITHMETIC_OPERATION(~),);
      break;
    case GRN_OP_BITWISE_OR :
      ARITHMETIC_BINARY_OPERATION_DISPATCH(
        "|",
        INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
        FLOAT_ARITHMETIC_OPERATION_BITWISE_OR,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        TEXT_ARITHMETIC_OPERATION(|),);
      break;
    case GRN_OP_BITWISE_XOR :
      ARITHMETIC_BINARY_OPERATION_DISPATCH(
        "^",
        INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
        FLOAT_ARITHMETIC_OPERATION_BITWISE_XOR,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        TEXT_ARITHMETIC_OPERATION(^),);
      break;
    case GRN_OP_BITWISE_AND :
      ARITHMETIC_BINARY_OPERATION_DISPATCH(
        "&",
        INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
        INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
        FLOAT_ARITHMETIC_OPERATION_BITWISE_AND,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        TEXT_ARITHMETIC_OPERATION(&),);
      break;
    case GRN_OP_SHIFTL :
      ARITHMETIC_BINARY_OPERATION_DISPATCH(
        "<<",
        INTEGER_ARITHMETIC_OPERATION_SHIFTL,
        INTEGER_ARITHMETIC_OPERATION_SHIFTL,
        INTEGER_ARITHMETIC_OPERATION_SHIFTL,
        INTEGER_ARITHMETIC_OPERATION_SHIFTL,
        FLOAT_ARITHMETIC_OPERATION_SHIFTL,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        TEXT_ARITHMETIC_OPERATION(<<),);
      break;
    case GRN_OP_SHIFTR :
      ARITHMETIC_BINARY_OPERATION_DISPATCH(
        ">>",
        INTEGER_ARITHMETIC_OPERATION_SHIFTR,
        INTEGER_ARITHMETIC_OPERATION_SHIFTR,
        INTEGER_ARITHMETIC_OPERATION_SHIFTR,
        INTEGER_ARITHMETIC_OPERATION_SHIFTR,
        FLOAT_ARITHMETIC_OPERATION_SHIFTR,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        TEXT_ARITHMETIC_OPERATION(>>),);
      break;
    case GRN_OP_SHIFTRR :
      ARITHMETIC_BINARY_OPERATION_DISPATCH(
        ">>>",
        INTEGER8_ARITHMETIC_OPERATION_SHIFTRR,
        INTEGER16_ARITHMETIC_OPERATION_SHIFTRR,
        INTEGER32_ARITHMETIC_OPERATION_SHIFTRR,
        INTEGER64_ARITHMETIC_OPERATION_SHIFTRR,
        FLOAT_ARITHMETIC_OPERATION_SHIFTRR,
        ARITHMETIC_OPERATION_NO_CHECK,
        ARITHMETIC_OPERATION_NO_CHECK,
        {
          long long unsigned int x_;
          long long unsigned int y_;

          res->header.type = GRN_BULK;
          res->header.domain = GRN_DB_INT64;

          GRN_INT64_SET(ctx, res, 0);
          grn_obj_cast(ctx, x, res, GRN_FALSE);
          x_ = GRN_INT64_VALUE(res);

          GRN_INT64_SET(ctx, res, 0);
          grn_obj_cast(ctx, y, res, GRN_FALSE);
          y_ = GRN_INT64_VALUE(res);

          GRN_INT64_SET(ctx, res, x_ >> y_);
        }
        ,);
      break;
    case GRN_OP_INCR :
      UNARY_OPERATE_AND_ASSIGN_DISPATCH(EXEC_OPERATE, 1, GRN_OBJ_INCR);
      break;
    case GRN_OP_DECR :
      UNARY_OPERATE_AND_ASSIGN_DISPATCH(EXEC_OPERATE, 1, GRN_OBJ_DECR);
      break;
    case GRN_OP_INCR_POST :
      UNARY_OPERATE_AND_ASSIGN_DISPATCH(EXEC_OPERATE_POST, 1, GRN_OBJ_INCR);
      break;
    case GRN_OP_DECR_POST :
      UNARY_OPERATE_AND_ASSIGN_DISPATCH(EXEC_OPERATE_POST, 1, GRN_OBJ_DECR);
      break;
    case GRN_OP_NOT :
      {
        grn_obj *value = NULL;
        grn_bool value_boolean;
        POP1ALLOC1(value, res);
        GRN_OBJ_IS_TRUE(ctx, value, value_boolean);
        grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
        GRN_BOOL_SET(ctx, res, !value_boolean);
      }
      code++;
      break;
    case GRN_OP_GET_MEMBER :
      {
        grn_obj *receiver = NULL;
        grn_obj *index_or_key = NULL;
        POP2ALLOC1(receiver, index_or_key, res);
        if (receiver->header.type == GRN_PTR) {
          grn_obj *index = index_or_key;
          grn_expr_exec_get_member_vector(ctx, expr, receiver, index, res);
        } else {
          grn_obj *key = index_or_key;
          grn_expr_exec_get_member_table(ctx, expr, receiver, key, res);
        }
        code++;
      }
      break;
    case GRN_OP_REGEXP :
      {
        grn_obj *target = NULL;
        grn_obj *pattern = NULL;
        grn_bool matched;
        POP1(pattern);
        POP1(target);
        WITH_SPSAVE({
          matched = grn_operator_exec_regexp(ctx, target, pattern);
        });
        ALLOC1(res);
        grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
        GRN_BOOL_SET(ctx, res, matched);
      }
      code++;
      break;
    default :
      ERR(GRN_FUNCTION_NOT_IMPLEMENTED, "not implemented operator assigned");
      goto exit;
      break;
    }
  }
  ctx->impl->stack_curr = sp - s_;
  if (ctx->impl->stack_curr > stack_curr) {
    val = grn_ctx_pop(ctx);
  }
exit :
  if (ctx->impl->stack_curr > stack_curr) {
    /*
      GRN_LOG(ctx, GRN_LOG_WARNING, "stack balance=%d",
      stack_curr - ctx->impl->stack_curr);
    */
    ctx->impl->stack_curr = stack_curr;
  }
  return val;
}

static grn_obj *
grn_expr_executor_exec_general(grn_ctx *ctx,
                               grn_expr_executor *executor,
                               grn_id id)
{
  if (!executor->variable && id != GRN_ID_NIL) {
    ERR(GRN_INVALID_ARGUMENT,
        "[expr-executor][exec][general] can't specify ID for no variable expr");
    return NULL;
  }
  if (executor->variable) {
    GRN_RECORD_SET(ctx, executor->variable, id);
  }
  return expr_exec(ctx, executor->expr);
}

static void
grn_expr_executor_fin_general(grn_ctx *ctx,
                              grn_expr_executor *executor)
{
}

static void
grn_expr_executor_init_constant(grn_ctx *ctx,
                                grn_expr_executor *executor)
{
  grn_expr *e = (grn_expr *)(executor->expr);

  executor->data.constant.value = e->codes[0].value;
}

static grn_bool
grn_expr_executor_is_constant(grn_ctx *ctx,
                              grn_expr_executor *executor)
{
  grn_expr *e = (grn_expr *)(executor->expr);
  grn_expr_code *target;

  if (e->codes_curr != 1) {
    return GRN_FALSE;
  }

  target = &(e->codes[0]);

  if (target->op != GRN_OP_PUSH) {
    return GRN_FALSE;
  }
  if (!target->value) {
    return GRN_FALSE;
  }

  grn_expr_executor_init_constant(ctx, executor);

  return GRN_TRUE;
}

static grn_obj *
grn_expr_executor_exec_constant(grn_ctx *ctx,
                                grn_expr_executor *executor,
                                grn_id id)
{
  return executor->data.constant.value;
}

static void
grn_expr_executor_fin_constant(grn_ctx *ctx,
                               grn_expr_executor *executor)
{
}

static void
grn_expr_executor_init_value(grn_ctx *ctx,
                             grn_expr_executor *executor)
{
  grn_expr *e = (grn_expr *)(executor->expr);

  executor->data.value.column = e->codes[0].value;
  GRN_VOID_INIT(&(executor->data.value.value_buffer));
}

static grn_bool
grn_expr_executor_is_value(grn_ctx *ctx,
                           grn_expr_executor *executor)
{
  grn_expr *e = (grn_expr *)(executor->expr);
  grn_expr_code *target;

  if (e->codes_curr != 1) {
    return GRN_FALSE;
  }

  target = &(e->codes[0]);

  if (target->op != GRN_OP_GET_VALUE) {
    return GRN_FALSE;
  }
  if (!target->value) {
    return GRN_FALSE;
  }

  grn_expr_executor_init_value(ctx, executor);

  return GRN_TRUE;
}

static grn_obj *
grn_expr_executor_exec_value(grn_ctx *ctx,
                             grn_expr_executor *executor,
                             grn_id id)
{
  grn_obj *value_buffer = &(executor->data.value.value_buffer);

  GRN_BULK_REWIND(value_buffer);
  grn_obj_get_value(ctx, executor->data.value.column, id, value_buffer);

  return value_buffer;
}

static void
grn_expr_executor_fin_value(grn_ctx *ctx,
                            grn_expr_executor *executor)
{
  GRN_OBJ_FIN(ctx, &(executor->data.value.value_buffer));
}

#ifdef GRN_SUPPORT_REGEXP
static void
grn_expr_executor_init_simple_regexp(grn_ctx *ctx,
                                     grn_expr_executor *executor)
{
  grn_expr *e = (grn_expr *)(executor->expr);
  grn_obj *result_buffer = &(executor->data.simple_regexp.result_buffer);
  grn_obj *pattern;

  GRN_BOOL_INIT(result_buffer, 0);
  GRN_BOOL_SET(ctx, result_buffer, GRN_FALSE);

  pattern = e->codes[1].value;
  executor->data.simple_regexp.regex =
    grn_onigmo_new(ctx,
                   GRN_TEXT_VALUE(pattern),
                   GRN_TEXT_LEN(pattern),
                   GRN_ONIGMO_OPTION_DEFAULT,
                   GRN_ONIGMO_SYNTAX_DEFAULT,
                   "[expr-executor]");
  if (!executor->data.simple_regexp.regex) {
    return;
  }

  GRN_VOID_INIT(&(executor->data.simple_regexp.value_buffer));

  executor->data.simple_regexp.normalizer =
    grn_ctx_get(ctx, GRN_NORMALIZER_AUTO_NAME, -1);
}

static grn_bool
grn_expr_executor_is_simple_regexp(grn_ctx *ctx,
                                   grn_expr_executor *executor)
{
  grn_expr *e = (grn_expr *)(executor->expr);
  grn_expr_code *target;
  grn_expr_code *pattern;
  grn_expr_code *operator;

  if (e->codes_curr != 3) {
    return GRN_FALSE;
  }

  target = &(e->codes[0]);
  pattern = &(e->codes[1]);
  operator = &(e->codes[2]);

  if (operator->op != GRN_OP_REGEXP) {
    return GRN_FALSE;
  }
  if (operator->nargs != 2) {
    return GRN_FALSE;
  }

  if (target->op != GRN_OP_GET_VALUE) {
    return GRN_FALSE;
  }
  if (target->nargs != 1) {
    return GRN_FALSE;
  }
  if (!target->value) {
    return GRN_FALSE;
  }
  if (target->value->header.type != GRN_COLUMN_VAR_SIZE) {
    return GRN_FALSE;
  }
  if ((target->value->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) !=
      GRN_OBJ_COLUMN_SCALAR) {
    return GRN_FALSE;
  }
  switch (grn_obj_get_range(ctx, target->value)) {
  case GRN_DB_SHORT_TEXT :
  case GRN_DB_TEXT :
  case GRN_DB_LONG_TEXT :
    break;
  default :
    return GRN_FALSE;
  }

  if (pattern->op != GRN_OP_PUSH) {
    return GRN_FALSE;
  }
  if (pattern->nargs != 1) {
    return GRN_FALSE;
  }
  if (!pattern->value) {
    return GRN_FALSE;
  }
  if (pattern->value->header.type != GRN_BULK) {
    return GRN_FALSE;
  }
  switch (pattern->value->header.domain) {
  case GRN_DB_SHORT_TEXT :
  case GRN_DB_TEXT :
  case GRN_DB_LONG_TEXT :
    break;
  default :
    return GRN_FALSE;
  }

  if (!grn_onigmo_is_valid_encoding(ctx)) {
    return GRN_FALSE;
  }

  grn_expr_executor_init_simple_regexp(ctx, executor);

  return GRN_TRUE;
}

static grn_obj *
grn_expr_executor_exec_simple_regexp(grn_ctx *ctx,
                                     grn_expr_executor *executor,
                                     grn_id id)
{
  grn_expr *e = (grn_expr *)(executor->expr);
  OnigRegex regex = executor->data.simple_regexp.regex;
  grn_obj *value_buffer = &(executor->data.simple_regexp.value_buffer);
  grn_obj *result_buffer = &(executor->data.simple_regexp.result_buffer);

  if (ctx->rc) {
    GRN_BOOL_SET(ctx, result_buffer, GRN_FALSE);
    return result_buffer;
  }

  if (!regex) {
    return result_buffer;
  }

  grn_obj_reinit_for(ctx, value_buffer, e->codes[0].value);
  grn_obj_get_value(ctx, e->codes[0].value, id, value_buffer);
  {
    grn_obj *norm_target;
    const char *norm_target_raw;
    unsigned int norm_target_raw_length_in_bytes;

    norm_target = grn_string_open(ctx,
                                  GRN_TEXT_VALUE(value_buffer),
                                  GRN_TEXT_LEN(value_buffer),
                                  executor->data.simple_regexp.normalizer,
                                  0);
    grn_string_get_normalized(ctx, norm_target,
                              &norm_target_raw,
                              &norm_target_raw_length_in_bytes,
                              NULL);

    {
      OnigPosition position;
      position = onig_search(regex,
                             norm_target_raw,
                             norm_target_raw + norm_target_raw_length_in_bytes,
                             norm_target_raw,
                             norm_target_raw + norm_target_raw_length_in_bytes,
                             NULL,
                             ONIG_OPTION_NONE);
      grn_obj_close(ctx, norm_target);
      GRN_BOOL_SET(ctx, result_buffer, (position != ONIG_MISMATCH));
      return result_buffer;
    }
  }
}

static void
grn_expr_executor_fin_simple_regexp(grn_ctx *ctx,
                                    grn_expr_executor *executor)
{
  GRN_OBJ_FIN(ctx, &(executor->data.simple_regexp.result_buffer));

  if (!executor->data.simple_regexp.regex) {
    return;
  }

  onig_free(executor->data.simple_regexp.regex);
  GRN_OBJ_FIN(ctx, &(executor->data.simple_regexp.value_buffer));
}
#endif /* GRN_SUPPORT_REGEXP */

static void
grn_expr_executor_init_proc(grn_ctx *ctx,
                            grn_expr_executor *executor)
{
  grn_proc_ctx *proc_ctx = &(executor->data.proc.proc_ctx);
  grn_expr *expr;
  grn_proc *proc;

  expr = (grn_expr *)(executor->expr);
  proc = (grn_proc *)(expr->codes[0].value);
  proc_ctx->proc = proc;
  proc_ctx->caller = executor->expr;
  proc_ctx->phase = PROC_INIT;

  executor->data.proc.n_args = expr->codes[expr->codes_curr - 1].nargs - 1;

  proc->funcs[PROC_INIT](ctx, 0, NULL, &(proc_ctx->user_data));
}

static grn_bool
grn_expr_executor_is_proc(grn_ctx *ctx,
                          grn_expr_executor *executor)
{
  grn_expr *e = (grn_expr *)(executor->expr);
  grn_obj *first;
  grn_proc *proc;

  if (e->codes_curr < 2) {
    return GRN_FALSE;
  }

  first = e->codes[0].value;
  if (!grn_obj_is_function_proc(ctx, first)) {
    return GRN_FALSE;
  }

  proc = (grn_proc *)first;
  if (!(proc->funcs[PROC_INIT] &&
        proc->funcs[PROC_NEXT])) {
    return GRN_FALSE;
  }

  if (e->codes[e->codes_curr - 1].op != GRN_OP_CALL) {
    return GRN_FALSE;
  }

  grn_expr_executor_init_proc(ctx, executor);

  return GRN_TRUE;
}

static grn_obj *
grn_expr_executor_exec_proc(grn_ctx *ctx,
                            grn_expr_executor *executor,
                            grn_id id)
{
  grn_proc_ctx *proc_ctx = &(executor->data.proc.proc_ctx);
  int n_args = executor->data.proc.n_args;
  grn_proc *proc;
  grn_expr *expr;
  grn_obj **args;
  uint32_t values_curr;
  uint32_t values_tail;
  grn_obj *result;

  proc = proc_ctx->proc;
  proc_ctx->phase = PROC_NEXT;
  if (executor->variable) {
    GRN_RECORD_SET(ctx, executor->variable, id);
  }

  expr = (grn_expr *)(executor->expr);
  values_curr = expr->values_curr;
  values_tail = expr->values_tail;

  expr->codes += 1;
  expr->codes_curr -= 2;
  grn_expr_exec(ctx, executor->expr, 0);
  expr->codes_curr += 2;
  expr->codes -= 1;

  args = ctx->impl->stack + ctx->impl->stack_curr;
  while (ctx->impl->stack_curr + n_args > ctx->impl->stack_size) {
    if (grn_ctx_expand_stack(ctx) != GRN_SUCCESS) {
      return NULL;
    }
  }
  ctx->impl->stack_curr += n_args;
  expr->values_curr = expr->values_tail;
  result = proc->funcs[PROC_NEXT](ctx,
                                  n_args,
                                  args,
                                  &(proc_ctx->user_data));
  ctx->impl->stack_curr -= n_args;

  expr->values_tail = values_tail;
  expr->values_curr = values_curr;

  return result;
}

static void
grn_expr_executor_fin_proc(grn_ctx *ctx,
                           grn_expr_executor *executor)
{
  grn_proc_ctx *proc_ctx = &(executor->data.proc.proc_ctx);
  grn_proc *proc;

  proc = proc_ctx->proc;
  proc_ctx->phase = PROC_FIN;
  if (proc->funcs[PROC_FIN]) {
    proc->funcs[PROC_FIN](ctx, 0, NULL, &(proc_ctx->user_data));
  }
}

static grn_bool
grn_expr_executor_is_simple_condition_constant(grn_ctx *ctx,
                                               grn_expr_executor *executor)
{
  grn_expr *e = (grn_expr *)(executor->expr);
  grn_expr_code *target;
  grn_expr_code *constant;
  grn_expr_code *operator;
  grn_id target_range;
  grn_id constant_range;
  grn_bool constant_value_is_int = GRN_TRUE;
  int64_t constant_value_int = 0;
  uint64_t constant_value_uint = 0;
  grn_bool constant_always_over = GRN_FALSE;
  grn_bool constant_always_under = GRN_FALSE;

  if (e->codes_curr != 3) {
    return GRN_FALSE;
  }

  target = &(e->codes[0]);
  constant = &(e->codes[1]);
  operator = &(e->codes[2]);

  switch (operator->op) {
  case GRN_OP_EQUAL :
  case GRN_OP_NOT_EQUAL :
  case GRN_OP_LESS :
  case GRN_OP_GREATER :
  case GRN_OP_LESS_EQUAL :
  case GRN_OP_GREATER_EQUAL :
    break;
  default :
    return GRN_FALSE;
  }
  if (operator->nargs != 2) {
    return GRN_FALSE;
  }

  if (target->op != GRN_OP_GET_VALUE) {
    return GRN_FALSE;
  }
  if (target->nargs != 1) {
    return GRN_FALSE;
  }
  if (target->value->header.type != GRN_COLUMN_FIX_SIZE) {
    return GRN_FALSE;
  }

  target_range = grn_obj_get_range(ctx, target->value);
  if (!(GRN_DB_INT8 <= target_range && target_range <= GRN_DB_UINT64)) {
    return GRN_FALSE;
  }

  if (constant->op != GRN_OP_PUSH) {
    return GRN_FALSE;
  }
  if (constant->nargs != 1) {
    return GRN_FALSE;
  }
  if (!constant->value) {
    return GRN_FALSE;
  }
  if (constant->value->header.type != GRN_BULK) {
    return GRN_FALSE;
  }
  constant_range = constant->value->header.domain;
  if (!(GRN_DB_INT8 <= constant_range && constant_range <= GRN_DB_UINT64)) {
    return GRN_FALSE;
  }

#define CASE_INT(N)                                                     \
  GRN_DB_INT ## N :                                                     \
    constant_value_is_int = GRN_TRUE;                                   \
    constant_value_int = GRN_INT ## N ##_VALUE(constant->value);        \
    break
#define CASE_UINT(N)                                                    \
  GRN_DB_UINT ## N :                                                    \
    constant_value_is_int = GRN_FALSE;                                  \
    constant_value_uint = GRN_UINT ## N ##_VALUE(constant->value);      \
    break

  switch (constant_range) {
  case CASE_INT(8);
  case CASE_UINT(8);
  case CASE_INT(16);
  case CASE_UINT(16);
  case CASE_INT(32);
  case CASE_UINT(32);
  case CASE_INT(64);
  case CASE_UINT(64);
  default :
    return GRN_FALSE;
  }

#undef CASE_INT
#undef CASE_UINT

#define CASE_INT(N)                                                     \
  GRN_DB_INT ## N :                                                     \
    if (constant_value_is_int) {                                        \
      if (constant_value_int > INT ## N ## _MAX) {                      \
        constant_always_over = GRN_TRUE;                                \
      } else if (constant_value_int < INT ## N ## _MIN) {               \
        constant_always_under = GRN_TRUE;                               \
      }                                                                 \
    } else {                                                            \
      if (constant_value_uint > INT ## N ## _MAX) {                     \
        constant_always_over = GRN_TRUE;                                \
      }                                                                 \
    }                                                                   \
    break
#define CASE_UINT(N)                                                    \
  GRN_DB_UINT ## N :                                                    \
    if (constant_value_is_int) {                                        \
      if (constant_value_int > UINT ## N ## _MAX) {                     \
        constant_always_over = GRN_TRUE;                                \
      } else if (constant_value_int < 0) {                              \
        constant_always_under = GRN_TRUE;                               \
      }                                                                 \
    } else {                                                            \
      if (constant_value_uint > UINT ## N ## _MAX) {                    \
        constant_always_over = GRN_TRUE;                                \
      }                                                                 \
    }                                                                   \
    break

  switch (target_range) {
  case CASE_INT(8);
  case CASE_UINT(8);
  case CASE_INT(16);
  case CASE_UINT(16);
  case CASE_INT(32);
  case CASE_UINT(32);
  case CASE_INT(64);
  case GRN_DB_UINT64 :
    if (constant_value_is_int && constant_value_int < 0) {
      constant_always_under = GRN_TRUE;
    }
    break;
  default :
    return GRN_FALSE;
  }

  if (!constant_always_over && !constant_always_under) {
    return GRN_FALSE;
  }

  {
    grn_bool result;
    grn_obj *result_buffer =
      &(executor->data.simple_condition_constant.result_buffer);

    switch (operator->op) {
    case GRN_OP_EQUAL :
      result = GRN_FALSE;
      break;
    case GRN_OP_NOT_EQUAL :
      result = GRN_TRUE;
      break;
    case GRN_OP_LESS :
    case GRN_OP_LESS_EQUAL :
      result = constant_always_over;
      break;
    case GRN_OP_GREATER :
    case GRN_OP_GREATER_EQUAL :
      result = constant_always_under;
      break;
    default :
      return GRN_FALSE;
    }

    GRN_BOOL_INIT(result_buffer, 0);
    GRN_BOOL_SET(ctx, result_buffer, result);

    return GRN_TRUE;
  }
}

static grn_obj *
grn_expr_executor_exec_simple_condition_constant(grn_ctx *ctx,
                                                 grn_expr_executor *executor,
                                                 grn_id id)
{
  grn_obj *result_buffer =
    &(executor->data.simple_condition_constant.result_buffer);
  return result_buffer;
}

static void
grn_expr_executor_fin_simple_condition_constant(grn_ctx *ctx,
                                                grn_expr_executor *executor)
{
  GRN_OBJ_FIN(ctx,
              &(executor->data.simple_condition_constant.result_buffer));
}

static void
grn_expr_executor_init_simple_condition_ra(grn_ctx *ctx,
                                           grn_expr_executor *executor)
{
  grn_expr *e = (grn_expr *)(executor->expr);
  grn_obj *target;
  grn_obj *constant;
  grn_operator op;
  grn_obj *result_buffer;
  grn_obj *value_buffer;
  grn_obj *constant_buffer;

  target = e->codes[0].value;
  constant = e->codes[1].value;
  op = e->codes[2].op;

  result_buffer = &(executor->data.simple_condition_ra.result_buffer);
  GRN_BOOL_INIT(result_buffer, 0);
  GRN_BOOL_SET(ctx, result_buffer, GRN_FALSE);

  value_buffer = &(executor->data.simple_condition_ra.value_buffer);
  GRN_VOID_INIT(value_buffer);
  grn_obj_reinit_for(ctx, value_buffer, target);

  executor->data.simple_condition_ra.ra = (grn_ra *)target;
  GRN_RA_CACHE_INIT(executor->data.simple_condition_ra.ra,
                    &(executor->data.simple_condition_ra.ra_cache));
  grn_ra_info(ctx,
              executor->data.simple_condition_ra.ra,
              &(executor->data.simple_condition_ra.ra_element_size));

  executor->data.simple_condition_ra.exec = grn_operator_to_exec_func(op);

  constant_buffer = &(executor->data.simple_condition_ra.constant_buffer);
  if (grn_obj_is_reference_column(ctx, target)) {
    GRN_OBJ_INIT(constant_buffer, GRN_BULK, 0, constant->header.domain);
    grn_obj_cast(ctx, constant, constant_buffer, GRN_FALSE);
  } else {
    GRN_VOID_INIT(constant_buffer);
    grn_obj_reinit_for(ctx, constant_buffer, target);
    grn_obj_cast(ctx, constant, constant_buffer, GRN_FALSE);
  }
}

static grn_bool
grn_expr_executor_is_simple_condition_ra(grn_ctx *ctx,
                                         grn_expr_executor *executor)
{
  grn_expr *e = (grn_expr *)(executor->expr);
  grn_expr_code *target;
  grn_expr_code *constant;
  grn_expr_code *operator;

  if (e->codes_curr != 3) {
    return GRN_FALSE;
  }

  target = &(e->codes[0]);
  constant = &(e->codes[1]);
  operator = &(e->codes[2]);

  switch (operator->op) {
  case GRN_OP_EQUAL :
  case GRN_OP_NOT_EQUAL :
  case GRN_OP_LESS :
  case GRN_OP_GREATER :
  case GRN_OP_LESS_EQUAL :
  case GRN_OP_GREATER_EQUAL :
    break;
  default :
    return GRN_FALSE;
  }
  if (operator->nargs != 2) {
    return GRN_FALSE;
  }

  if (target->op != GRN_OP_GET_VALUE) {
    return GRN_FALSE;
  }
  if (target->nargs != 1) {
    return GRN_FALSE;
  }
  if (target->value->header.type != GRN_COLUMN_FIX_SIZE) {
    return GRN_FALSE;
  }

  if (constant->op != GRN_OP_PUSH) {
    return GRN_FALSE;
  }
  if (constant->nargs != 1) {
    return GRN_FALSE;
  }
  if (!constant->value) {
    return GRN_FALSE;
  }
  if (constant->value->header.type != GRN_BULK) {
    return GRN_FALSE;
  }

  if (!grn_obj_is_reference_column(ctx, target->value)) {
    grn_obj constant_buffer;
    grn_rc rc;
    GRN_VOID_INIT(&constant_buffer);
    grn_obj_reinit_for(ctx, &constant_buffer, target->value);
    rc = grn_obj_cast(ctx, constant->value, &constant_buffer, GRN_FALSE);
    GRN_OBJ_FIN(ctx, &constant_buffer);
    if (rc != GRN_SUCCESS) {
      return GRN_FALSE;
    }
  }

  grn_expr_executor_init_simple_condition_ra(ctx, executor);

  return GRN_TRUE;
}

static grn_obj *
grn_expr_executor_exec_simple_condition_ra(grn_ctx *ctx,
                                           grn_expr_executor *executor,
                                           grn_id id)
{
  grn_obj *result_buffer = &(executor->data.simple_condition_ra.result_buffer);
  grn_obj *value_buffer = &(executor->data.simple_condition_ra.value_buffer);
  grn_obj *constant_buffer =
    &(executor->data.simple_condition_ra.constant_buffer);

  if (ctx->rc) {
    GRN_BOOL_SET(ctx, result_buffer, GRN_FALSE);
    return result_buffer;
  }

  {
    grn_ra *ra = executor->data.simple_condition_ra.ra;
    grn_ra_cache *ra_cache = &(executor->data.simple_condition_ra.ra_cache);
    unsigned int ra_element_size =
      executor->data.simple_condition_ra.ra_element_size;
    void *raw_value;
    raw_value = grn_ra_ref_cache(ctx, ra, id, ra_cache);
    GRN_BULK_REWIND(value_buffer);
    grn_bulk_write(ctx, value_buffer, raw_value, ra_element_size);
  }

  if (executor->data.simple_condition_ra.exec(ctx,
                                              value_buffer,
                                              constant_buffer)) {
    GRN_BOOL_SET(ctx, result_buffer, GRN_TRUE);
  } else {
    GRN_BOOL_SET(ctx, result_buffer, GRN_FALSE);
  }
  return result_buffer;
}

static void
grn_expr_executor_fin_simple_condition_ra(grn_ctx *ctx,
                                          grn_expr_executor *executor)
{
  GRN_OBJ_FIN(ctx, &(executor->data.simple_condition_ra.result_buffer));
  GRN_RA_CACHE_FIN(executor->data.simple_condition_ra.ra,
                   &(executor->data.simple_condition_ra.ra_cache));
  GRN_OBJ_FIN(ctx, &(executor->data.simple_condition_ra.value_buffer));
  GRN_OBJ_FIN(ctx, &(executor->data.simple_condition_ra.constant_buffer));
}

static void
grn_expr_executor_init_simple_condition(grn_ctx *ctx,
                                        grn_expr_executor *executor)
{
  grn_expr *e = (grn_expr *)(executor->expr);
  grn_obj *target;
  grn_obj *constant;
  grn_operator op;
  grn_obj *result_buffer;
  grn_obj *value_buffer;
  grn_obj *constant_buffer;
  grn_rc rc;

  target = e->codes[0].value;
  constant = e->codes[1].value;
  op = e->codes[2].op;

  executor->data.simple_condition.need_exec = GRN_TRUE;

  result_buffer = &(executor->data.simple_condition.result_buffer);
  GRN_BOOL_INIT(result_buffer, 0);
  GRN_BOOL_SET(ctx, result_buffer, GRN_FALSE);

  value_buffer = &(executor->data.simple_condition.value_buffer);
  GRN_VOID_INIT(value_buffer);
  grn_obj_reinit_for(ctx, value_buffer, target);

  executor->data.simple_condition.exec = grn_operator_to_exec_func(op);

  constant_buffer = &(executor->data.simple_condition.constant_buffer);
  GRN_VOID_INIT(constant_buffer);
  grn_obj_reinit_for(ctx, constant_buffer, target);
  rc = grn_obj_cast(ctx, constant, constant_buffer, GRN_FALSE);
  if (rc != GRN_SUCCESS) {
    grn_obj *type;

    type = grn_ctx_at(ctx, constant_buffer->header.domain);
    if (grn_obj_is_table(ctx, type)) {
      GRN_BOOL_SET(ctx, result_buffer, (op == GRN_OP_NOT_EQUAL));
      executor->data.simple_condition.need_exec = GRN_FALSE;
    } else {
      int type_name_size;
      char type_name[GRN_TABLE_MAX_KEY_SIZE];
      grn_obj inspected;

      type_name_size = grn_obj_name(ctx, type, type_name,
                                    GRN_TABLE_MAX_KEY_SIZE);
      GRN_TEXT_INIT(&inspected, 0);
      grn_inspect(ctx, &inspected, constant);
      ERR(rc,
          "[expr-executor][condition] "
          "failed to cast to <%.*s>: <%.*s>",
          type_name_size, type_name,
          (int)GRN_TEXT_LEN(&inspected), GRN_TEXT_VALUE(&inspected));
    }
  }
}

static grn_bool
grn_expr_executor_is_simple_condition(grn_ctx *ctx,
                                      grn_expr_executor *executor)
{
  grn_expr *e = (grn_expr *)(executor->expr);
  grn_expr_code *target;
  grn_expr_code *constant;
  grn_expr_code *operator;

  if (e->codes_curr != 3) {
    return GRN_FALSE;
  }

  target = &(e->codes[0]);
  constant = &(e->codes[1]);
  operator = &(e->codes[2]);

  switch (operator->op) {
  case GRN_OP_EQUAL :
  case GRN_OP_NOT_EQUAL :
  case GRN_OP_LESS :
  case GRN_OP_GREATER :
  case GRN_OP_LESS_EQUAL :
  case GRN_OP_GREATER_EQUAL :
    break;
  default :
    return GRN_FALSE;
  }
  if (operator->nargs != 2) {
    return GRN_FALSE;
  }

  if (target->op != GRN_OP_GET_VALUE) {
    return GRN_FALSE;
  }
  if (target->nargs != 1) {
    return GRN_FALSE;
  }
  if (!grn_obj_is_scalar_column(ctx, target->value)) {
    return GRN_FALSE;
  }

  if (constant->op != GRN_OP_PUSH) {
    return GRN_FALSE;
  }
  if (constant->nargs != 1) {
    return GRN_FALSE;
  }
  if (!constant->value) {
    return GRN_FALSE;
  }
  if (constant->value->header.type != GRN_BULK) {
    return GRN_FALSE;
  }

  grn_expr_executor_init_simple_condition(ctx, executor);

  return GRN_TRUE;
}

static grn_obj *
grn_expr_executor_exec_simple_condition(grn_ctx *ctx,
                                        grn_expr_executor *executor,
                                        grn_id id)
{
  grn_expr *e = (grn_expr *)(executor->expr);
  grn_obj *target;
  grn_obj *result_buffer = &(executor->data.simple_condition.result_buffer);
  grn_obj *value_buffer = &(executor->data.simple_condition.value_buffer);
  grn_obj *constant_buffer = &(executor->data.simple_condition.constant_buffer);

  if (ctx->rc) {
    GRN_BOOL_SET(ctx, result_buffer, GRN_FALSE);
    return result_buffer;
  }

  if (!executor->data.simple_condition.need_exec) {
    return result_buffer;
  }

  target = e->codes[0].value;
  GRN_BULK_REWIND(value_buffer);
  grn_obj_get_value(ctx, target, id, value_buffer);

  if (executor->data.simple_condition.exec(ctx, value_buffer, constant_buffer)) {
    GRN_BOOL_SET(ctx, result_buffer, GRN_TRUE);
  } else {
    GRN_BOOL_SET(ctx, result_buffer, GRN_FALSE);
  }
  return result_buffer;
}

static void
grn_expr_executor_fin_simple_condition(grn_ctx *ctx,
                                       grn_expr_executor *executor)
{
  GRN_OBJ_FIN(ctx, &(executor->data.simple_condition.result_buffer));
  GRN_OBJ_FIN(ctx, &(executor->data.simple_condition.value_buffer));
  GRN_OBJ_FIN(ctx, &(executor->data.simple_condition.constant_buffer));
}

grn_rc
grn_expr_executor_init(grn_ctx *ctx,
                       grn_expr_executor *executor,
                       grn_obj *expr)
{
  GRN_API_ENTER;

  if (!grn_obj_is_expr(ctx, expr)) {
    grn_rc rc = ctx->rc;
    grn_obj inspected;
    GRN_TEXT_INIT(&inspected, 0);
    grn_inspect(ctx, &inspected, expr);
    if (rc == GRN_SUCCESS) {
      rc = GRN_INVALID_ARGUMENT;
    }
    ERR(rc,
        "[expr-executor][init] invalid expression: %.*s",
        (int)GRN_TEXT_LEN(&inspected),
        GRN_TEXT_VALUE(&inspected));
    GRN_OBJ_FIN(ctx, &inspected);
    GRN_API_RETURN(ctx->rc);
  }

  executor->expr = expr;
  executor->variable = grn_expr_get_var_by_offset(ctx, expr, 0);
  if (grn_expr_executor_is_constant(ctx, executor)) {
    executor->exec = grn_expr_executor_exec_constant;
    executor->fin = grn_expr_executor_fin_constant;
  } else if (grn_expr_executor_is_value(ctx, executor)) {
    executor->exec = grn_expr_executor_exec_value;
    executor->fin = grn_expr_executor_fin_value;
#ifdef GRN_SUPPORT_REGEXP
  } else if (grn_expr_executor_is_simple_regexp(ctx, executor)) {
    executor->exec = grn_expr_executor_exec_simple_regexp;
    executor->fin = grn_expr_executor_fin_simple_regexp;
#endif /* GRN_SUPPORT_REGEXP */
  } else if (grn_expr_executor_is_proc(ctx, executor)) {
    executor->exec = grn_expr_executor_exec_proc;
    executor->fin = grn_expr_executor_fin_proc;
  } else if (grn_expr_executor_is_simple_condition_constant(ctx, executor)) {
    executor->exec = grn_expr_executor_exec_simple_condition_constant;
    executor->fin = grn_expr_executor_fin_simple_condition_constant;
  } else if (grn_expr_executor_is_simple_condition_ra(ctx, executor)) {
    executor->exec = grn_expr_executor_exec_simple_condition_ra;
    executor->fin = grn_expr_executor_fin_simple_condition_ra;
  } else if (grn_expr_executor_is_simple_condition(ctx, executor)) {
    executor->exec = grn_expr_executor_exec_simple_condition;
    executor->fin = grn_expr_executor_fin_simple_condition;
  } else {
    grn_expr_executor_init_general(ctx, executor);
    executor->exec = grn_expr_executor_exec_general;
    executor->fin = grn_expr_executor_fin_general;
  }

  GRN_API_RETURN(ctx->rc);
}

grn_rc
grn_expr_executor_fin(grn_ctx *ctx,
                      grn_expr_executor *executor)
{
  GRN_API_ENTER;

  if (!executor) {
    GRN_API_RETURN(GRN_SUCCESS);
  }

  executor->fin(ctx, executor);

  GRN_API_RETURN(GRN_SUCCESS);
}

grn_expr_executor *
grn_expr_executor_open(grn_ctx *ctx, grn_obj *expr)
{
  grn_expr_executor *executor;

  GRN_API_ENTER;

  executor = GRN_CALLOC(sizeof(grn_expr_executor));
  if (!executor) {
    char errbuf[GRN_CTX_MSGSIZE];
    grn_strcpy(errbuf, GRN_CTX_MSGSIZE, ctx->errbuf);
    ERR(ctx->rc,
        "[expr-executor][open] failed to allocate: %s",
        errbuf);
    GRN_API_RETURN(NULL);
  }

  grn_expr_executor_init(ctx, executor, expr);

  if (ctx->rc != GRN_SUCCESS) {
    GRN_FREE(executor);
    executor = NULL;
  }

  GRN_API_RETURN(executor);
}

grn_rc
grn_expr_executor_close(grn_ctx *ctx, grn_expr_executor *executor)
{
  GRN_API_ENTER;

  if (!executor) {
    GRN_API_RETURN(GRN_SUCCESS);
  }

  grn_expr_executor_fin(ctx, executor);
  GRN_FREE(executor);

  GRN_API_RETURN(GRN_SUCCESS);
}

grn_obj *
grn_expr_executor_exec(grn_ctx *ctx, grn_expr_executor *executor, grn_id id)
{
  grn_obj *value;

  GRN_API_ENTER;

  if (!executor) {
    GRN_API_RETURN(NULL);
  }

  value = executor->exec(ctx, executor, id);

  GRN_API_RETURN(value);
}
