Пример #1
0
grn_rc
grn_ts_obj_cursor_open(grn_ctx *ctx, grn_obj *obj, grn_ts_cursor **cursor)
{
  grn_ts_obj_cursor *new_cursor;
  if (!ctx) {
    return GRN_INVALID_ARGUMENT;
  }
  if (!obj || !cursor) {
    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
  }
  switch (obj->header.type) {
    case GRN_CURSOR_TABLE_HASH_KEY:
    case GRN_CURSOR_TABLE_PAT_KEY:
    case GRN_CURSOR_TABLE_DAT_KEY:
    case GRN_CURSOR_TABLE_NO_KEY: {
      break;
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
    }
  }
  new_cursor = GRN_MALLOCN(grn_ts_obj_cursor, 1);
  if (!new_cursor) {
    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
                      "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",
                      sizeof(grn_ts_obj_cursor));
  }
  new_cursor->type = GRN_TS_OBJ_CURSOR;
  new_cursor->obj = obj;
  *cursor = (grn_ts_cursor *)new_cursor;
  return GRN_SUCCESS;
}
Пример #2
0
grn_rc
grn_ts_expr_parse(grn_ctx *ctx, grn_obj *table, grn_ts_str str,
                  grn_ts_expr **expr)
{
    grn_rc rc;
    grn_ts_expr *new_expr;
    grn_ts_expr_parser *parser;
    if (!ctx) {
        return GRN_INVALID_ARGUMENT;
    }
    if (!table || !grn_ts_obj_is_table(ctx, table) ||
            (!str.ptr && str.size) || !expr) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
    }
    rc = grn_ts_expr_parser_open(ctx, table, &parser);
    if (rc != GRN_SUCCESS) {
        return rc;
    }
    rc = grn_ts_expr_parser_parse(ctx, parser, str, &new_expr);
    grn_ts_expr_parser_close(ctx, parser);
    if (rc != GRN_SUCCESS) {
        return rc;
    }
    *expr = new_expr;
    return GRN_SUCCESS;
}
Пример #3
0
/*
 * grn_ts_writer_output_body() evaluates expressions and outputs the results.
 */
