/* -*- c-basic-offset: 2 -*- */ /* Copyright(C) 2009-2015 Brazil This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ #include "grn.h" #include #include "grn_str.h" #include "grn_db.h" #include "grn_expr_code.h" #include "grn_util.h" #include "grn_output.h" #define LEVELS (&ctx->impl->output.levels) #define DEPTH (GRN_BULK_VSIZE(LEVELS)>>2) #define CURR_LEVEL (DEPTH ? (GRN_UINT32_VALUE_AT(LEVELS, (DEPTH - 1))) : 0) #define INCR_DEPTH(i) GRN_UINT32_PUT(ctx, LEVELS, i) #define DECR_DEPTH (DEPTH ? grn_bulk_truncate(ctx, LEVELS, GRN_BULK_VSIZE(LEVELS) - sizeof(uint32_t)) : 0) #define INCR_LENGTH (DEPTH ? (GRN_UINT32_VALUE_AT(LEVELS, (DEPTH - 1)) += 2) : 0) static void indent(grn_ctx *ctx, grn_obj *outbuf, size_t level) { size_t i; for (i = 0; i < level; i++) { GRN_TEXT_PUTS(ctx, outbuf, " "); } } static void json_array_open(grn_ctx *ctx, grn_obj *outbuf, size_t *indent_level) { GRN_TEXT_PUTC(ctx, outbuf, '['); if (ctx->impl->output.is_pretty) { GRN_TEXT_PUTC(ctx, outbuf, '\n'); (*indent_level)++; indent(ctx, outbuf, *indent_level); } } static void json_array_close(grn_ctx *ctx, grn_obj *outbuf, size_t *indent_level) { if (ctx->impl->output.is_pretty) { GRN_TEXT_PUTC(ctx, outbuf, '\n'); (*indent_level)--; indent(ctx, outbuf, *indent_level); } GRN_TEXT_PUTC(ctx, outbuf, ']'); } static void json_element_end(grn_ctx *ctx, grn_obj *outbuf, size_t indent_level) { GRN_TEXT_PUTC(ctx, outbuf, ','); if (ctx->impl->output.is_pretty) { GRN_TEXT_PUTC(ctx, outbuf, '\n'); indent(ctx, outbuf, indent_level); } } static void json_map_open(grn_ctx *ctx, grn_obj *outbuf, size_t *indent_level) { GRN_TEXT_PUTC(ctx, outbuf, '{'); if (ctx->impl->output.is_pretty) { GRN_TEXT_PUTC(ctx, outbuf, '\n'); (*indent_level)++; indent(ctx, outbuf, *indent_level); } } static void json_map_close(grn_ctx *ctx, grn_obj *outbuf, size_t *indent_level) { if (ctx->impl->output.is_pretty) { GRN_TEXT_PUTC(ctx, outbuf, '\n'); (*indent_level)--; indent(ctx, outbuf, *indent_level); } GRN_TEXT_PUTC(ctx, outbuf, '}'); } static void json_key_end(grn_ctx *ctx, grn_obj *outbuf) { GRN_TEXT_PUTC(ctx, outbuf, ':'); if (ctx->impl->output.is_pretty) { GRN_TEXT_PUTC(ctx, outbuf, ' '); } } static void json_key(grn_ctx *ctx, grn_obj *outbuf, const char *key) { grn_text_esc(ctx, outbuf, key, strlen(key)); json_key_end(ctx, outbuf); } static void json_value_end(grn_ctx *ctx, grn_obj *outbuf, size_t indent_level) { GRN_TEXT_PUTC(ctx, outbuf, ','); if (ctx->impl->output.is_pretty) { GRN_TEXT_PUTC(ctx, outbuf, '\n'); indent(ctx, outbuf, indent_level); } } static void put_delimiter(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type) { uint32_t level = CURR_LEVEL; switch (output_type) { case GRN_CONTENT_JSON: if (level < 2) { if (DEPTH > 0 && ctx->impl->output.is_pretty) { GRN_TEXT_PUTC(ctx, outbuf, '\n'); indent(ctx, outbuf, DEPTH + 1); } return; } if ((level & 3) == 3) { GRN_TEXT_PUTC(ctx, outbuf, ':'); if (ctx->impl->output.is_pretty) { GRN_TEXT_PUTC(ctx, outbuf, ' '); } } else { json_element_end(ctx, outbuf, DEPTH + 1); } // if (DEPTH == 1 && ((level & 3) != 3)) { GRN_TEXT_PUTC(ctx, outbuf, '\n'); } break; case GRN_CONTENT_XML: if (!DEPTH) { return; } GRN_TEXT_PUTC(ctx, outbuf, '\n'); break; case GRN_CONTENT_TSV: if (level < 2) { return; } if (DEPTH <= 2) { GRN_TEXT_PUTC(ctx, outbuf, ((level & 3) == 3) ? '\t' : '\n'); } else { GRN_TEXT_PUTC(ctx, outbuf, '\t'); } case GRN_CONTENT_MSGPACK : // do nothing break; case GRN_CONTENT_GROONGA_COMMAND_LIST : break; case GRN_CONTENT_NONE: break; } } void grn_output_array_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, const char *name, int nelements) { put_delimiter(ctx, outbuf, output_type); switch (output_type) { case GRN_CONTENT_JSON: GRN_TEXT_PUTC(ctx, outbuf, '['); break; case GRN_CONTENT_XML: GRN_TEXT_PUTC(ctx, outbuf, '<'); GRN_TEXT_PUTS(ctx, outbuf, name); GRN_TEXT_PUTC(ctx, outbuf, '>'); grn_vector_add_element(ctx, &ctx->impl->output.names, name, strlen(name), 0, GRN_DB_SHORT_TEXT); break; case GRN_CONTENT_TSV: if (DEPTH > 2) { GRN_TEXT_PUTS(ctx, outbuf, "[\t"); } break; case GRN_CONTENT_MSGPACK : #ifdef GRN_WITH_MESSAGE_PACK if (nelements < 0) { GRN_LOG(ctx, GRN_LOG_DEBUG, "grn_output_array_open nelements (%d) for <%s>", nelements, name); } msgpack_pack_array(&ctx->impl->output.msgpacker, nelements); #endif break; case GRN_CONTENT_GROONGA_COMMAND_LIST : break; case GRN_CONTENT_NONE: break; } INCR_DEPTH(0); } void grn_output_array_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type) { switch (output_type) { case GRN_CONTENT_JSON: if (ctx->impl->output.is_pretty) { GRN_TEXT_PUTC(ctx, outbuf, '\n'); indent(ctx, outbuf, DEPTH); } GRN_TEXT_PUTC(ctx, outbuf, ']'); break; case GRN_CONTENT_TSV: if (DEPTH > 3) { if (CURR_LEVEL >= 2) { GRN_TEXT_PUTC(ctx, outbuf, '\t'); } GRN_TEXT_PUTC(ctx, outbuf, ']'); } break; case GRN_CONTENT_XML: { const char *name; unsigned int name_len; name_len = grn_vector_pop_element(ctx, &ctx->impl->output.names, &name, NULL, NULL); GRN_TEXT_PUTS(ctx, outbuf, "'); } break; case GRN_CONTENT_MSGPACK : // do nothing break; case GRN_CONTENT_GROONGA_COMMAND_LIST : break; case GRN_CONTENT_NONE: break; } DECR_DEPTH; INCR_LENGTH; } void grn_output_map_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, const char *name, int nelements) { put_delimiter(ctx, outbuf, output_type); switch (output_type) { case GRN_CONTENT_JSON: GRN_TEXT_PUTS(ctx, outbuf, "{"); break; case GRN_CONTENT_XML: GRN_TEXT_PUTC(ctx, outbuf, '<'); GRN_TEXT_PUTS(ctx, outbuf, name); GRN_TEXT_PUTC(ctx, outbuf, '>'); grn_vector_add_element(ctx, &ctx->impl->output.names, name, strlen(name), 0, GRN_DB_SHORT_TEXT); break; case GRN_CONTENT_TSV: if (DEPTH > 2) { GRN_TEXT_PUTS(ctx, outbuf, "{\t"); } break; case GRN_CONTENT_MSGPACK : #ifdef GRN_WITH_MESSAGE_PACK if (nelements < 0) { GRN_LOG(ctx, GRN_LOG_DEBUG, "grn_output_map_open nelements (%d) for <%s>", nelements, name); } msgpack_pack_map(&ctx->impl->output.msgpacker, nelements); #endif break; case GRN_CONTENT_GROONGA_COMMAND_LIST : break; case GRN_CONTENT_NONE: break; } INCR_DEPTH(1); } void grn_output_map_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type) { switch (output_type) { case GRN_CONTENT_JSON: if (ctx->impl->output.is_pretty) { GRN_TEXT_PUTC(ctx, outbuf, '\n'); indent(ctx, outbuf, DEPTH); } GRN_TEXT_PUTS(ctx, outbuf, "}"); break; case GRN_CONTENT_TSV: if (DEPTH > 3) { if (CURR_LEVEL >= 2) { GRN_TEXT_PUTC(ctx, outbuf, '\t'); } GRN_TEXT_PUTC(ctx, outbuf, '}'); } break; case GRN_CONTENT_XML: { const char *name; unsigned int name_len; name_len = grn_vector_pop_element(ctx, &ctx->impl->output.names, &name, NULL, NULL); GRN_TEXT_PUTS(ctx, outbuf, "'); } break; case GRN_CONTENT_MSGPACK : // do nothing break; case GRN_CONTENT_GROONGA_COMMAND_LIST : break; case GRN_CONTENT_NONE: break; } DECR_DEPTH; INCR_LENGTH; } void grn_output_int32(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int value) { put_delimiter(ctx, outbuf, output_type); switch (output_type) { case GRN_CONTENT_JSON: grn_text_itoa(ctx, outbuf, value); break; case GRN_CONTENT_TSV: grn_text_itoa(ctx, outbuf, value); break; case GRN_CONTENT_XML: GRN_TEXT_PUTS(ctx, outbuf, ""); grn_text_itoa(ctx, outbuf, value); GRN_TEXT_PUTS(ctx, outbuf, ""); break; case GRN_CONTENT_MSGPACK : #ifdef GRN_WITH_MESSAGE_PACK msgpack_pack_int32(&ctx->impl->output.msgpacker, value); #endif break; case GRN_CONTENT_GROONGA_COMMAND_LIST : grn_text_itoa(ctx, outbuf, value); break; case GRN_CONTENT_NONE: break; } INCR_LENGTH; } void grn_output_int64(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int64_t value) { put_delimiter(ctx, outbuf, output_type); switch (output_type) { case GRN_CONTENT_JSON: grn_text_lltoa(ctx, outbuf, value); break; case GRN_CONTENT_TSV: grn_text_lltoa(ctx, outbuf, value); break; case GRN_CONTENT_XML: GRN_TEXT_PUTS(ctx, outbuf, ""); grn_text_lltoa(ctx, outbuf, value); GRN_TEXT_PUTS(ctx, outbuf, ""); break; case GRN_CONTENT_MSGPACK : #ifdef GRN_WITH_MESSAGE_PACK msgpack_pack_int64(&ctx->impl->output.msgpacker, value); #endif break; case GRN_CONTENT_GROONGA_COMMAND_LIST : grn_text_lltoa(ctx, outbuf, value); break; case GRN_CONTENT_NONE: break; } INCR_LENGTH; } void grn_output_uint64(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, uint64_t value) { put_delimiter(ctx, outbuf, output_type); switch (output_type) { case GRN_CONTENT_JSON: grn_text_ulltoa(ctx, outbuf, value); break; case GRN_CONTENT_TSV: grn_text_ulltoa(ctx, outbuf, value); break; case GRN_CONTENT_XML: GRN_TEXT_PUTS(ctx, outbuf, ""); grn_text_ulltoa(ctx, outbuf, value); GRN_TEXT_PUTS(ctx, outbuf, ""); break; case GRN_CONTENT_MSGPACK : #ifdef GRN_WITH_MESSAGE_PACK msgpack_pack_uint64(&ctx->impl->output.msgpacker, value); #endif break; case GRN_CONTENT_GROONGA_COMMAND_LIST : grn_text_ulltoa(ctx, outbuf, value); break; case GRN_CONTENT_NONE: break; } INCR_LENGTH; } void grn_output_float(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, double value) { put_delimiter(ctx, outbuf, output_type); switch (output_type) { case GRN_CONTENT_JSON: grn_text_ftoa(ctx, outbuf, value); break; case GRN_CONTENT_TSV: grn_text_ftoa(ctx, outbuf, value); break; case GRN_CONTENT_XML: GRN_TEXT_PUTS(ctx, outbuf, ""); grn_text_ftoa(ctx, outbuf, value); GRN_TEXT_PUTS(ctx, outbuf, ""); break; case GRN_CONTENT_MSGPACK : #ifdef GRN_WITH_MESSAGE_PACK msgpack_pack_double(&ctx->impl->output.msgpacker, value); #endif break; case GRN_CONTENT_GROONGA_COMMAND_LIST : grn_text_ftoa(ctx, outbuf, value); break; case GRN_CONTENT_NONE: break; } INCR_LENGTH; } void grn_output_str(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, const char *value, size_t value_len) { put_delimiter(ctx, outbuf, output_type); switch (output_type) { case GRN_CONTENT_JSON: grn_text_esc(ctx, outbuf, value, value_len); break; case GRN_CONTENT_TSV: grn_text_esc(ctx, outbuf, value, value_len); break; case GRN_CONTENT_XML: GRN_TEXT_PUTS(ctx, outbuf, ""); grn_text_escape_xml(ctx, outbuf, value, value_len); GRN_TEXT_PUTS(ctx, outbuf, ""); break; case GRN_CONTENT_MSGPACK : #ifdef GRN_WITH_MESSAGE_PACK msgpack_pack_str(&ctx->impl->output.msgpacker, value_len); msgpack_pack_str_body(&ctx->impl->output.msgpacker, value, value_len); #endif break; case GRN_CONTENT_GROONGA_COMMAND_LIST : GRN_TEXT_PUT(ctx, outbuf, value, value_len); break; case GRN_CONTENT_NONE: break; } INCR_LENGTH; } void grn_output_cstr(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, const char *value) { grn_output_str(ctx, outbuf, output_type, value, strlen(value)); } void grn_output_bool(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_bool value) { put_delimiter(ctx, outbuf, output_type); switch (output_type) { case GRN_CONTENT_JSON: GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false"); break; case GRN_CONTENT_TSV: GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false"); break; case GRN_CONTENT_XML: GRN_TEXT_PUTS(ctx, outbuf, ""); GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false"); GRN_TEXT_PUTS(ctx, outbuf, ""); break; case GRN_CONTENT_MSGPACK : #ifdef GRN_WITH_MESSAGE_PACK if (value) { msgpack_pack_true(&ctx->impl->output.msgpacker); } else { msgpack_pack_false(&ctx->impl->output.msgpacker); } #endif break; case GRN_CONTENT_GROONGA_COMMAND_LIST : GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false"); break; case GRN_CONTENT_NONE: break; } INCR_LENGTH; } void grn_output_null(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type) { put_delimiter(ctx, outbuf, output_type); switch (output_type) { case GRN_CONTENT_JSON: GRN_TEXT_PUTS(ctx, outbuf, "null"); break; case GRN_CONTENT_TSV: break; case GRN_CONTENT_XML: GRN_TEXT_PUTS(ctx, outbuf, ""); break; case GRN_CONTENT_MSGPACK : #ifdef GRN_WITH_MESSAGE_PACK msgpack_pack_nil(&ctx->impl->output.msgpacker); #endif break; case GRN_CONTENT_GROONGA_COMMAND_LIST : break; case GRN_CONTENT_NONE: break; } INCR_LENGTH; } static inline void grn_output_bulk_void(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, const char *value, size_t value_len) { if (value_len == sizeof(grn_id) && *(grn_id *)value == GRN_ID_NIL) { grn_output_null(ctx, outbuf, output_type); } else { grn_output_str(ctx, outbuf, output_type, value, value_len); } } void grn_output_time(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int64_t value) { double dv = value; dv /= 1000000.0; put_delimiter(ctx, outbuf, output_type); switch (output_type) { case GRN_CONTENT_JSON: grn_text_ftoa(ctx, outbuf, dv); break; case GRN_CONTENT_TSV: grn_text_ftoa(ctx, outbuf, dv); break; case GRN_CONTENT_XML: GRN_TEXT_PUTS(ctx, outbuf, ""); grn_text_ftoa(ctx, outbuf, dv); GRN_TEXT_PUTS(ctx, outbuf, ""); break; case GRN_CONTENT_MSGPACK : #ifdef GRN_WITH_MESSAGE_PACK msgpack_pack_double(&ctx->impl->output.msgpacker, dv); #endif break; case GRN_CONTENT_GROONGA_COMMAND_LIST : grn_text_ftoa(ctx, outbuf, dv); break; case GRN_CONTENT_NONE: break; } INCR_LENGTH; } void grn_output_geo_point(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_geo_point *value) { put_delimiter(ctx, outbuf, output_type); switch (output_type) { case GRN_CONTENT_JSON: if (value) { GRN_TEXT_PUTC(ctx, outbuf, '"'); grn_text_itoa(ctx, outbuf, value->latitude); GRN_TEXT_PUTC(ctx, outbuf, 'x'); grn_text_itoa(ctx, outbuf, value->longitude); GRN_TEXT_PUTC(ctx, outbuf, '"'); } else { GRN_TEXT_PUTS(ctx, outbuf, "null"); } break; case GRN_CONTENT_TSV: if (value) { GRN_TEXT_PUTC(ctx, outbuf, '"'); grn_text_itoa(ctx, outbuf, value->latitude); GRN_TEXT_PUTC(ctx, outbuf, 'x'); grn_text_itoa(ctx, outbuf, value->longitude); GRN_TEXT_PUTC(ctx, outbuf, '"'); } else { GRN_TEXT_PUTS(ctx, outbuf, "\"\""); } break; case GRN_CONTENT_XML: GRN_TEXT_PUTS(ctx, outbuf, ""); if (value) { grn_text_itoa(ctx, outbuf, value->latitude); GRN_TEXT_PUTC(ctx, outbuf, 'x'); grn_text_itoa(ctx, outbuf, value->longitude); } GRN_TEXT_PUTS(ctx, outbuf, ""); break; case GRN_CONTENT_MSGPACK : #ifdef GRN_WITH_MESSAGE_PACK if (value) { grn_obj buf; GRN_TEXT_INIT(&buf, 0); grn_text_itoa(ctx, &buf, value->latitude); GRN_TEXT_PUTC(ctx, &buf, 'x'); grn_text_itoa(ctx, &buf, value->longitude); msgpack_pack_str(&ctx->impl->output.msgpacker, GRN_TEXT_LEN(&buf)); msgpack_pack_str_body(&ctx->impl->output.msgpacker, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf)); grn_obj_close(ctx, &buf); } else { msgpack_pack_nil(&ctx->impl->output.msgpacker); } #endif break; case GRN_CONTENT_GROONGA_COMMAND_LIST : if (value) { GRN_TEXT_PUTC(ctx, outbuf, '"'); grn_text_itoa(ctx, outbuf, value->latitude); GRN_TEXT_PUTC(ctx, outbuf, 'x'); grn_text_itoa(ctx, outbuf, value->longitude); GRN_TEXT_PUTC(ctx, outbuf, '"'); } else { GRN_TEXT_PUTS(ctx, outbuf, "\"\""); } break; case GRN_CONTENT_NONE: break; } INCR_LENGTH; } static void grn_text_atoj(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *obj, grn_id id) { uint32_t vs; grn_obj buf; if (obj->header.type == GRN_ACCESSOR) { grn_accessor *a = (grn_accessor *)obj; GRN_TEXT_INIT(&buf, 0); for (;;) { buf.header.domain = grn_obj_get_range(ctx, obj); GRN_BULK_REWIND(&buf); switch (a->action) { case GRN_ACCESSOR_GET_ID : GRN_UINT32_PUT(ctx, &buf, id); buf.header.domain = GRN_DB_UINT32; break; case GRN_ACCESSOR_GET_KEY : grn_table_get_key2(ctx, a->obj, id, &buf); buf.header.domain = DB_OBJ(a->obj)->header.domain; break; case GRN_ACCESSOR_GET_VALUE : grn_obj_get_value(ctx, a->obj, id, &buf); buf.header.domain = DB_OBJ(a->obj)->range; break; case GRN_ACCESSOR_GET_SCORE : { grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs); if (grn_ctx_get_command_version(ctx) == GRN_COMMAND_VERSION_1) { int32_t int32_score = ri->score; GRN_INT32_PUT(ctx, &buf, int32_score); buf.header.domain = GRN_DB_INT32; } else { double float_score = ri->score; GRN_FLOAT_PUT(ctx, &buf, float_score); buf.header.domain = GRN_DB_FLOAT; } } break; case GRN_ACCESSOR_GET_NSUBRECS : { grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs); GRN_INT32_PUT(ctx, &buf, ri->n_subrecs); } buf.header.domain = GRN_DB_INT32; break; case GRN_ACCESSOR_GET_MAX : { grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs); int64_t max; max = grn_rset_recinfo_get_max(ctx, ri, a->obj); GRN_INT64_PUT(ctx, &buf, max); } buf.header.domain = GRN_DB_INT64; break; case GRN_ACCESSOR_GET_MIN : { grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs); int64_t min; min = grn_rset_recinfo_get_min(ctx, ri, a->obj); GRN_INT64_PUT(ctx, &buf, min); } buf.header.domain = GRN_DB_INT64; break; case GRN_ACCESSOR_GET_SUM : { grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs); int64_t sum; sum = grn_rset_recinfo_get_sum(ctx, ri, a->obj); GRN_INT64_PUT(ctx, &buf, sum); } buf.header.domain = GRN_DB_INT64; break; case GRN_ACCESSOR_GET_AVG : { grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs); double avg; avg = grn_rset_recinfo_get_avg(ctx, ri, a->obj); GRN_FLOAT_PUT(ctx, &buf, avg); } buf.header.domain = GRN_DB_FLOAT; break; case GRN_ACCESSOR_GET_COLUMN_VALUE : if ((a->obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) == GRN_OBJ_COLUMN_VECTOR) { if (a->next) { grn_id *idp; grn_obj_get_value(ctx, a->obj, id, &buf); idp = (grn_id *)GRN_BULK_HEAD(&buf); vs = GRN_BULK_VSIZE(&buf) / sizeof(grn_id); grn_output_array_open(ctx, outbuf, output_type, "VECTOR", vs); for (; vs--; idp++) { grn_text_atoj(ctx, outbuf, output_type, (grn_obj *)a->next, *idp); } grn_output_array_close(ctx, outbuf, output_type); } else { grn_text_atoj(ctx, outbuf, output_type, a->obj, id); } goto exit; } else { grn_obj_get_value(ctx, a->obj, id, &buf); } break; case GRN_ACCESSOR_GET_DB_OBJ : /* todo */ break; case GRN_ACCESSOR_LOOKUP : /* todo */ break; case GRN_ACCESSOR_FUNCALL : /* todo */ break; } if (a->next) { a = a->next; if (GRN_BULK_VSIZE(&buf) >= sizeof(grn_id)) { id = *((grn_id *)GRN_BULK_HEAD(&buf)); } else { id = GRN_ID_NIL; } } else { break; } } grn_output_obj(ctx, outbuf, output_type, &buf, NULL); } else { grn_obj_format *format_argument = NULL; grn_obj_format format; GRN_OBJ_FORMAT_INIT(&format, 0, 0, 0, 0); switch (obj->header.type) { case GRN_COLUMN_FIX_SIZE : GRN_VALUE_FIX_SIZE_INIT(&buf, 0, DB_OBJ(obj)->range); break; case GRN_COLUMN_VAR_SIZE : if ((obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) == GRN_OBJ_COLUMN_VECTOR) { grn_obj *range = grn_ctx_at(ctx, DB_OBJ(obj)->range); if (GRN_OBJ_TABLEP(range) || (range->header.flags & GRN_OBJ_KEY_VAR_SIZE) == 0) { GRN_VALUE_FIX_SIZE_INIT(&buf, GRN_OBJ_VECTOR, DB_OBJ(obj)->range); } else { GRN_VALUE_VAR_SIZE_INIT(&buf, GRN_OBJ_VECTOR, DB_OBJ(obj)->range); } if (obj->header.flags & GRN_OBJ_WITH_WEIGHT) { format.flags |= GRN_OBJ_FORMAT_WITH_WEIGHT; format_argument = &format; } } else { GRN_VALUE_VAR_SIZE_INIT(&buf, 0, DB_OBJ(obj)->range); } break; case GRN_COLUMN_INDEX : GRN_UINT32_INIT(&buf, 0); break; default: GRN_TEXT_INIT(&buf, 0); break; } grn_obj_get_value(ctx, obj, id, &buf); grn_output_obj(ctx, outbuf, output_type, &buf, format_argument); } exit : grn_obj_close(ctx, &buf); } static inline void grn_output_void(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *bulk, grn_obj_format *format) { grn_output_null(ctx, outbuf, output_type); } static inline void grn_output_bulk(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *bulk, grn_obj_format *format) { grn_obj buf; GRN_TEXT_INIT(&buf, 0); switch (bulk->header.domain) { case GRN_DB_VOID : grn_output_bulk_void(ctx, outbuf, output_type, GRN_BULK_HEAD(bulk), GRN_BULK_VSIZE(bulk)); break; case GRN_DB_SHORT_TEXT : case GRN_DB_TEXT : case GRN_DB_LONG_TEXT : grn_output_str(ctx, outbuf, output_type, GRN_BULK_HEAD(bulk), GRN_BULK_VSIZE(bulk)); break; case GRN_DB_BOOL : grn_output_bool(ctx, outbuf, output_type, GRN_BULK_VSIZE(bulk) ? GRN_UINT8_VALUE(bulk) : 0); break; case GRN_DB_INT8 : grn_output_int32(ctx, outbuf, output_type, GRN_BULK_VSIZE(bulk) ? GRN_INT8_VALUE(bulk) : 0); break; case GRN_DB_UINT8 : grn_output_int32(ctx, outbuf, output_type, GRN_BULK_VSIZE(bulk) ? GRN_UINT8_VALUE(bulk) : 0); break; case GRN_DB_INT16 : grn_output_int32(ctx, outbuf, output_type, GRN_BULK_VSIZE(bulk) ? GRN_INT16_VALUE(bulk) : 0); break; case GRN_DB_UINT16 : grn_output_int32(ctx, outbuf, output_type, GRN_BULK_VSIZE(bulk) ? GRN_UINT16_VALUE(bulk) : 0); break; case GRN_DB_INT32 : grn_output_int32(ctx, outbuf, output_type, GRN_BULK_VSIZE(bulk) ? GRN_INT32_VALUE(bulk) : 0); break; case GRN_DB_UINT32 : grn_output_int64(ctx, outbuf, output_type, GRN_BULK_VSIZE(bulk) ? GRN_UINT32_VALUE(bulk) : 0); break; case GRN_DB_INT64 : grn_output_int64(ctx, outbuf, output_type, GRN_BULK_VSIZE(bulk) ? GRN_INT64_VALUE(bulk) : 0); break; case GRN_DB_UINT64 : grn_output_uint64(ctx, outbuf, output_type, GRN_BULK_VSIZE(bulk) ? GRN_UINT64_VALUE(bulk) : 0); break; case GRN_DB_FLOAT : grn_output_float(ctx, outbuf, output_type, GRN_BULK_VSIZE(bulk) ? GRN_FLOAT_VALUE(bulk) : 0); break; case GRN_DB_TIME : grn_output_time(ctx, outbuf, output_type, GRN_BULK_VSIZE(bulk) ? GRN_INT64_VALUE(bulk) : 0); break; case GRN_DB_TOKYO_GEO_POINT : case GRN_DB_WGS84_GEO_POINT : grn_output_geo_point(ctx, outbuf, output_type, GRN_BULK_VSIZE(bulk) ? (grn_geo_point *)GRN_BULK_HEAD(bulk) : NULL); break; default : if (format) { int j; int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *); grn_id id = GRN_RECORD_VALUE(bulk); grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns); if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) { grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", ncolumns); for (j = 0; j < ncolumns; j++) { grn_id range_id; grn_output_array_open(ctx, outbuf, output_type, "COLUMN", 2); GRN_BULK_REWIND(&buf); grn_column_name_(ctx, columns[j], &buf); grn_output_obj(ctx, outbuf, output_type, &buf, NULL); /* column range */ range_id = grn_obj_get_range(ctx, columns[j]); if (range_id == GRN_ID_NIL) { GRN_TEXT_PUTS(ctx, outbuf, "null"); } else { int name_len; grn_obj *range_obj; char name_buf[GRN_TABLE_MAX_KEY_SIZE]; range_obj = grn_ctx_at(ctx, range_id); name_len = grn_obj_name(ctx, range_obj, name_buf, GRN_TABLE_MAX_KEY_SIZE); GRN_BULK_REWIND(&buf); GRN_TEXT_PUT(ctx, &buf, name_buf, name_len); grn_output_obj(ctx, outbuf, output_type, &buf, NULL); } grn_output_array_close(ctx, outbuf, output_type); } grn_output_array_close(ctx, outbuf, output_type); } grn_output_array_open(ctx, outbuf, output_type, "HIT", ncolumns); for (j = 0; j < ncolumns; j++) { grn_text_atoj(ctx, outbuf, output_type, columns[j], id); } grn_output_array_close(ctx, outbuf, output_type); } else { grn_obj *table = grn_ctx_at(ctx, bulk->header.domain); grn_id id = GRN_RECORD_VALUE(bulk); if (table && table->header.type != GRN_TABLE_NO_KEY) { grn_obj *accessor = grn_obj_column(ctx, table, GRN_COLUMN_NAME_KEY, GRN_COLUMN_NAME_KEY_LEN); if (accessor) { if (id == GRN_ID_NIL) { grn_obj_reinit_for(ctx, &buf, accessor); } else { grn_obj_get_value(ctx, accessor, id, &buf); } grn_obj_unlink(ctx, accessor); } grn_output_obj(ctx, outbuf, output_type, &buf, format); } else { grn_output_int64(ctx, outbuf, output_type, id); } } break; } GRN_OBJ_FIN(ctx, &buf); } static void grn_output_uvector_result_set(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *uvector, grn_obj_format *format) { unsigned int i_hit, n_hits; unsigned int i_column, n_columns; unsigned int n_elements; grn_obj **columns; grn_obj buf; grn_bool with_column_names = GRN_FALSE; n_hits = grn_vector_size(ctx, uvector); n_columns = GRN_BULK_VSIZE(&format->columns) / sizeof(grn_obj *); columns = (grn_obj **)GRN_BULK_HEAD(&format->columns); GRN_TEXT_INIT(&buf, 0); if (n_hits > 0 && format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) { with_column_names = GRN_TRUE; } n_elements = 1; /* for NHITS */ if (with_column_names) { n_elements += 1; /* for COLUMNS */ } n_elements += n_hits; /* for HITS */ grn_output_array_open(ctx, outbuf, output_type, "RESULTSET", n_elements); grn_output_array_open(ctx, outbuf, output_type, "NHITS", 1); grn_text_itoa(ctx, outbuf, n_hits); grn_output_array_close(ctx, outbuf, output_type); if (with_column_names) { grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", n_columns); for (i_column = 0; i_column < n_columns; i_column++) { grn_id range_id; grn_output_array_open(ctx, outbuf, output_type, "COLUMN", 2); /* name */ GRN_BULK_REWIND(&buf); grn_column_name_(ctx, columns[i_column], &buf); grn_output_obj(ctx, outbuf, output_type, &buf, NULL); /* type */ range_id = grn_obj_get_range(ctx, columns[i_column]); if (range_id == GRN_ID_NIL) { GRN_TEXT_PUTS(ctx, outbuf, "null"); } else { int name_len; grn_obj *range_obj; char name_buf[GRN_TABLE_MAX_KEY_SIZE]; range_obj = grn_ctx_at(ctx, range_id); name_len = grn_obj_name(ctx, range_obj, name_buf, GRN_TABLE_MAX_KEY_SIZE); GRN_BULK_REWIND(&buf); GRN_TEXT_PUT(ctx, &buf, name_buf, name_len); grn_output_obj(ctx, outbuf, output_type, &buf, NULL); } grn_output_array_close(ctx, outbuf, output_type); } grn_output_array_close(ctx, outbuf, output_type); } for (i_hit = 0; i_hit < n_hits++; i_hit++) { grn_id id; id = grn_uvector_get_element(ctx, uvector, i_hit, NULL); grn_output_array_open(ctx, outbuf, output_type, "HITS", n_columns); for (i_column = 0; i_column < n_columns; i_column++) { GRN_BULK_REWIND(&buf); grn_obj_get_value(ctx, columns[i_column], id, &buf); grn_output_obj(ctx, outbuf, output_type, &buf, NULL); } grn_output_array_close(ctx, outbuf, output_type); } grn_output_array_close(ctx, outbuf, output_type); GRN_OBJ_FIN(ctx, &buf); } static inline void grn_output_uvector(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *uvector, grn_obj_format *format) { grn_bool output_result_set = GRN_FALSE; grn_bool with_weight = GRN_FALSE; grn_obj *range; grn_bool range_is_type; if (format) { if (GRN_BULK_VSIZE(&(format->columns)) > 0) { output_result_set = GRN_TRUE; } if (format->flags & GRN_OBJ_FORMAT_WITH_WEIGHT) { with_weight = GRN_TRUE; } } if (output_result_set) { grn_output_uvector_result_set(ctx, outbuf, output_type, uvector, format); return; } range = grn_ctx_at(ctx, uvector->header.domain); range_is_type = (range->header.type == GRN_TYPE); if (range_is_type) { unsigned int i, n; char *raw_elements; unsigned int element_size; grn_obj element; raw_elements = GRN_BULK_HEAD(uvector); element_size = GRN_TYPE_SIZE(DB_OBJ(range)); n = GRN_BULK_VSIZE(uvector) / element_size; grn_output_array_open(ctx, outbuf, output_type, "VECTOR", n); GRN_OBJ_INIT(&element, GRN_BULK, 0, uvector->header.domain); for (i = 0; i < n; i++) { GRN_BULK_REWIND(&element); grn_bulk_write_from(ctx, &element, raw_elements + (element_size * i), 0, element_size); grn_output_obj(ctx, outbuf, output_type, &element, NULL); } GRN_OBJ_FIN(ctx, &element); grn_output_array_close(ctx, outbuf, output_type); } else { unsigned int i, n; grn_obj id_value; grn_obj key_value; GRN_UINT32_INIT(&id_value, 0); GRN_OBJ_INIT(&key_value, GRN_BULK, 0, range->header.domain); n = grn_vector_size(ctx, uvector); if (with_weight) { grn_output_map_open(ctx, outbuf, output_type, "WEIGHT_VECTOR", n); } else { grn_output_array_open(ctx, outbuf, output_type, "VECTOR", n); } for (i = 0; i < n; i++) { grn_id id; unsigned int weight; id = grn_uvector_get_element(ctx, uvector, i, &weight); if (range->header.type == GRN_TABLE_NO_KEY) { GRN_UINT32_SET(ctx, &id_value, id); grn_output_obj(ctx, outbuf, output_type, &id_value, NULL); } else { GRN_BULK_REWIND(&key_value); grn_table_get_key2(ctx, range, id, &key_value); grn_output_obj(ctx, outbuf, output_type, &key_value, NULL); } if (with_weight) { grn_output_uint64(ctx, outbuf, output_type, weight); } } if (with_weight) { grn_output_map_close(ctx, outbuf, output_type); } else { grn_output_array_close(ctx, outbuf, output_type); } GRN_OBJ_FIN(ctx, &id_value); GRN_OBJ_FIN(ctx, &key_value); } grn_obj_unlink(ctx, range); } static inline void grn_output_vector(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *vector, grn_obj_format *format) { grn_bool with_weight = GRN_FALSE; if (vector->header.domain == GRN_DB_VOID) { ERR(GRN_INVALID_ARGUMENT, "invalid obj->header.domain"); return; } if (format) { if (format->flags & GRN_OBJ_FORMAT_WITH_WEIGHT) { with_weight = GRN_TRUE; } } if (with_weight) { unsigned int i, n; grn_obj value; GRN_VOID_INIT(&value); n = grn_vector_size(ctx, vector); grn_output_map_open(ctx, outbuf, output_type, "WEIGHT_VECTOR", n); for (i = 0; i < n; i++) { const char *_value; unsigned int weight, length; grn_id domain; length = grn_vector_get_element(ctx, vector, i, &_value, &weight, &domain); if (domain != GRN_DB_VOID) { grn_obj_reinit(ctx, &value, domain, 0); } else { grn_obj_reinit(ctx, &value, vector->header.domain, 0); } grn_bulk_write(ctx, &value, _value, length); grn_output_obj(ctx, outbuf, output_type, &value, NULL); grn_output_uint64(ctx, outbuf, output_type, weight); } grn_output_map_close(ctx, outbuf, output_type); GRN_OBJ_FIN(ctx, &value); } else { unsigned int i, n; grn_obj value; GRN_VOID_INIT(&value); n = grn_vector_size(ctx, vector); grn_output_array_open(ctx, outbuf, output_type, "VECTOR", n); for (i = 0; i < n; i++) { const char *_value; unsigned int weight, length; grn_id domain; length = grn_vector_get_element(ctx, vector, i, &_value, &weight, &domain); if (domain != GRN_DB_VOID) { grn_obj_reinit(ctx, &value, domain, 0); } else { grn_obj_reinit(ctx, &value, vector->header.domain, 0); } grn_bulk_write(ctx, &value, _value, length); grn_output_obj(ctx, outbuf, output_type, &value, NULL); } grn_output_array_close(ctx, outbuf, output_type); GRN_OBJ_FIN(ctx, &value); } } static inline void grn_output_pvector(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *pvector, grn_obj_format *format) { if (format) { ERR(GRN_FUNCTION_NOT_IMPLEMENTED, "cannot print GRN_PVECTOR using grn_obj_format"); } else { unsigned int i, n; grn_output_array_open(ctx, outbuf, output_type, "VECTOR", -1); n = GRN_BULK_VSIZE(pvector) / sizeof(grn_obj *); for (i = 0; i < n; i++) { grn_obj *value; value = GRN_PTR_VALUE_AT(pvector, i); grn_output_obj(ctx, outbuf, output_type, value, NULL); } grn_output_array_close(ctx, outbuf, output_type); } } static inline void grn_output_result_set_n_hits_v1(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj_format *format) { grn_output_array_open(ctx, outbuf, output_type, "NHITS", 1); if (output_type == GRN_CONTENT_XML) { grn_text_itoa(ctx, outbuf, format->nhits); } else { grn_output_int32(ctx, outbuf, output_type, format->nhits); } grn_output_array_close(ctx, outbuf, output_type); } static inline void grn_output_result_set_n_hits_v3(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj_format *format) { grn_output_cstr(ctx, outbuf, output_type, "n_hits"); grn_output_int32(ctx, outbuf, output_type, format->nhits); } static inline void grn_output_result_set_n_hits(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj_format *format) { if (format->nhits == -1) { return; } if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) { grn_output_result_set_n_hits_v1(ctx, outbuf, output_type, format); } else { grn_output_result_set_n_hits_v3(ctx, outbuf, output_type, format); } } static inline void grn_output_table_column_info(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, const char *name, const char *type) { if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) { grn_output_array_open(ctx, outbuf, output_type, "COLUMN", 2); if (name) { grn_output_cstr(ctx, outbuf, output_type, name); } else { grn_output_null(ctx, outbuf, output_type); } if (type) { grn_output_cstr(ctx, outbuf, output_type, type); } else { grn_output_null(ctx, outbuf, output_type); } grn_output_array_close(ctx, outbuf, output_type); } else { grn_output_map_open(ctx, outbuf, output_type, "column", 2); grn_output_cstr(ctx, outbuf, output_type, "name"); if (name) { grn_output_cstr(ctx, outbuf, output_type, name); } else { grn_output_null(ctx, outbuf, output_type); } grn_output_cstr(ctx, outbuf, output_type, "type"); if (type) { grn_output_cstr(ctx, outbuf, output_type, type); } else { grn_output_null(ctx, outbuf, output_type); } grn_output_map_close(ctx, outbuf, output_type); } } static inline int count_n_elements_in_expression(grn_ctx *ctx, grn_obj *expression) { int n_elements = 0; grn_bool is_first_comma = GRN_TRUE; grn_expr *expr = (grn_expr *)expression; grn_expr_code *code; grn_expr_code *code_end = expr->codes + expr->codes_curr; for (code = expr->codes; code < code_end; code++) { if (code->op == GRN_OP_COMMA) { n_elements++; if (is_first_comma) { n_elements++; is_first_comma = GRN_FALSE; } } } return n_elements; } static grn_bool is_score_accessor(grn_ctx *ctx, grn_obj *obj) { grn_accessor *a; if (obj->header.type != GRN_ACCESSOR) { return GRN_FALSE; } for (a = (grn_accessor *)obj; a->next; a = a->next) { } return a->action == GRN_ACCESSOR_GET_SCORE; } static inline void grn_output_table_column(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *column, grn_obj *buf) { grn_id range_id = GRN_ID_NIL; if (!column) { grn_output_table_column_info(ctx, outbuf, output_type, NULL, NULL); return; } GRN_BULK_REWIND(buf); grn_column_name_(ctx, column, buf); GRN_TEXT_PUTC(ctx, buf, '\0'); if (column->header.type == GRN_COLUMN_INDEX) { range_id = GRN_DB_UINT32; } else if (is_score_accessor(ctx, column)) { if (grn_ctx_get_command_version(ctx) == GRN_COMMAND_VERSION_1) { range_id = GRN_DB_INT32; } else { range_id = GRN_DB_FLOAT; } } if (range_id == GRN_ID_NIL) { range_id = grn_obj_get_range(ctx, column); } if (range_id == GRN_ID_NIL) { grn_output_table_column_info(ctx, outbuf, output_type, GRN_TEXT_VALUE(buf), NULL); } else { grn_obj *range_obj; char type_name[GRN_TABLE_MAX_KEY_SIZE]; int type_name_len; range_obj = grn_ctx_at(ctx, range_id); type_name_len = grn_obj_name(ctx, range_obj, type_name, GRN_TABLE_MAX_KEY_SIZE); type_name[type_name_len] = '\0'; grn_output_table_column_info(ctx, outbuf, output_type, GRN_TEXT_VALUE(buf), type_name); } } static inline void grn_output_table_column_by_expression(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_expr_code *code, grn_expr_code *code_end, grn_obj *buf) { if (code_end <= code) { grn_output_table_column_info(ctx, outbuf, output_type, NULL, NULL); return; } switch (code_end[-1].op) { case GRN_OP_GET_MEMBER : if ((code_end - code) == 3) { GRN_BULK_REWIND(buf); grn_column_name_(ctx, code[0].value, buf); GRN_TEXT_PUTC(ctx, buf, '['); grn_inspect(ctx, buf, code[1].value); GRN_TEXT_PUTC(ctx, buf, ']'); GRN_TEXT_PUTC(ctx, buf, '\0'); grn_output_table_column_info(ctx, outbuf, output_type, GRN_TEXT_VALUE(buf), NULL); } else { grn_output_table_column(ctx, outbuf, output_type, code->value, buf); } break; default : grn_output_table_column(ctx, outbuf, output_type, code->value, buf); break; } } static inline void grn_output_table_columns_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int n_columns) { if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) { grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", n_columns); } else { grn_output_cstr(ctx, outbuf, output_type, "columns"); grn_output_array_open(ctx, outbuf, output_type, "columns", n_columns); } } static inline void grn_output_table_columns_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type) { if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) { grn_output_array_close(ctx, outbuf, output_type); } else { grn_output_array_close(ctx, outbuf, output_type); } } static inline void grn_output_table_columns_by_expression(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *table, grn_obj_format *format, grn_obj *buf) { int n_elements; int previous_comma_offset = -1; grn_bool is_first_comma = GRN_TRUE; grn_bool have_comma = GRN_FALSE; grn_expr *expr = (grn_expr *)format->expression; grn_expr_code *code; grn_expr_code *code_end = expr->codes + expr->codes_curr; n_elements = count_n_elements_in_expression(ctx, format->expression); grn_output_table_columns_open(ctx, outbuf, output_type, n_elements); for (code = expr->codes; code < code_end; code++) { int code_start_offset; if (code->op != GRN_OP_COMMA) { continue; } have_comma = GRN_TRUE; if (is_first_comma) { unsigned int n_used_codes; int code_end_offset; n_used_codes = grn_expr_code_n_used_codes(ctx, expr->codes, code - 1); code_end_offset = code - expr->codes - n_used_codes; grn_output_table_column_by_expression(ctx, outbuf, output_type, expr->codes, expr->codes + code_end_offset, buf); code_start_offset = code_end_offset; is_first_comma = GRN_FALSE; } else { code_start_offset = previous_comma_offset + 1; } grn_output_table_column_by_expression(ctx, outbuf, output_type, expr->codes + code_start_offset, code, buf); previous_comma_offset = code - expr->codes; } if (!have_comma && expr->codes_curr > 0) { grn_output_table_column_by_expression(ctx, outbuf, output_type, expr->codes, code_end, buf); } grn_output_table_columns_close(ctx, outbuf, output_type); } static inline void grn_output_table_columns_by_columns(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *table, grn_obj_format *format, grn_obj *buf) { int i; int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *); grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns); grn_output_table_columns_open(ctx, outbuf, output_type, ncolumns); for (i = 0; i < ncolumns; i++) { grn_output_table_column(ctx, outbuf, output_type, columns[i], buf); } grn_output_table_columns_close(ctx, outbuf, output_type); } void grn_output_table_columns(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *table, grn_obj_format *format) { grn_obj buf; GRN_TEXT_INIT(&buf, 0); if (format->expression) { grn_output_table_columns_by_expression(ctx, outbuf, output_type, table, format, &buf); } else { grn_output_table_columns_by_columns(ctx, outbuf, output_type, table, format, &buf); } GRN_OBJ_FIN(ctx, &buf); } static inline void grn_output_table_record_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int n_columns) { if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) { grn_output_array_open(ctx, outbuf, output_type, "HIT", n_columns); } else { grn_output_array_open(ctx, outbuf, output_type, "record", n_columns); } } static inline void grn_output_table_record_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type) { if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) { grn_output_array_close(ctx, outbuf, output_type); } else { grn_output_array_close(ctx, outbuf, output_type); } } static inline void grn_output_table_record_by_column(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *column, grn_id id) { grn_text_atoj(ctx, outbuf, output_type, column, id); } static inline void grn_output_table_record_by_expression(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *expression, grn_obj *record) { grn_expr *expr = (grn_expr *)expression; if (expr->codes_curr == 1 && expr->codes[0].op == GRN_OP_GET_VALUE) { grn_obj *column = expr->codes[0].value; grn_output_table_record_by_column(ctx, outbuf, output_type, column, GRN_RECORD_VALUE(record)); } else { grn_obj *result; result = grn_expr_exec(ctx, expression, 0); if (result) { grn_output_obj(ctx, outbuf, output_type, result, NULL); } else { grn_output_cstr(ctx, outbuf, output_type, ctx->errbuf); } } } static inline void grn_output_table_records_by_expression(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_table_cursor *tc, grn_obj_format *format) { int n_elements = 0; grn_id id; grn_obj *record; grn_expr *expr = (grn_expr *)format->expression; grn_expr_code *code; grn_expr_code *code_end = expr->codes + expr->codes_curr; n_elements = count_n_elements_in_expression(ctx, format->expression); record = grn_expr_get_var_by_offset(ctx, format->expression, 0); while ((id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL) { int previous_comma_offset = -1; grn_bool is_first_comma = GRN_TRUE; grn_bool have_comma = GRN_FALSE; GRN_RECORD_SET(ctx, record, id); grn_output_table_record_open(ctx, outbuf, output_type, n_elements); for (code = expr->codes; code < code_end; code++) { if (code->op == GRN_OP_COMMA) { int code_start_offset = previous_comma_offset + 1; int code_end_offset; int original_codes_curr = expr->codes_curr; have_comma = GRN_TRUE; if (is_first_comma) { int second_code_offset; unsigned int second_code_n_used_codes; second_code_offset = code - expr->codes - 1; second_code_n_used_codes = grn_expr_code_n_used_codes(ctx, expr->codes, expr->codes + second_code_offset); expr->codes_curr = second_code_offset - second_code_n_used_codes + 1; grn_output_table_record_by_expression(ctx, outbuf, output_type, format->expression, record); code_start_offset = expr->codes_curr; is_first_comma = GRN_FALSE; } code_end_offset = code - expr->codes - code_start_offset; expr->codes += code_start_offset; expr->codes_curr = code_end_offset; grn_output_table_record_by_expression(ctx, outbuf, output_type, format->expression, record); expr->codes -= code_start_offset; expr->codes_curr = original_codes_curr; previous_comma_offset = code - expr->codes; } } if (!have_comma && expr->codes_curr > 0) { grn_output_table_record_by_expression(ctx, outbuf, output_type, format->expression, record); } grn_output_table_record_close(ctx, outbuf, output_type); } } static inline void grn_output_table_records_by_columns(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_table_cursor *tc, grn_obj_format *format) { int i; grn_id id; int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *); grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns); while ((id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL) { grn_output_table_record_open(ctx, outbuf, output_type, ncolumns); for (i = 0; i < ncolumns; i++) { grn_output_table_record_by_column(ctx, outbuf, output_type, columns[i], id); } grn_output_table_record_close(ctx, outbuf, output_type); } } static inline void grn_output_table_records_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int n_records) { if (grn_ctx_get_command_version(ctx) >= GRN_COMMAND_VERSION_3) { grn_output_cstr(ctx, outbuf, output_type, "records"); grn_output_array_open(ctx, outbuf, output_type, "records", n_records); } } static inline void grn_output_table_records_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type) { if (grn_ctx_get_command_version(ctx) >= GRN_COMMAND_VERSION_3) { grn_output_array_close(ctx, outbuf, output_type); } } void grn_output_table_records(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *table, grn_obj_format *format) { grn_table_cursor *tc; grn_output_table_records_open(ctx, outbuf, output_type, format->limit); tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, format->offset, format->limit, GRN_CURSOR_ASCENDING); if (tc) { if (format->expression) { grn_output_table_records_by_expression(ctx, outbuf, output_type, tc, format); } else { grn_output_table_records_by_columns(ctx, outbuf, output_type, tc, format); } grn_table_cursor_close(ctx, tc); } else { ERRCLR(ctx); } grn_output_table_records_close(ctx, outbuf, output_type); } static void grn_output_result_set_open_v1(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *table, grn_obj_format *format, uint32_t n_additional_elements) { grn_obj buf; GRN_TEXT_INIT(&buf, 0); if (format) { int resultset_size = 1; /* resultset: [NHITS, (COLUMNS), (HITS)] */ if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) { resultset_size++; } resultset_size += format->limit; resultset_size += n_additional_elements; grn_output_array_open(ctx, outbuf, output_type, "RESULTSET", resultset_size); grn_output_result_set_n_hits(ctx, outbuf, output_type, format); if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) { grn_output_table_columns(ctx, outbuf, output_type, table, format); } grn_output_table_records(ctx, outbuf, output_type, table, format); } else { int i; grn_obj *column = grn_obj_column(ctx, table, GRN_COLUMN_NAME_KEY, GRN_COLUMN_NAME_KEY_LEN); grn_table_cursor *tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1, GRN_CURSOR_ASCENDING); grn_output_array_open(ctx, outbuf, output_type, "HIT", -1); if (tc) { grn_id id; for (i = 0; (id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL; i++) { GRN_BULK_REWIND(&buf); grn_obj_get_value(ctx, column, id, &buf); grn_text_esc(ctx, outbuf, GRN_BULK_HEAD(&buf), GRN_BULK_VSIZE(&buf)); } grn_table_cursor_close(ctx, tc); } grn_obj_unlink(ctx, column); } GRN_OBJ_FIN(ctx, &buf); } static void grn_output_result_set_close_v1(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *table, grn_obj_format *format) { grn_output_array_close(ctx, outbuf, output_type); } static void grn_output_result_set_open_v3(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *result_set, grn_obj_format *format, uint32_t n_additional_elements) { grn_obj buf; GRN_TEXT_INIT(&buf, 0); if (format) { int n_elements = 2; /* result_set: {"n_hits": N, ("columns": COLUMNS,) "records": records} */ if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) { n_elements++; } n_elements += n_additional_elements; grn_output_map_open(ctx, outbuf, output_type, "result_set", n_elements); grn_output_result_set_n_hits(ctx, outbuf, output_type, format); if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) { grn_output_table_columns(ctx, outbuf, output_type, result_set, format); } grn_output_table_records(ctx, outbuf, output_type, result_set, format); } else { grn_obj *column; int n_records; int n_elements = 1; column = grn_obj_column(ctx, result_set, GRN_COLUMN_NAME_KEY, GRN_COLUMN_NAME_KEY_LEN); n_elements += n_additional_elements; grn_output_map_open(ctx, outbuf, output_type, "result_set", n_elements); n_records = grn_table_size(ctx, result_set); grn_output_cstr(ctx, outbuf, output_type, "keys"); grn_output_array_open(ctx, outbuf, output_type, "keys", n_records); GRN_TABLE_EACH_BEGIN(ctx, result_set, cursor, id) { GRN_BULK_REWIND(&buf); grn_obj_get_value(ctx, column, id, &buf); grn_text_esc(ctx, outbuf, GRN_BULK_HEAD(&buf), GRN_BULK_VSIZE(&buf)); } GRN_TABLE_EACH_END(ctx, cursor); grn_output_array_close(ctx, outbuf, output_type); grn_obj_unlink(ctx, column); } GRN_OBJ_FIN(ctx, &buf); } static void grn_output_result_set_close_v3(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *result_set, grn_obj_format *format) { grn_output_map_close(ctx, outbuf, output_type); } void grn_output_result_set_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *result_set, grn_obj_format *format, uint32_t n_additional_elements) { if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) { grn_output_result_set_open_v1(ctx, outbuf, output_type, result_set, format, n_additional_elements); } else { grn_output_result_set_open_v3(ctx, outbuf, output_type, result_set, format, n_additional_elements); } } void grn_output_result_set_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *result_set, grn_obj_format *format) { if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) { grn_output_result_set_close_v1(ctx, outbuf, output_type, result_set, format); } else { grn_output_result_set_close_v3(ctx, outbuf, output_type, result_set, format); } } void grn_output_result_set(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *result_set, grn_obj_format *format) { uint32_t n_additional_elements = 0; grn_output_result_set_open(ctx, outbuf, output_type, result_set, format, n_additional_elements); grn_output_result_set_close(ctx, outbuf, output_type, result_set, format); } void grn_output_obj(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_obj *obj, grn_obj_format *format) { grn_obj buf; GRN_TEXT_INIT(&buf, 0); switch (obj->header.type) { case GRN_VOID : grn_output_void(ctx, outbuf, output_type, obj, format); break; case GRN_BULK : grn_output_bulk(ctx, outbuf, output_type, obj, format); break; case GRN_UVECTOR : grn_output_uvector(ctx, outbuf, output_type, obj, format); break; case GRN_VECTOR : grn_output_vector(ctx, outbuf, output_type, obj, format); break; case GRN_PVECTOR : grn_output_pvector(ctx, outbuf, output_type, obj, format); break; case GRN_TABLE_HASH_KEY : case GRN_TABLE_PAT_KEY : case GRN_TABLE_DAT_KEY : case GRN_TABLE_NO_KEY : /* Deprecated. Use grn_output_result_set() directly. */ grn_output_result_set(ctx, outbuf, output_type, obj, format); break; } GRN_OBJ_FIN(ctx, &buf); } typedef enum { XML_START, XML_START_ELEMENT, XML_END_ELEMENT, XML_TEXT } xml_status; typedef enum { XML_PLACE_NONE, XML_PLACE_COLUMN, XML_PLACE_HIT } xml_place; static char * transform_xml_next_column(grn_obj *columns, int n) { char *column = GRN_TEXT_VALUE(columns); while (n--) { while (*column) { column++; } column++; } return column; } static void transform_xml(grn_ctx *ctx, grn_obj *output, grn_obj *transformed) { char *s, *e; xml_status status = XML_START; xml_place place = XML_PLACE_NONE; grn_obj buf, name, columns, *expr; unsigned int len; int offset = 0, limit = 0, record_n = 0; int column_n = 0, column_text_n = 0, result_set_n = -1; grn_bool in_vector = GRN_FALSE; unsigned int vector_element_n = 0; grn_bool in_weight_vector = GRN_FALSE; unsigned int weight_vector_item_n = 0; s = GRN_TEXT_VALUE(output); e = GRN_BULK_CURR(output); GRN_TEXT_INIT(&buf, 0); GRN_TEXT_INIT(&name, 0); GRN_TEXT_INIT(&columns, 0); expr = ctx->impl->curr_expr; #define EQUAL_NAME_P(_name) \ (GRN_TEXT_LEN(&name) == strlen(_name) && \ !memcmp(GRN_TEXT_VALUE(&name), _name, strlen(_name))) while (s < e) { switch (*s) { case '<' : s++; switch (*s) { case '/' : status = XML_END_ELEMENT; s++; break; default : status = XML_START_ELEMENT; break; } GRN_BULK_REWIND(&name); break; case '>' : switch (status) { case XML_START_ELEMENT : if (EQUAL_NAME_P("COLUMN")) { place = XML_PLACE_COLUMN; column_text_n = 0; } else if (EQUAL_NAME_P("HIT")) { place = XML_PLACE_HIT; column_n = 0; if (result_set_n == 0) { GRN_TEXT_PUTS(ctx, transformed, "\n"); } else { GRN_TEXT_PUTS(ctx, transformed, "\n"); } } else if (EQUAL_NAME_P("VECTOR")) { char *c = transform_xml_next_column(&columns, column_n++); in_vector = GRN_TRUE; vector_element_n = 0; GRN_TEXT_PUTS(ctx, transformed, ""); } else if (EQUAL_NAME_P("WEIGHT_VECTOR")) { char *c = transform_xml_next_column(&columns, column_n++); in_weight_vector = GRN_TRUE; weight_vector_item_n = 0; GRN_TEXT_PUTS(ctx, transformed, ""); } break; case XML_END_ELEMENT : if (EQUAL_NAME_P("HIT")) { place = XML_PLACE_NONE; if (result_set_n == 0) { GRN_TEXT_PUTS(ctx, transformed, "\n"); } else { GRN_TEXT_PUTS(ctx, transformed, "/>\n"); } } else if (EQUAL_NAME_P("RESULTSET")) { place = XML_PLACE_NONE; if (result_set_n == 0) { GRN_TEXT_PUTS(ctx, transformed, "\n"); } else { GRN_TEXT_PUTS(ctx, transformed, "\n" "\n"); } } else if (EQUAL_NAME_P("RESULT")) { GRN_TEXT_PUTS(ctx, transformed, "\n" "\n" "\n"); } else if (EQUAL_NAME_P("VECTOR")) { in_vector = GRN_FALSE; GRN_TEXT_PUTS(ctx, transformed, "\n"); } else if (EQUAL_NAME_P("WEIGHT_VECTOR")) { in_weight_vector = GRN_FALSE; GRN_TEXT_PUTS(ctx, transformed, "\n"); } else { switch (place) { case XML_PLACE_HIT : if (result_set_n == 0) { if (in_vector) { if (vector_element_n > 0) { GRN_TEXT_PUTS(ctx, transformed, ", "); } GRN_TEXT_PUT(ctx, transformed, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf)); vector_element_n++; } else if (in_weight_vector) { grn_bool is_key; is_key = ((weight_vector_item_n % 2) == 0); if (is_key) { unsigned int weight_vector_key_n; weight_vector_key_n = weight_vector_item_n / 2; if (weight_vector_key_n > 0) { GRN_TEXT_PUTS(ctx, transformed, ", "); } } else { GRN_TEXT_PUTS(ctx, transformed, ":"); } GRN_TEXT_PUT(ctx, transformed, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf)); weight_vector_item_n++; } else { char *c = transform_xml_next_column(&columns, column_n++); GRN_TEXT_PUTS(ctx, transformed, ""); GRN_TEXT_PUT(ctx, transformed, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf)); GRN_TEXT_PUTS(ctx, transformed, "\n"); } } else { char *c = transform_xml_next_column(&columns, column_n++); GRN_TEXT_PUTS(ctx, transformed, c); GRN_TEXT_PUTS(ctx, transformed, "=\""); GRN_TEXT_PUT(ctx, transformed, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf)); GRN_TEXT_PUTS(ctx, transformed, "\" "); } break; default : if (EQUAL_NAME_P("NHITS")) { if (result_set_n == 0) { uint32_t nhits; grn_obj *offset_value, *limit_value; nhits = grn_atoui(GRN_TEXT_VALUE(&buf), GRN_BULK_CURR(&buf), NULL); offset_value = grn_expr_get_var(ctx, expr, "offset", strlen("offset")); limit_value = grn_expr_get_var(ctx, expr, "limit", strlen("limit")); if (GRN_TEXT_LEN(offset_value)) { offset = grn_atoi(GRN_TEXT_VALUE(offset_value), GRN_BULK_CURR(offset_value), NULL); } else { offset = 0; } if (GRN_TEXT_LEN(limit_value)) { limit = grn_atoi(GRN_TEXT_VALUE(limit_value), GRN_BULK_CURR(limit_value), NULL); } else { #define DEFAULT_LIMIT 10 limit = DEFAULT_LIMIT; #undef DEFAULT_LIMIT } grn_normalize_offset_and_limit(ctx, nhits, &offset, &limit); record_n = offset + 1; GRN_TEXT_PUTS(ctx, transformed, "\n" "\n" "\n" "\n" "\n"); } else { GRN_TEXT_PUTS(ctx, transformed, "\n"); } } else if (EQUAL_NAME_P("TEXT")) { switch (place) { case XML_PLACE_COLUMN : if (column_text_n == 0) { GRN_TEXT_PUT(ctx, &columns, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf)); GRN_TEXT_PUTC(ctx, &columns, '\0'); } column_text_n++; break; default : break; } } } } default : break; } s++; GRN_BULK_REWIND(&buf); status = XML_TEXT; break; default : len = grn_charlen(ctx, s, e); switch (status) { case XML_START_ELEMENT : case XML_END_ELEMENT : GRN_TEXT_PUT(ctx, &name, s, len); break; default : GRN_TEXT_PUT(ctx, &buf, s, len); break; } s += len; break; } } #undef EQUAL_NAME_P GRN_OBJ_FIN(ctx, &buf); GRN_OBJ_FIN(ctx, &name); GRN_OBJ_FIN(ctx, &columns); } #ifdef GRN_WITH_MESSAGE_PACK typedef struct { grn_ctx *ctx; grn_obj *buffer; } msgpack_writer_ctx; static inline int msgpack_buffer_writer(void* data, const char* buf, msgpack_size_t len) { msgpack_writer_ctx *writer_ctx = (msgpack_writer_ctx *)data; return grn_bulk_write(writer_ctx->ctx, writer_ctx->buffer, buf, len); } #endif #define JSON_CALLBACK_PARAM "callback" static void grn_output_envelope_json_v1(grn_ctx *ctx, grn_rc rc, grn_obj *head, grn_obj *body, grn_obj *foot, double started, double elapsed, const char *file, int line) { size_t indent_level = 0; json_array_open(ctx, head, &indent_level); { json_array_open(ctx, head, &indent_level); { grn_text_itoa(ctx, head, rc); json_element_end(ctx, head, indent_level); grn_text_ftoa(ctx, head, started); json_element_end(ctx, head, indent_level); grn_text_ftoa(ctx, head, elapsed); if (rc != GRN_SUCCESS) { json_element_end(ctx, head, indent_level); grn_text_esc(ctx, head, ctx->errbuf, strlen(ctx->errbuf)); if (ctx->errfunc && ctx->errfile) { grn_obj *command; json_element_end(ctx, head, indent_level); json_array_open(ctx, head, &indent_level); { json_array_open(ctx, head, &indent_level); { grn_text_esc(ctx, head, ctx->errfunc, strlen(ctx->errfunc)); json_element_end(ctx, head, indent_level); grn_text_esc(ctx, head, ctx->errfile, strlen(ctx->errfile)); json_element_end(ctx, head, indent_level); grn_text_itoa(ctx, head, ctx->errline); } json_array_close(ctx, head, &indent_level); if (file && (command = GRN_CTX_USER_DATA(ctx)->ptr)) { json_element_end(ctx, head, indent_level); json_array_open(ctx, head, &indent_level); { grn_text_esc(ctx, head, file, strlen(file)); json_element_end(ctx, head, indent_level); grn_text_itoa(ctx, head, line); json_element_end(ctx, head, indent_level); grn_text_esc(ctx, head, GRN_TEXT_VALUE(command), GRN_TEXT_LEN(command)); } json_array_close(ctx, head, &indent_level); } } json_array_close(ctx, head, &indent_level); } } } json_array_close(ctx, head, &indent_level); } if (GRN_TEXT_LEN(body)) { json_element_end(ctx, head, indent_level); } json_array_close(ctx, foot, &indent_level); } static void grn_output_envelope_json(grn_ctx *ctx, grn_rc rc, grn_obj *head, grn_obj *body, grn_obj *foot, double started, double elapsed, const char *file, int line) { size_t indent_level = 0; json_map_open(ctx, head, &indent_level); { json_key(ctx, head, "header"); json_map_open(ctx, head, &indent_level); { json_key(ctx, head, "return_code"); grn_text_itoa(ctx, head, rc); json_value_end(ctx, head, indent_level); json_key(ctx, head, "start_time"); grn_text_ftoa(ctx, head, started); json_value_end(ctx, head, indent_level); json_key(ctx, head, "elapsed_time"); grn_text_ftoa(ctx, head, elapsed); if (rc != GRN_SUCCESS) { json_value_end(ctx, head, indent_level); json_key(ctx, head, "error"); json_map_open(ctx, head, &indent_level); { json_key(ctx, head, "message"); grn_text_esc(ctx, head, ctx->errbuf, strlen(ctx->errbuf)); if (ctx->errfunc && ctx->errfile) { json_value_end(ctx, head, indent_level); json_key(ctx, head, "function"); grn_text_esc(ctx, head, ctx->errfunc, strlen(ctx->errfunc)); json_value_end(ctx, head, indent_level); json_key(ctx, head, "file"); grn_text_esc(ctx, head, ctx->errfile, strlen(ctx->errfile)); json_value_end(ctx, head, indent_level); json_key(ctx, head, "line"); grn_text_itoa(ctx, head, ctx->errline); } if (file) { grn_obj *command; command = GRN_CTX_USER_DATA(ctx)->ptr; if (command) { json_value_end(ctx, head, indent_level); json_key(ctx, head, "input"); json_map_open(ctx, head, &indent_level); { json_key(ctx, head, "file"); grn_text_esc(ctx, head, file, strlen(file)); json_value_end(ctx, head, indent_level); json_key(ctx, head, "line"); grn_text_itoa(ctx, head, line); json_value_end(ctx, head, indent_level); json_key(ctx, head, "command"); grn_text_esc(ctx, head, GRN_TEXT_VALUE(command), GRN_TEXT_LEN(command)); } json_map_close(ctx, head, &indent_level); } } } json_map_close(ctx, head, &indent_level); } } json_map_close(ctx, head, &indent_level); if (GRN_TEXT_LEN(body)) { json_value_end(ctx, head, indent_level); json_key(ctx, head, "body"); } json_map_close(ctx, foot, &indent_level); } } #ifdef GRN_WITH_MESSAGE_PACK static void msgpack_pack_cstr(msgpack_packer *packer, const char *string) { size_t size; size = strlen(string); msgpack_pack_str(packer, size); msgpack_pack_str_body(packer, string, size); } static void grn_output_envelope_msgpack_v1(grn_ctx *ctx, grn_rc rc, grn_obj *head, grn_obj *body, grn_obj *foot, double started, double elapsed, const char *file, int line) { msgpack_writer_ctx head_writer_ctx; msgpack_packer header_packer; int header_size; head_writer_ctx.ctx = ctx; head_writer_ctx.buffer = head; msgpack_packer_init(&header_packer, &head_writer_ctx, msgpack_buffer_writer); /* [HEADER, (BODY)] */ if (GRN_TEXT_LEN(body) > 0) { msgpack_pack_array(&header_packer, 2); } else { msgpack_pack_array(&header_packer, 1); } /* HEADER := [rc, started, elapsed, (error, (ERROR DETAIL))] */ header_size = 3; if (rc != GRN_SUCCESS) { header_size++; if (ctx->errfunc && ctx->errfile) { header_size++; } } msgpack_pack_array(&header_packer, header_size); msgpack_pack_int(&header_packer, rc); msgpack_pack_double(&header_packer, started); msgpack_pack_double(&header_packer, elapsed); if (rc != GRN_SUCCESS) { msgpack_pack_str(&header_packer, strlen(ctx->errbuf)); msgpack_pack_str_body(&header_packer, ctx->errbuf, strlen(ctx->errbuf)); if (ctx->errfunc && ctx->errfile) { grn_obj *command = GRN_CTX_USER_DATA(ctx)->ptr; int error_detail_size; /* ERROR DETAIL : = [[errfunc, errfile, errline, (file, line, command)]] */ /* TODO: output backtrace */ msgpack_pack_array(&header_packer, 1); error_detail_size = 3; if (command) { error_detail_size += 3; } msgpack_pack_array(&header_packer, error_detail_size); msgpack_pack_str(&header_packer, strlen(ctx->errfunc)); msgpack_pack_str_body(&header_packer, ctx->errfunc, strlen(ctx->errfunc)); msgpack_pack_str(&header_packer, strlen(ctx->errfile)); msgpack_pack_str_body(&header_packer, ctx->errfile, strlen(ctx->errfile)); msgpack_pack_int(&header_packer, ctx->errline); if (command) { if (file) { msgpack_pack_str(&header_packer, strlen(file)); msgpack_pack_str_body(&header_packer, file, strlen(file)); } else { msgpack_pack_str(&header_packer, 7); msgpack_pack_str_body(&header_packer, "(stdin)", 7); } msgpack_pack_int(&header_packer, line); msgpack_pack_str(&header_packer, GRN_TEXT_LEN(command)); msgpack_pack_str_body(&header_packer, GRN_TEXT_VALUE(command), GRN_TEXT_LEN(command)); } } } } static void grn_output_envelope_msgpack(grn_ctx *ctx, grn_rc rc, grn_obj *head, grn_obj *body, grn_obj *foot, double started, double elapsed, const char *file, int line) { msgpack_writer_ctx writer_ctx; msgpack_packer packer; int n_elements; writer_ctx.ctx = ctx; writer_ctx.buffer = head; msgpack_packer_init(&packer, &writer_ctx, msgpack_buffer_writer); /* * ENVELOPE := { * "header": HEADER, * "body": BODY (optional) * } */ if (GRN_TEXT_LEN(body) > 0) { n_elements = 2; } else { n_elements = 1; } msgpack_pack_map(&packer, n_elements); { int n_header_elements = 3; /* * HEADER := { * "return_code": rc, * "start_time": started, * "elapsed_time": elapsed, " "error": { (optional) * "message": errbuf, * "function": errfunc, * "file": errfile, * "line": errline, * "input": { (optional) * "file": input_file, * "line": line, * "command": command * } * } * } */ if (rc != GRN_SUCCESS) { n_header_elements++; } msgpack_pack_cstr(&packer, "header"); msgpack_pack_map(&packer, n_header_elements); { msgpack_pack_cstr(&packer, "return_code"); msgpack_pack_int(&packer, rc); msgpack_pack_cstr(&packer, "start_time"); msgpack_pack_double(&packer, started); msgpack_pack_cstr(&packer, "elapsed_time"); msgpack_pack_double(&packer, elapsed); if (rc != GRN_SUCCESS) { int n_error_elements = 1; grn_obj *command; if (ctx->errfunc) { n_error_elements++; } if (ctx->errfile) { n_error_elements += 2; } command = GRN_CTX_USER_DATA(ctx)->ptr; if (file || command) { n_error_elements++; } msgpack_pack_cstr(&packer, "error"); msgpack_pack_map(&packer, n_error_elements); { msgpack_pack_cstr(&packer, "message"); msgpack_pack_cstr(&packer, ctx->errbuf); if (ctx->errfunc) { msgpack_pack_cstr(&packer, "function"); msgpack_pack_cstr(&packer, ctx->errfunc); } if (ctx->errfile) { msgpack_pack_cstr(&packer, "file"); msgpack_pack_cstr(&packer, ctx->errfile); msgpack_pack_cstr(&packer, "line"); msgpack_pack_int(&packer, ctx->errline); } if (file || command) { int n_input_elements = 0; if (file) { n_input_elements += 2; } if (command) { n_input_elements++; } msgpack_pack_cstr(&packer, "input"); msgpack_pack_map(&packer, n_input_elements); if (file) { msgpack_pack_cstr(&packer, "file"); msgpack_pack_cstr(&packer, file); msgpack_pack_cstr(&packer, "line"); msgpack_pack_int(&packer, line); } if (command) { msgpack_pack_cstr(&packer, "command"); msgpack_pack_str(&packer, GRN_TEXT_LEN(command)); msgpack_pack_str_body(&packer, GRN_TEXT_VALUE(command), GRN_TEXT_LEN(command)); } } } } } if (GRN_TEXT_LEN(body) > 0) { msgpack_pack_cstr(&packer, "body"); } } } #endif /* GRN_WITH_MESSAGE_PACK */ void grn_output_envelope(grn_ctx *ctx, grn_rc rc, grn_obj *head, grn_obj *body, grn_obj *foot, const char *file, int line) { double started, finished, elapsed; grn_timeval tv_now; grn_timeval_now(ctx, &tv_now); started = ctx->impl->tv.tv_sec; started += ctx->impl->tv.tv_nsec / GRN_TIME_NSEC_PER_SEC_F; finished = tv_now.tv_sec; finished += tv_now.tv_nsec / GRN_TIME_NSEC_PER_SEC_F; elapsed = finished - started; switch (ctx->impl->output.type) { case GRN_CONTENT_JSON: { grn_obj *expr; grn_obj *jsonp_func = NULL; expr = ctx->impl->curr_expr; if (expr) { jsonp_func = grn_expr_get_var(ctx, expr, JSON_CALLBACK_PARAM, strlen(JSON_CALLBACK_PARAM)); } if (jsonp_func && GRN_TEXT_LEN(jsonp_func)) { GRN_TEXT_PUT(ctx, head, GRN_TEXT_VALUE(jsonp_func), GRN_TEXT_LEN(jsonp_func)); GRN_TEXT_PUTC(ctx, head, '('); } if (grn_ctx_get_command_version(ctx) <= GRN_COMMAND_VERSION_2) { grn_output_envelope_json_v1(ctx, rc, head, body, foot, started, elapsed, file, line); } else { grn_output_envelope_json(ctx, rc, head, body, foot, started, elapsed, file, line); } if (jsonp_func && GRN_TEXT_LEN(jsonp_func)) { GRN_TEXT_PUTS(ctx, foot, ");"); } } break; case GRN_CONTENT_TSV: grn_text_itoa(ctx, head, rc); GRN_TEXT_PUTC(ctx, head, '\t'); grn_text_ftoa(ctx, head, started); GRN_TEXT_PUTC(ctx, head, '\t'); grn_text_ftoa(ctx, head, elapsed); if (rc != GRN_SUCCESS) { GRN_TEXT_PUTC(ctx, head, '\t'); grn_text_esc(ctx, head, ctx->errbuf, strlen(ctx->errbuf)); if (ctx->errfunc && ctx->errfile) { /* TODO: output backtrace */ GRN_TEXT_PUTC(ctx, head, '\t'); grn_text_esc(ctx, head, ctx->errfunc, strlen(ctx->errfunc)); GRN_TEXT_PUTC(ctx, head, '\t'); grn_text_esc(ctx, head, ctx->errfile, strlen(ctx->errfile)); GRN_TEXT_PUTC(ctx, head, '\t'); grn_text_itoa(ctx, head, ctx->errline); } } GRN_TEXT_PUTS(ctx, head, "\n"); GRN_TEXT_PUTS(ctx, foot, "\nEND"); break; case GRN_CONTENT_XML: { char buf[GRN_TABLE_MAX_KEY_SIZE]; int is_select = 0; if (!rc && ctx->impl->curr_expr) { int len = grn_obj_name(ctx, ctx->impl->curr_expr, buf, GRN_TABLE_MAX_KEY_SIZE); buf[len] = '\0'; is_select = strcmp(buf, "select") == 0; } if (is_select) { grn_obj transformed; GRN_TEXT_INIT(&transformed, 0); transform_xml(ctx, body, &transformed); GRN_TEXT_SET(ctx, body, GRN_TEXT_VALUE(&transformed), GRN_TEXT_LEN(&transformed)); GRN_OBJ_FIN(ctx, &transformed); } else { GRN_TEXT_PUTS(ctx, head, "\n\n"); if (rc != GRN_SUCCESS) { GRN_TEXT_PUTS(ctx, head, ""); grn_text_escape_xml(ctx, head, ctx->errbuf, strlen(ctx->errbuf)); if (ctx->errfunc && ctx->errfile) { /* TODO: output backtrace */ GRN_TEXT_PUTS(ctx, head, "errfunc, strlen(ctx->errfunc)); GRN_TEXT_PUTS(ctx, head, "\" FILE=\""); grn_text_escape_xml(ctx, head, ctx->errfile, strlen(ctx->errfile)); GRN_TEXT_PUTS(ctx, head, "\" LINE=\""); grn_text_itoa(ctx, head, ctx->errline); GRN_TEXT_PUTS(ctx, head, "\"/>"); } GRN_TEXT_PUTS(ctx, head, ""); } GRN_TEXT_PUTS(ctx, foot, "\n"); } } break; case GRN_CONTENT_MSGPACK: #ifdef GRN_WITH_MESSAGE_PACK if (grn_ctx_get_command_version(ctx) <= GRN_COMMAND_VERSION_2) { grn_output_envelope_msgpack_v1(ctx, rc, head, body, foot, started, elapsed, file, line); } else { grn_output_envelope_msgpack(ctx, rc, head, body, foot, started, elapsed, file, line); } #endif /* GRN_WITH_MESSAGE_PACK */ break; case GRN_CONTENT_GROONGA_COMMAND_LIST : break; case GRN_CONTENT_NONE: break; } } static inline grn_bool is_output_columns_format_v1(grn_ctx *ctx, const char *output_columns, unsigned int output_columns_len) { const char *current; const char *end; grn_bool in_identifier = GRN_FALSE; current = output_columns; end = current + output_columns_len; while (current < end) { int char_length; char_length = grn_charlen(ctx, current, end); if (char_length != 1) { return GRN_FALSE; } switch (current[0]) { case ' ' : case ',' : in_identifier = GRN_FALSE; break; case '_' : in_identifier = GRN_TRUE; break; case '.' : case '-' : case '#' : case '@' : if (!in_identifier) { return GRN_FALSE; } break; default : if ('a' <= current[0] && current[0] <= 'z') { in_identifier = GRN_TRUE; break; } else if ('A' <= current[0] && current[0] <= 'Z') { in_identifier = GRN_TRUE; break; } else if ('0' <= current[0] && current[0] <= '9') { in_identifier = GRN_TRUE; break; } else { return GRN_FALSE; } } current += char_length; } return GRN_TRUE; } grn_rc grn_output_format_set_columns(grn_ctx *ctx, grn_obj_format *format, grn_obj *table, const char *columns, int columns_len) { grn_rc rc; if (is_output_columns_format_v1(ctx, columns, columns_len)) { rc = grn_obj_columns(ctx, table, columns, columns_len, &(format->columns)); } else { grn_obj *variable; GRN_EXPR_CREATE_FOR_QUERY(ctx, table, format->expression, variable); rc = grn_expr_parse(ctx, format->expression, columns, columns_len, NULL, GRN_OP_MATCH, GRN_OP_AND, GRN_EXPR_SYNTAX_OUTPUT_COLUMNS); } return rc; }