static grn_rc
grn_ts_writer_output_body(grn_ctx *ctx, grn_ts_writer *writer,
                          const grn_ts_record *in, size_t n_in)
{
  size_t i, j, count = 0;
  writer->bufs = GRN_MALLOCN(grn_ts_buf, writer->n_exprs);
  if (!writer->bufs) {
    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
                      "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x %" GRN_FMT_SIZE,
                      sizeof(grn_ts_buf), writer->n_exprs);
  }
  for (i = 0; i < writer->n_exprs; i++) {
    grn_ts_buf_init(ctx, &writer->bufs[i]);
  }
  while (count < n_in) {
    size_t batch_size = GRN_TS_BATCH_SIZE;
    if (batch_size > (n_in - count)) {
      batch_size = n_in - count;
    }
    for (i = 0; i < writer->n_exprs; ++i) {
      grn_rc rc = grn_ts_expr_evaluate_to_buf(ctx, writer->exprs[i], in + count,
                                              batch_size, &writer->bufs[i]);
      if (rc != GRN_SUCCESS) {
        return rc;
      }
    }
    for (i = 0; i < batch_size; ++i) {
      GRN_OUTPUT_ARRAY_OPEN("HIT", writer->n_exprs);
      for (j = 0; j < writer->n_exprs; ++j) {
        if (j) {
          GRN_TEXT_PUTC(ctx, ctx->impl->output.buf, ',');
        }
        switch (writer->exprs[j]->data_kind) {
          GRN_TS_WRITER_OUTPUT_BODY_CASE(BOOL, bool);
          GRN_TS_WRITER_OUTPUT_BODY_CASE(INT, int);
          GRN_TS_WRITER_OUTPUT_BODY_CASE(FLOAT, float);
          GRN_TS_WRITER_OUTPUT_BODY_CASE(TIME, time);
          GRN_TS_WRITER_OUTPUT_BODY_CASE(TEXT, text);
          GRN_TS_WRITER_OUTPUT_BODY_CASE(GEO, geo);
          GRN_TS_WRITER_OUTPUT_BODY_VECTOR_CASE(BOOL, bool);
          GRN_TS_WRITER_OUTPUT_BODY_VECTOR_CASE(INT, int);
          GRN_TS_WRITER_OUTPUT_BODY_VECTOR_CASE(FLOAT, float);
          GRN_TS_WRITER_OUTPUT_BODY_VECTOR_CASE(TIME, time);
          GRN_TS_WRITER_OUTPUT_BODY_VECTOR_CASE(TEXT, text);
          GRN_TS_WRITER_OUTPUT_BODY_VECTOR_CASE(GEO, geo);
          default: {
            break;
          }
        }
      }
      GRN_OUTPUT_ARRAY_CLOSE(); /* HITS. */
    }
    count += batch_size;
  }
  return GRN_SUCCESS;
}
Пример #4
0
grn_rc
grn_ts_cursor_close(grn_ctx *ctx, grn_ts_cursor *cursor)
{
  if (!ctx) {
    return GRN_INVALID_ARGUMENT;
  }
  if (!cursor) {
    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
  }
  switch (cursor->type) {
    case GRN_TS_OBJ_CURSOR: {
      return grn_ts_obj_cursor_close(ctx, (grn_ts_obj_cursor *)cursor);
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid cursor type: %d",
                        cursor->type);
    }
  }
}
Пример #5
0
grn_rc
grn_ts_cursor_read(grn_ctx *ctx, grn_ts_cursor *cursor,
                   grn_ts_record *out, size_t max_n_out, size_t *n_out)
{
  if (!ctx) {
    return GRN_INVALID_ARGUMENT;
  }
  if (!cursor || (!out && max_n_out) || !n_out) {
    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
  }
  switch (cursor->type) {
    case GRN_TS_OBJ_CURSOR: {
      return grn_ts_obj_cursor_read(ctx, (grn_ts_obj_cursor *)cursor,
                                    out, max_n_out, n_out);
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid cursor type: %d",
                        cursor->type);
    }
  }
}
Пример #6
0
grn_rc
grn_ts_expr_close(grn_ctx *ctx, grn_ts_expr *expr)
{
    if (!ctx) {
        return GRN_INVALID_ARGUMENT;
    }
    if (!expr) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
    }
    grn_ts_expr_fin(ctx, expr);
    GRN_FREE(expr);
    return GRN_SUCCESS;
}
Пример #7
0
/* grn_ts_writer_build() builds output expresions. */
static grn_rc
grn_ts_writer_build(grn_ctx *ctx, grn_ts_writer *writer, grn_obj *table)
{
  size_t i, n_names = grn_vector_size(ctx, &writer->name_buf);
  if (!n_names) {
    return GRN_SUCCESS;
  }
  writer->names = GRN_MALLOCN(grn_ts_str, n_names);
  if (!writer->names) {
    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
                      "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x %" GRN_FMT_SIZE,
                      sizeof(grn_ts_str), n_names);
  }
  writer->exprs = GRN_MALLOCN(grn_ts_expr *, n_names);
  if (!writer->exprs) {
    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
                      "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x %" GRN_FMT_SIZE,
                      sizeof(grn_ts_expr *), n_names);
  }
  for (i = 0; i < n_names; i++) {
    grn_rc rc;
    grn_ts_expr *new_expr;
    const char *name_ptr;
    size_t name_size = grn_vector_get_element(ctx, &writer->name_buf, i,
                                              &name_ptr, NULL, NULL);
    rc = grn_ts_expr_parser_parse(ctx, writer->parser,
                                  (grn_ts_str){ name_ptr, name_size },
                                  &new_expr);
    if (rc != GRN_SUCCESS) {
      return rc;
    }
    writer->names[i].ptr = name_ptr;
    writer->names[i].size = name_size;
    writer->exprs[i] = new_expr;
    writer->n_exprs++;
  }
  return GRN_SUCCESS;
}
Пример #8
0
grn_rc
grn_ts_expr_adjust(grn_ctx *ctx, grn_ts_expr *expr,
                   grn_ts_record *io, size_t n_io)
{
    if (!ctx) {
        return GRN_INVALID_ARGUMENT;
    }
    if (!expr || (!io && n_io)) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
    }
    if (!n_io) {
        return GRN_SUCCESS;
    }
    return grn_ts_expr_node_adjust(ctx, expr->root, io, n_io);
}
Пример #9
0
grn_rc
grn_ts_expr_evaluate(grn_ctx *ctx, grn_ts_expr *expr,
                     const grn_ts_record *in, size_t n_in, void *out)
{
    if (!ctx) {
        return GRN_INVALID_ARGUMENT;
    }
    if (!expr || (!in && n_in) || (n_in && !out)) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
    }
    if (!n_in) {
        return GRN_SUCCESS;
    }
    return grn_ts_expr_node_evaluate(ctx, expr->root, in, n_in, out);
}
Пример #10
0
grn_rc
grn_ts_expr_filter(grn_ctx *ctx, grn_ts_expr *expr,
                   grn_ts_record *in, size_t n_in,
                   grn_ts_record *out, size_t *n_out)
{
    if (!ctx) {
        return GRN_INVALID_ARGUMENT;
    }
    if (!expr || (!in && n_in) || !out || !n_out) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
    }
    if (!n_in) {
        *n_out = 0;
        return GRN_SUCCESS;
    }
    return grn_ts_expr_node_filter(ctx, expr->root, in, n_in, out, n_out);
}
Пример #11
0
grn_rc
grn_ts_select(grn_ctx *ctx, grn_obj *table,
              const char *filter_ptr, size_t filter_len,
              const char *scorer_ptr, size_t scorer_len,
              const char *sortby_ptr, size_t sortby_len,
              const char *output_columns_ptr, size_t output_columns_len,
              size_t offset, size_t limit)
{
  grn_rc rc;
  grn_ts_str filter = { filter_ptr, filter_len };
  grn_ts_str scorer = { scorer_ptr, scorer_len };
  grn_ts_str sortby = { sortby_ptr, sortby_len };
  grn_ts_str output_columns = { output_columns_ptr, output_columns_len };
  if (!ctx) {
    return GRN_INVALID_ARGUMENT;
  }
  if (!table || !grn_ts_obj_is_table(ctx, table) ||
      (!filter_ptr && filter_len) || (!scorer_ptr && scorer_len) ||
      (!sortby_ptr && sortby_len) ||
      (!output_columns_ptr && output_columns_len)) {
    GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
  }
  if (sortby_len) {
    rc = grn_ts_select_with_sortby(ctx, table, filter, scorer, sortby,
                                   output_columns, offset, limit);
  } else {
    rc = grn_ts_select_without_sortby(ctx, table, filter, scorer,
                                      output_columns, offset, limit);
  }
  if (rc != GRN_SUCCESS) {
    GRN_BULK_REWIND(ctx->impl->output.buf);
    if ((ctx->rc == GRN_SUCCESS) || !ctx->errbuf[0]) {
      ERR(rc, "error message is missing");
    } else if (ctx->errlvl < GRN_LOG_ERROR) {
      ctx->errlvl = GRN_LOG_ERROR;
    }
  }
  return rc;
}
Пример #12
0
/* grn_ts_obj_cursor_read() reads records from a wrapper cursor. */
static grn_rc
grn_ts_obj_cursor_read(grn_ctx *ctx, grn_ts_obj_cursor *cursor,
                       grn_ts_record *recs, size_t max_n_recs, size_t *n_recs)
{
  switch (cursor->obj->header.type) {
    case GRN_CURSOR_TABLE_HASH_KEY: {
      GRN_TS_OBJ_CURSOR_READ(hash)
    }
    case GRN_CURSOR_TABLE_PAT_KEY: {
      GRN_TS_OBJ_CURSOR_READ(pat)
    }
    case GRN_CURSOR_TABLE_DAT_KEY: {
      GRN_TS_OBJ_CURSOR_READ(dat)
    }
    case GRN_CURSOR_TABLE_NO_KEY: {
      GRN_TS_OBJ_CURSOR_READ(array)
    }
    default: {
      GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
    }
  }
  return GRN_SUCCESS;
}
Пример #13
0
/* grn_ts_writer_open() creates a writer. */
static grn_rc
grn_ts_writer_open(grn_ctx *ctx, grn_obj *table, grn_ts_str str,
                   grn_ts_writer **writer)
{
  grn_rc rc;
  grn_ts_writer *new_writer = GRN_MALLOCN(grn_ts_writer, 1);
  if (!new_writer) {
    GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
                      "GRN_MALLOCN failed: %" GRN_FMT_SIZE " x 1",
                      sizeof(grn_ts_writer));
  }
  grn_ts_writer_init(ctx, new_writer);
  rc = grn_ts_writer_parse(ctx, new_writer, table, str);
  if (rc == GRN_SUCCESS) {
    rc = grn_ts_writer_build(ctx, new_writer, table);
  }
  if (rc != GRN_SUCCESS) {
    grn_ts_writer_fin(ctx, new_writer);
    GRN_FREE(new_writer);
    return rc;
  }
  *writer = new_writer;
  return GRN_SUCCESS;
}
Пример #14
0
grn_rc
grn_ts_expr_open(grn_ctx *ctx, grn_obj *table, grn_ts_expr_node *root,
                 grn_ts_expr **expr)
{
    grn_rc rc;
    grn_ts_expr *new_expr;
    grn_ts_expr_type type;
    if (!ctx) {
        return GRN_INVALID_ARGUMENT;
    }
    if (!table || !grn_ts_obj_is_table(ctx, table) || !root || !expr) {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
    }
    switch (root->type) {
    case GRN_TS_EXPR_ID_NODE: {
        type = GRN_TS_EXPR_ID;
        break;
    }
    case GRN_TS_EXPR_SCORE_NODE: {
        type = GRN_TS_EXPR_SCORE;
        break;
    }
    case GRN_TS_EXPR_KEY_NODE:
    case GRN_TS_EXPR_VALUE_NODE: {
        type = GRN_TS_EXPR_VARIABLE;
        break;
    }
    case GRN_TS_EXPR_CONST_NODE: {
        type = GRN_TS_EXPR_CONST;
        break;
    }
    case GRN_TS_EXPR_COLUMN_NODE:
    case GRN_TS_EXPR_OP_NODE:
    case GRN_TS_EXPR_BRIDGE_NODE: {
        type = GRN_TS_EXPR_VARIABLE;
        break;
    }
    default: {
        GRN_TS_ERR_RETURN(GRN_INVALID_ARGUMENT, "invalid argument");
    }
    }
    new_expr = GRN_MALLOCN(grn_ts_expr, 1);
    if (!new_expr) {
        GRN_TS_ERR_RETURN(GRN_NO_MEMORY_AVAILABLE,
                          "GRN_MALLOCN failed: %" GRN_FMT_SIZE,
                          sizeof(grn_ts_expr));
    }
    rc = grn_ts_obj_increment_ref_count(ctx, table);
    if (rc != GRN_SUCCESS) {
        GRN_FREE(new_expr);
        return rc;
    }
    grn_ts_expr_init(ctx, new_expr);
    new_expr->table = table;
    new_expr->type = type;
    new_expr->data_kind = root->data_kind;
    new_expr->data_type = root->data_type;
    new_expr->root = root;
    *expr = new_expr;
    return GRN_SUCCESS;
}
Пример #15
0
/* grn_ts_select_with_sortby() executes a select command with --sortby. */
static grn_rc
grn_ts_select_with_sortby(grn_ctx *ctx, grn_obj *table,
                          grn_ts_str filter, grn_ts_str scorer,
                          grn_ts_str sortby, grn_ts_str output_columns,
                          size_t offset, size_t limit)
{
  grn_rc rc;
  grn_ts_record *recs = NULL;
  size_t n_recs = 0, max_n_recs = 0, n_hits = 0;
  grn_table_cursor *cursor_obj;
  grn_ts_cursor *cursor = NULL;
  grn_ts_expr *filter_expr = NULL;
  grn_ts_expr *scorer_expr = NULL;
  grn_ts_sorter *sorter = NULL;
  cursor_obj = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1,
                                     GRN_CURSOR_ASCENDING | GRN_CURSOR_BY_ID);
  if (!cursor_obj) {
    GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "grn_table_cursor_open failed");
  }
  rc = grn_ts_obj_cursor_open(ctx, cursor_obj, &cursor);
  if (rc != GRN_SUCCESS) {
    grn_obj_close(ctx, cursor_obj);
    return rc;
  }
  rc = grn_ts_expr_parse(ctx, table, filter, &filter_expr);
  if (rc == GRN_SUCCESS) {
    scorer = grn_ts_str_trim_score_assignment(scorer);
    if (scorer.size) {
      rc = grn_ts_expr_parse(ctx, table, scorer, &scorer_expr);
    }
    if (rc == GRN_SUCCESS) {
      rc = grn_ts_sorter_parse(ctx, table, sortby, offset, limit, &sorter);
    }
  }
  if (rc == GRN_SUCCESS) {
    for ( ; ; ) {
      size_t batch_size;
      grn_ts_record *batch;
      /* Extend a buffer for records. */
      if (max_n_recs < (n_recs + GRN_TS_BATCH_SIZE)) {
        size_t n_bytes, new_max_n_recs = max_n_recs * 2;
        grn_ts_record *new_recs;
        if (!new_max_n_recs) {
          new_max_n_recs = GRN_TS_BATCH_SIZE;
        }
        n_bytes = sizeof(grn_ts_record) * new_max_n_recs;
        new_recs = (grn_ts_record *)GRN_REALLOC(recs, n_bytes);
        if (!new_recs) {
          GRN_TS_ERR(GRN_NO_MEMORY_AVAILABLE,
                     "GRN_REALLOC failed: %" GRN_FMT_SIZE,
                     n_bytes);
          rc = ctx->rc;
          break;
        }
        recs = new_recs;
        max_n_recs = new_max_n_recs;
      }
      /* Read records from a cursor. */
      batch = recs + n_recs;
      rc = grn_ts_cursor_read(ctx, cursor, batch, GRN_TS_BATCH_SIZE,
                              &batch_size);
      if (rc != GRN_SUCCESS) {
        break;
      } else if (!batch_size) {
        /* Complete sorting. */
        rc = grn_ts_sorter_complete(ctx, sorter, recs, n_recs, &n_recs);
        break;
      }
      /* Apply a filter and a scorer. */
      rc = grn_ts_expr_filter(ctx, filter_expr, batch, batch_size,
                              batch, &batch_size);
      if (rc != GRN_SUCCESS) {
        break;
      }
      if (scorer_expr) {
        rc = grn_ts_expr_adjust(ctx, scorer_expr, batch, batch_size);
        if (rc != GRN_SUCCESS) {
          break;
        }
      }
      n_hits += batch_size;
      n_recs += batch_size;
      /* Progress sorting. */
      rc = grn_ts_sorter_progress(ctx, sorter, recs, n_recs, &n_recs);
      if (rc != GRN_SUCCESS) {
        break;
      }
    }
  }
  if (rc == GRN_SUCCESS) {
    rc = grn_ts_select_output(ctx, table, output_columns,
                              recs, n_recs, n_hits);
  }
  if (cursor) {
    grn_ts_cursor_close(ctx, cursor);
  }
  if (recs) {
    GRN_FREE(recs);
  }
  if (sorter) {
    grn_ts_sorter_close(ctx, sorter);
  }
  if (scorer_expr) {
    grn_ts_expr_close(ctx, scorer_expr);
  }
  if (filter_expr) {
    grn_ts_expr_close(ctx, filter_expr);
  }
  return rc;
}
Пример #16
0
/* grn_ts_writer_output_header() outputs names and data types. */
static grn_rc
grn_ts_writer_output_header(grn_ctx *ctx, grn_ts_writer *writer)
{
  grn_rc rc;
  GRN_OUTPUT_ARRAY_OPEN("COLUMNS", writer->n_exprs);
  for (size_t i = 0; i < writer->n_exprs; ++i) {
    GRN_OUTPUT_ARRAY_OPEN("COLUMN", 2);
    rc = grn_text_esc(ctx, ctx->impl->output.buf,
                      writer->names[i].ptr, writer->names[i].size);
    if (rc != GRN_SUCCESS) {
      return rc;
    }
    GRN_TEXT_PUT(ctx, ctx->impl->output.buf, ",\"", 2);
    switch (writer->exprs[i]->data_type) {
      case GRN_DB_VOID: {
        if (writer->exprs[i]->data_kind == GRN_TS_GEO) {
          GRN_TEXT_PUTS(ctx, ctx->impl->output.buf, "GeoPoint");
        } else {
          GRN_TEXT_PUTS(ctx, ctx->impl->output.buf, "Void");
        }
        break;
      }
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(BOOL, "Bool")
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(INT8, "Int8")
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(INT16, "Int16")
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(INT32, "Int32")
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(INT64, "Int64")
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(UINT8, "UInt8")
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(UINT16, "UInt16")
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(UINT32, "UInt32")
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(UINT64, "UInt64")
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(FLOAT, "Float")
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(TIME, "Time")
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(SHORT_TEXT, "ShortText")
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(TEXT, "Text")
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(LONG_TEXT, "LongText")
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(TOKYO_GEO_POINT, "TokyoGeoPoint")
      GRN_TS_WRITER_OUTPUT_HEADER_CASE(WGS84_GEO_POINT, "WGS84GeoPoint")
      default: {
        char name_buf[GRN_TABLE_MAX_KEY_SIZE];
        size_t name_size;
        grn_obj *obj = grn_ctx_at(ctx, writer->exprs[i]->data_type);
        if (!obj) {
          GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "grn_ctx_at failed: %d",
                            writer->exprs[i]->data_type);
        }
        if (!grn_ts_obj_is_table(ctx, obj)) {
          grn_obj_unlink(ctx, obj);
          GRN_TS_ERR_RETURN(GRN_UNKNOWN_ERROR, "not table: %d",
                            writer->exprs[i]->data_type);
        }
        name_size = grn_obj_name(ctx, obj, name_buf, sizeof(name_buf));
        GRN_TEXT_PUT(ctx, ctx->impl->output.buf, name_buf, name_size);
        grn_obj_unlink(ctx, obj);
        break;
      }
    }
    GRN_TEXT_PUTC(ctx, ctx->impl->output.buf, '"');
    GRN_OUTPUT_ARRAY_CLOSE();
  }
  GRN_OUTPUT_ARRAY_CLOSE(); /* COLUMNS. */
  return GRN_SUCCESS;
}