summaryrefslogtreecommitdiffstats
path: root/storage/mroonga/vendor/groonga/lib/ctx.c
diff options
context:
space:
mode:
Diffstat (limited to 'storage/mroonga/vendor/groonga/lib/ctx.c')
-rw-r--r--storage/mroonga/vendor/groonga/lib/ctx.c1873
1 files changed, 1873 insertions, 0 deletions
diff --git a/storage/mroonga/vendor/groonga/lib/ctx.c b/storage/mroonga/vendor/groonga/lib/ctx.c
new file mode 100644
index 00000000..1fd912d4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/ctx.c
@@ -0,0 +1,1873 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2009-2017 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 <string.h>
+#include "grn_request_canceler.h"
+#include "grn_request_timer.h"
+#include "grn_tokenizers.h"
+#include "grn_ctx_impl.h"
+#include "grn_ii.h"
+#include "grn_pat.h"
+#include "grn_index_column.h"
+#include "grn_proc.h"
+#include "grn_plugin.h"
+#include "grn_snip.h"
+#include "grn_output.h"
+#include "grn_normalizer.h"
+#include "grn_mrb.h"
+#include "grn_ctx_impl_mrb.h"
+#include "grn_logger.h"
+#include "grn_cache.h"
+#include "grn_expr.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+
+#ifdef GRN_WITH_ONIGMO
+# define GRN_SUPPORT_REGEXP
+#endif /* GRN_WITH_ONIGMO */
+
+#ifdef GRN_SUPPORT_REGEXP
+# include <onigmo.h>
+#endif /* GRN_SUPPORT_REGEXP */
+
+#ifdef WIN32
+# include <share.h>
+#else /* WIN32 */
+# include <netinet/in.h>
+#endif /* WIN32 */
+
+#define GRN_CTX_INITIALIZER(enc) \
+ { GRN_SUCCESS, 0, enc, 0, GRN_LOG_NOTICE,\
+ GRN_CTX_FIN, 0, 0, 0, 0, {0}, NULL, NULL, NULL, NULL, NULL, \
+ {NULL, NULL,NULL, NULL,NULL, NULL,NULL, NULL,NULL, NULL,NULL, NULL,NULL, NULL,NULL, NULL}, ""}
+
+#define GRN_CTX_CLOSED(ctx) ((ctx)->stat == GRN_CTX_FIN)
+
+grn_ctx grn_gctx = GRN_CTX_INITIALIZER(GRN_ENC_DEFAULT);
+int grn_pagesize;
+grn_critical_section grn_glock;
+uint32_t grn_gtick;
+int grn_lock_timeout = GRN_LOCK_TIMEOUT;
+
+#ifdef USE_UYIELD
+int grn_uyield_count = 0;
+#endif
+
+static grn_bool grn_ctx_per_db = GRN_FALSE;
+
+static void
+grn_init_from_env(void)
+{
+ {
+ char grn_ctx_per_db_env[GRN_ENV_BUFFER_SIZE];
+ grn_getenv("GRN_CTX_PER_DB",
+ grn_ctx_per_db_env,
+ GRN_ENV_BUFFER_SIZE);
+ if (grn_ctx_per_db_env[0] && strcmp(grn_ctx_per_db_env, "yes") == 0) {
+ grn_ctx_per_db = GRN_TRUE;
+ }
+ }
+
+ grn_alloc_init_from_env();
+ grn_mrb_init_from_env();
+ grn_ctx_impl_mrb_init_from_env();
+ grn_io_init_from_env();
+ grn_ii_init_from_env();
+ grn_db_init_from_env();
+ grn_expr_init_from_env();
+ grn_index_column_init_from_env();
+ grn_proc_init_from_env();
+ grn_plugin_init_from_env();
+}
+
+static void
+grn_init_external_libraries(void)
+{
+#ifdef GRN_SUPPORT_REGEXP
+ onig_init();
+#endif /* GRN_SUPPORT_REGEXP */
+}
+
+static void
+grn_fin_external_libraries(void)
+{
+#ifdef GRN_SUPPORT_REGEXP
+ onig_end();
+#endif /* GRN_SUPPORT_REGEXP */
+}
+
+void
+grn_sleep(uint32_t seconds)
+{
+#ifdef WIN32
+ Sleep(seconds * 1000);
+#else // WIN32
+ sleep(seconds);
+#endif // WIN32
+}
+
+void
+grn_nanosleep(uint64_t nanoseconds)
+{
+#ifdef WIN32
+ Sleep((DWORD)(nanoseconds / 1000000));
+#else // WIN32
+ struct timespec interval;
+ interval.tv_sec = (time_t)(nanoseconds / 1000000000);
+ interval.tv_nsec = (long)(nanoseconds % 1000000000);
+ nanosleep(&interval, NULL);
+#endif // WIN32
+}
+
+const char *
+grn_get_global_error_message(void)
+{
+ return grn_gctx.errbuf;
+}
+
+static void
+grn_loader_init(grn_loader *loader)
+{
+ GRN_TEXT_INIT(&loader->values, 0);
+ GRN_UINT32_INIT(&loader->level, GRN_OBJ_VECTOR);
+ GRN_PTR_INIT(&loader->columns, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ GRN_UINT32_INIT(&loader->ids, GRN_OBJ_VECTOR);
+ GRN_INT32_INIT(&loader->return_codes, GRN_OBJ_VECTOR);
+ GRN_TEXT_INIT(&loader->error_messages, GRN_OBJ_VECTOR);
+ loader->id_offset = -1;
+ loader->key_offset = -1;
+ loader->table = NULL;
+ loader->last = NULL;
+ loader->ifexists = NULL;
+ loader->each = NULL;
+ loader->values_size = 0;
+ loader->nrecords = 0;
+ loader->stat = GRN_LOADER_BEGIN;
+ loader->columns_status = GRN_LOADER_COLUMNS_UNSET;
+ loader->rc = GRN_SUCCESS;
+ loader->errbuf[0] = '\0';
+ loader->output_ids = GRN_FALSE;
+ loader->output_errors = GRN_FALSE;
+}
+
+void
+grn_ctx_loader_clear(grn_ctx *ctx)
+{
+ grn_loader *loader = &ctx->impl->loader;
+ grn_obj *v = (grn_obj *)(GRN_BULK_HEAD(&loader->values));
+ grn_obj *ve = (grn_obj *)(GRN_BULK_CURR(&loader->values));
+ grn_obj **p = (grn_obj **)GRN_BULK_HEAD(&loader->columns);
+ uint32_t i = GRN_BULK_VSIZE(&loader->columns) / sizeof(grn_obj *);
+ if (ctx->impl->db) { while (i--) { grn_obj_unlink(ctx, *p++); } }
+ if (loader->ifexists) { grn_obj_unlink(ctx, loader->ifexists); }
+ if (loader->each) { grn_obj_unlink(ctx, loader->each); }
+ while (v < ve) { GRN_OBJ_FIN(ctx, v++); }
+ GRN_OBJ_FIN(ctx, &loader->values);
+ GRN_OBJ_FIN(ctx, &loader->level);
+ GRN_OBJ_FIN(ctx, &loader->columns);
+ GRN_OBJ_FIN(ctx, &loader->ids);
+ GRN_OBJ_FIN(ctx, &loader->return_codes);
+ GRN_OBJ_FIN(ctx, &loader->error_messages);
+ grn_loader_init(loader);
+}
+
+#define IMPL_SIZE ((sizeof(struct _grn_ctx_impl) + (grn_pagesize - 1)) & ~(grn_pagesize - 1))
+
+#ifdef GRN_WITH_MESSAGE_PACK
+static int
+grn_msgpack_buffer_write(void *data, const char *buf, msgpack_size_t len)
+{
+ grn_ctx *ctx = (grn_ctx *)data;
+ return grn_bulk_write(ctx, ctx->impl->output.buf, buf, len);
+}
+#endif
+
+static grn_rc
+grn_ctx_impl_init(grn_ctx *ctx)
+{
+ grn_io_mapinfo mi;
+ if (!(ctx->impl = grn_io_anon_map(ctx, &mi, IMPL_SIZE))) {
+ return ctx->rc;
+ }
+ grn_alloc_init_ctx_impl(ctx);
+ ctx->impl->encoding = ctx->encoding;
+ ctx->impl->lifoseg = -1;
+ ctx->impl->currseg = -1;
+ CRITICAL_SECTION_INIT(ctx->impl->lock);
+ if (!(ctx->impl->values = grn_array_create(ctx, NULL, sizeof(grn_db_obj *),
+ GRN_ARRAY_TINY))) {
+ CRITICAL_SECTION_FIN(ctx->impl->lock);
+ grn_io_anon_unmap(ctx, &mi, IMPL_SIZE);
+ ctx->impl = NULL;
+ return ctx->rc;
+ }
+ if (!(ctx->impl->temporary_columns = grn_pat_create(ctx, NULL,
+ GRN_TABLE_MAX_KEY_SIZE,
+ sizeof(grn_obj *),
+ 0))) {
+ grn_array_close(ctx, ctx->impl->values);
+ CRITICAL_SECTION_FIN(ctx->impl->lock);
+ grn_io_anon_unmap(ctx, &mi, IMPL_SIZE);
+ ctx->impl = NULL;
+ return ctx->rc;
+ }
+ if (!(ctx->impl->ios = grn_hash_create(ctx, NULL, GRN_TABLE_MAX_KEY_SIZE,
+ sizeof(grn_io *),
+ GRN_OBJ_KEY_VAR_SIZE|GRN_HASH_TINY))) {
+ grn_array_close(ctx, ctx->impl->values);
+ grn_pat_close(ctx, ctx->impl->temporary_columns);
+ CRITICAL_SECTION_FIN(ctx->impl->lock);
+ grn_io_anon_unmap(ctx, &mi, IMPL_SIZE);
+ ctx->impl = NULL;
+ return ctx->rc;
+ }
+ ctx->impl->db = NULL;
+
+ ctx->impl->expr_vars = grn_hash_create(ctx, NULL, sizeof(grn_id), sizeof(grn_obj *), 0);
+ ctx->impl->stack_curr = 0;
+ ctx->impl->curr_expr = NULL;
+ GRN_TEXT_INIT(&ctx->impl->current_request_id, 0);
+ ctx->impl->current_request_timer_id = NULL;
+ ctx->impl->parser = NULL;
+
+ GRN_TEXT_INIT(&ctx->impl->output.names, GRN_OBJ_VECTOR);
+ GRN_UINT32_INIT(&ctx->impl->output.levels, GRN_OBJ_VECTOR);
+
+ ctx->impl->command.flags = 0;
+ if (ctx == &grn_gctx) {
+ ctx->impl->command.version = GRN_COMMAND_VERSION_STABLE;
+ } else {
+ ctx->impl->command.version = grn_get_default_command_version();
+ }
+ ctx->impl->command.keep.command = NULL;
+ ctx->impl->command.keep.version = ctx->impl->command.version;
+
+ if (ctx == &grn_gctx) {
+ ctx->impl->match_escalation_threshold =
+ GRN_DEFAULT_MATCH_ESCALATION_THRESHOLD;
+ } else {
+ ctx->impl->match_escalation_threshold =
+ grn_get_default_match_escalation_threshold();
+ }
+
+ ctx->impl->finalizer = NULL;
+
+ ctx->impl->com = NULL;
+ ctx->impl->output.buf = grn_obj_open(ctx, GRN_BULK, 0, GRN_DB_TEXT);
+ ctx->impl->output.func = NULL;
+ ctx->impl->output.data.ptr = NULL;
+#ifdef GRN_WITH_MESSAGE_PACK
+ msgpack_packer_init(&ctx->impl->output.msgpacker,
+ ctx, grn_msgpack_buffer_write);
+#endif
+ ctx->impl->tv.tv_sec = 0;
+ ctx->impl->tv.tv_nsec = 0;
+ ctx->impl->edge = NULL;
+ grn_loader_init(&ctx->impl->loader);
+ ctx->impl->plugin_path = NULL;
+
+ GRN_TEXT_INIT(&ctx->impl->query_log_buf, 0);
+
+ ctx->impl->previous_errbuf[0] = '\0';
+ ctx->impl->n_same_error_messages = 0;
+
+ grn_ctx_impl_mrb_init(ctx);
+
+ GRN_TEXT_INIT(&(ctx->impl->temporary_open_spaces.stack), 0);
+ ctx->impl->temporary_open_spaces.current = NULL;
+
+ return ctx->rc;
+}
+
+void
+grn_ctx_set_keep_command(grn_ctx *ctx, grn_obj *command)
+{
+ ctx->impl->command.keep.command = command;
+ ctx->impl->command.keep.version = ctx->impl->command.version;
+}
+
+static void
+grn_ctx_impl_clear_n_same_error_messagges(grn_ctx *ctx)
+{
+ if (ctx->impl->n_same_error_messages == 0) {
+ return;
+ }
+
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "(%u same messages are truncated)",
+ ctx->impl->n_same_error_messages);
+ ctx->impl->n_same_error_messages = 0;
+}
+
+grn_bool
+grn_ctx_impl_should_log(grn_ctx *ctx)
+{
+ if (!ctx->impl) {
+ return GRN_TRUE;
+ }
+
+ if (strcmp(ctx->errbuf, ctx->impl->previous_errbuf) == 0) {
+ ctx->impl->n_same_error_messages++;
+ return GRN_FALSE;
+ }
+
+ return GRN_TRUE;
+}
+
+void
+grn_ctx_impl_set_current_error_message(grn_ctx *ctx)
+{
+ if (!ctx->impl) {
+ return;
+ }
+
+ grn_ctx_impl_clear_n_same_error_messagges(ctx);
+ grn_strcpy(ctx->impl->previous_errbuf, GRN_CTX_MSGSIZE, ctx->errbuf);
+}
+
+static grn_rc
+grn_ctx_init_internal(grn_ctx *ctx, int flags)
+{
+ if (!ctx) { return GRN_INVALID_ARGUMENT; }
+ // if (ctx->stat != GRN_CTX_FIN) { return GRN_INVALID_ARGUMENT; }
+ ctx->rc = GRN_SUCCESS;
+ ERRCLR(ctx);
+ ctx->flags = flags;
+ if (grn_ctx_per_db) {
+ ctx->flags |= GRN_CTX_PER_DB;
+ }
+ ctx->stat = GRN_CTX_INITED;
+ ctx->encoding = grn_gctx.encoding;
+ ctx->seqno = 0;
+ ctx->seqno2 = 0;
+ ctx->subno = 0;
+ ctx->impl = NULL;
+ ctx->user_data.ptr = NULL;
+ CRITICAL_SECTION_ENTER(grn_glock);
+ ctx->next = grn_gctx.next;
+ ctx->prev = &grn_gctx;
+ grn_gctx.next->prev = ctx;
+ grn_gctx.next = ctx;
+ CRITICAL_SECTION_LEAVE(grn_glock);
+ ctx->errline = 0;
+ ctx->errfile = "";
+ ctx->errfunc = "";
+ ctx->trace[0] = NULL;
+ ctx->errbuf[0] = '\0';
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ctx_init(grn_ctx *ctx, int flags)
+{
+ grn_rc rc;
+
+ rc = grn_ctx_init_internal(ctx, flags);
+ if (rc == GRN_SUCCESS) {
+ grn_ctx_impl_init(ctx);
+ rc = ctx->rc;
+ if (rc != GRN_SUCCESS) {
+ grn_ctx_fin(ctx);
+ if (flags & GRN_CTX_ALLOCATED) {
+ CRITICAL_SECTION_ENTER(grn_glock);
+ ctx->next->prev = ctx->prev;
+ ctx->prev->next = ctx->next;
+ CRITICAL_SECTION_LEAVE(grn_glock);
+ }
+ }
+ }
+
+ return rc;
+}
+
+grn_ctx *
+grn_ctx_open(int flags)
+{
+ grn_ctx *ctx = GRN_GMALLOCN(grn_ctx, 1);
+ if (ctx) {
+ grn_ctx_init(ctx, flags|GRN_CTX_ALLOCATED);
+ if (ERRP(ctx, GRN_ERROR)) {
+ GRN_GFREE(ctx);
+ ctx = NULL;
+ }
+ }
+ return ctx;
+}
+
+grn_rc
+grn_ctx_fin(grn_ctx *ctx)
+{
+ grn_rc rc = GRN_SUCCESS;
+ if (!ctx) { return GRN_INVALID_ARGUMENT; }
+ if (ctx->stat == GRN_CTX_FIN) { return GRN_INVALID_ARGUMENT; }
+ if (!(ctx->flags & GRN_CTX_ALLOCATED)) {
+ CRITICAL_SECTION_ENTER(grn_glock);
+ ctx->next->prev = ctx->prev;
+ ctx->prev->next = ctx->next;
+ CRITICAL_SECTION_LEAVE(grn_glock);
+ }
+ if (ctx->impl) {
+ grn_ctx_impl_clear_n_same_error_messagges(ctx);
+ if (ctx->impl->finalizer) {
+ ctx->impl->finalizer(ctx, 0, NULL, &(ctx->user_data));
+ }
+ {
+ grn_obj *stack;
+ grn_obj *spaces;
+ unsigned int i, n_spaces;
+
+ stack = &(ctx->impl->temporary_open_spaces.stack);
+ spaces = (grn_obj *)GRN_BULK_HEAD(stack);
+ n_spaces = GRN_BULK_VSIZE(stack) / sizeof(grn_obj);
+ for (i = 0; i < n_spaces; i++) {
+ grn_obj *space = spaces + (n_spaces - i - 1);
+ GRN_OBJ_FIN(ctx, space);
+ }
+ GRN_OBJ_FIN(ctx, stack);
+ }
+ grn_ctx_impl_mrb_fin(ctx);
+ grn_ctx_loader_clear(ctx);
+ if (ctx->impl->parser) {
+ grn_expr_parser_close(ctx);
+ }
+ GRN_OBJ_FIN(ctx, &ctx->impl->current_request_id);
+ if (ctx->impl->values) {
+#ifndef USE_MEMORY_DEBUG
+ grn_db_obj *o;
+ GRN_ARRAY_EACH(ctx, ctx->impl->values, 0, 0, id, &o, {
+ grn_obj_close(ctx, *((grn_obj **)o));
+ });
+#endif
+ grn_array_close(ctx, ctx->impl->values);
+ }
+ if (ctx->impl->temporary_columns) {
+#ifndef USE_MEMORY_DEBUG
+ grn_obj *value;
+ GRN_PAT_EACH(ctx, ctx->impl->temporary_columns, id, NULL, NULL, &value, {
+ grn_obj_close(ctx, *((grn_obj **)value));
+ });
+#endif
+ grn_pat_close(ctx, ctx->impl->temporary_columns);
+ }
+ if (ctx->impl->ios) {
+ grn_hash_close(ctx, ctx->impl->ios);
+ }
+ if (ctx->impl->com) {
+ if (ctx->stat != GRN_CTX_QUIT) {
+ int flags;
+ char *str;
+ unsigned int str_len;
+ grn_ctx_send(ctx, "quit", 4, GRN_CTX_HEAD);
+ grn_ctx_recv(ctx, &str, &str_len, &flags);
+ }
+ grn_ctx_send(ctx, "ACK", 3, GRN_CTX_HEAD);
+ rc = grn_com_close(ctx, ctx->impl->com);
+ }
+ GRN_OBJ_FIN(ctx, &ctx->impl->query_log_buf);
+ GRN_OBJ_FIN(ctx, &ctx->impl->output.names);
+ GRN_OBJ_FIN(ctx, &ctx->impl->output.levels);
+ rc = grn_obj_close(ctx, ctx->impl->output.buf);
+ {
+ grn_hash **vp;
+ grn_obj *value;
+ GRN_HASH_EACH(ctx, ctx->impl->expr_vars, eid, NULL, NULL, &vp, {
+ if (*vp) {
+ GRN_HASH_EACH(ctx, *vp, id, NULL, NULL, &value, {
+ GRN_OBJ_FIN(ctx, value);
+ });
+ }
+ grn_hash_close(ctx, *vp);
+ });
+ }
+ grn_hash_close(ctx, ctx->impl->expr_vars);
+ if (ctx->impl->db && ctx->flags & GRN_CTX_PER_DB) {
+ grn_obj *db = ctx->impl->db;
+ ctx->impl->db = NULL;
+ grn_obj_close(ctx, db);
+ }
+ grn_alloc_fin_ctx_impl(ctx);
+ grn_alloc_info_dump(ctx);
+ grn_alloc_info_free(ctx);
+ CRITICAL_SECTION_FIN(ctx->impl->lock);
+ {
+ grn_io_mapinfo mi;
+ mi.map = (void *)ctx->impl;
+ grn_io_anon_unmap(ctx, &mi, IMPL_SIZE);
+ }
+ ctx->impl = NULL;
+ }
+ ctx->stat = GRN_CTX_FIN;
+ return rc;
+}
+
+grn_rc
+grn_ctx_set_finalizer(grn_ctx *ctx, grn_proc_func *finalizer)
+{
+ if (!ctx) { return GRN_INVALID_ARGUMENT; }
+ if (!ctx->impl) {
+ if (ERRP(ctx, GRN_ERROR)) { return ctx->rc; }
+ }
+ ctx->impl->finalizer = finalizer;
+ return GRN_SUCCESS;
+}
+
+grn_timeval grn_starttime;
+
+static void
+check_overcommit_memory(grn_ctx *ctx)
+{
+ FILE *file;
+ int value;
+ file = grn_fopen("/proc/sys/vm/overcommit_memory", "r");
+ if (!file) { return; }
+ value = fgetc(file);
+ if (value != '1') {
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "vm.overcommit_memory kernel parameter should be 1: <%c>: "
+ "See INFO level log to resolve this",
+ value);
+ GRN_LOG(ctx, GRN_LOG_INFO,
+ "Some processings with vm.overcommit_memory != 1 "
+ "may break DB under low memory condition.");
+ GRN_LOG(ctx, GRN_LOG_INFO,
+ "To set vm.overcommit_memory to 1");
+ GRN_LOG(ctx, GRN_LOG_INFO,
+ "add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and "
+ "restart your system or");
+ GRN_LOG(ctx, GRN_LOG_INFO,
+ "run 'sudo /sbin/sysctl vm.overcommit_memory=1' command.");
+ }
+ fclose(file);
+}
+
+grn_rc
+grn_init(void)
+{
+ grn_rc rc;
+ grn_ctx *ctx = &grn_gctx;
+ grn_init_from_env();
+ grn_init_external_libraries();
+ grn_alloc_info_init();
+ grn_logger_init();
+ grn_query_logger_init();
+ CRITICAL_SECTION_INIT(grn_glock);
+ grn_gtick = 0;
+ ctx->next = ctx;
+ ctx->prev = ctx;
+ rc = grn_ctx_init_internal(ctx, 0);
+ if (rc) {
+ goto fail_ctx_init_internal;
+ }
+ ctx->encoding = grn_encoding_parse(GRN_DEFAULT_ENCODING);
+ rc = grn_timeval_now(ctx, &grn_starttime);
+ if (rc) {
+ goto fail_start_time;
+ }
+#ifdef WIN32
+ {
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ grn_pagesize = si.dwAllocationGranularity;
+ }
+#else /* WIN32 */
+ if ((grn_pagesize = sysconf(_SC_PAGESIZE)) == -1) {
+ SERR("_SC_PAGESIZE");
+ rc = ctx->rc;
+ goto fail_page_size;
+ }
+#endif /* WIN32 */
+ if (grn_pagesize & (grn_pagesize - 1)) {
+ GRN_LOG(ctx, GRN_LOG_CRIT, "pagesize=%x", grn_pagesize);
+ }
+ // expand_stack();
+ if ((rc = grn_com_init())) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_com_init failed (%d)", rc);
+ goto fail_com;
+ }
+ if ((rc = grn_ctx_impl_init(ctx))) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_ctx_impl_init failed (%d)", rc);
+ goto fail_ctx_impl;
+ }
+ if ((rc = grn_plugins_init())) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_plugins_init failed (%d)", rc);
+ goto fail_plugins;
+ }
+ if ((rc = grn_normalizer_init())) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_normalizer_init failed (%d)", rc);
+ goto fail_normalizer;
+ }
+ if ((rc = grn_tokenizers_init())) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_tokenizers_init failed (%d)", rc);
+ goto fail_tokenizer;
+ }
+ grn_cache_init();
+ if (!grn_request_canceler_init()) {
+ rc = ctx->rc;
+ GRN_LOG(ctx, GRN_LOG_ALERT,
+ "failed to initialize request canceler (%d)", rc);
+ goto fail_request_canceler;
+ }
+ if (!grn_request_timer_init()) {
+ rc = ctx->rc;
+ GRN_LOG(ctx, GRN_LOG_ALERT,
+ "failed to initialize request timer (%d)", rc);
+ goto fail_request_timer;
+ }
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "grn_init: <%s>", grn_get_version());
+ check_overcommit_memory(ctx);
+ return rc;
+
+fail_request_timer:
+ grn_request_canceler_fin();
+fail_request_canceler:
+ grn_cache_fin();
+fail_tokenizer:
+ grn_normalizer_fin();
+fail_normalizer:
+ grn_plugins_fin();
+fail_plugins:
+ grn_ctx_fin(ctx);
+fail_ctx_impl:
+ grn_com_fin();
+fail_com:
+#ifndef WIN32
+fail_page_size:
+#endif /* WIN32 */
+fail_start_time:
+fail_ctx_init_internal:
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "grn_init: <%s>: failed", grn_get_version());
+ grn_query_logger_fin(ctx);
+ grn_logger_fin(ctx);
+ CRITICAL_SECTION_FIN(grn_glock);
+ grn_alloc_info_fin();
+ grn_fin_external_libraries();
+ return rc;
+}
+
+grn_encoding
+grn_get_default_encoding(void)
+{
+ return grn_gctx.encoding;
+}
+
+grn_rc
+grn_set_default_encoding(grn_encoding encoding)
+{
+ switch (encoding) {
+ case GRN_ENC_DEFAULT :
+ grn_gctx.encoding = grn_encoding_parse(GRN_DEFAULT_ENCODING);
+ return GRN_SUCCESS;
+ case GRN_ENC_NONE :
+ case GRN_ENC_EUC_JP :
+ case GRN_ENC_UTF8 :
+ case GRN_ENC_SJIS :
+ case GRN_ENC_LATIN1 :
+ case GRN_ENC_KOI8R :
+ grn_gctx.encoding = encoding;
+ return GRN_SUCCESS;
+ default :
+ return GRN_INVALID_ARGUMENT;
+ }
+}
+
+grn_command_version
+grn_get_default_command_version(void)
+{
+ return grn_ctx_get_command_version(&grn_gctx);
+}
+
+grn_rc
+grn_set_default_command_version(grn_command_version version)
+{
+ return grn_ctx_set_command_version(&grn_gctx, version);
+}
+
+long long int
+grn_get_default_match_escalation_threshold(void)
+{
+ return grn_ctx_get_match_escalation_threshold(&grn_gctx);
+}
+
+grn_rc
+grn_set_default_match_escalation_threshold(long long int threshold)
+{
+ return grn_ctx_set_match_escalation_threshold(&grn_gctx, threshold);
+}
+
+int
+grn_get_lock_timeout(void)
+{
+ return grn_lock_timeout;
+}
+
+grn_rc
+grn_set_lock_timeout(int timeout)
+{
+ grn_lock_timeout = timeout;
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_fin(void)
+{
+ grn_ctx *ctx, *ctx_;
+ if (grn_gctx.stat == GRN_CTX_FIN) { return GRN_INVALID_ARGUMENT; }
+ for (ctx = grn_gctx.next; ctx != &grn_gctx; ctx = ctx_) {
+ ctx_ = ctx->next;
+ if (ctx->stat != GRN_CTX_FIN) { grn_ctx_fin(ctx); }
+ if (ctx->flags & GRN_CTX_ALLOCATED) {
+ ctx->next->prev = ctx->prev;
+ ctx->prev->next = ctx->next;
+ GRN_GFREE(ctx);
+ }
+ }
+ grn_query_logger_fin(ctx);
+ grn_request_timer_fin();
+ grn_request_canceler_fin();
+ grn_cache_fin();
+ grn_tokenizers_fin();
+ grn_normalizer_fin();
+ grn_plugins_fin();
+ grn_ctx_fin(ctx);
+ grn_com_fin();
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "grn_fin (%d)", grn_alloc_count());
+ grn_logger_fin(ctx);
+ CRITICAL_SECTION_FIN(grn_glock);
+ grn_alloc_info_fin();
+ grn_fin_external_libraries();
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ctx_connect(grn_ctx *ctx, const char *host, int port, int flags)
+{
+ GRN_API_ENTER;
+ if (!ctx->impl) { goto exit; }
+ {
+ grn_com *com = grn_com_copen(ctx, NULL, host, port);
+ if (com) {
+ ctx->impl->com = com;
+ }
+ }
+exit :
+ GRN_API_RETURN(ctx->rc);
+}
+
+grn_rc
+grn_ctx_close(grn_ctx *ctx)
+{
+ grn_rc rc = grn_ctx_fin(ctx);
+ CRITICAL_SECTION_ENTER(grn_glock);
+ ctx->next->prev = ctx->prev;
+ ctx->prev->next = ctx->next;
+ CRITICAL_SECTION_LEAVE(grn_glock);
+ GRN_GFREE(ctx);
+ return rc;
+}
+
+grn_command_version
+grn_ctx_get_command_version(grn_ctx *ctx)
+{
+ if (ctx->impl) {
+ return ctx->impl->command.version;
+ } else {
+ return GRN_COMMAND_VERSION_STABLE;
+ }
+}
+
+grn_rc
+grn_ctx_set_command_version(grn_ctx *ctx, grn_command_version version)
+{
+ switch (version) {
+ case GRN_COMMAND_VERSION_DEFAULT :
+ ctx->impl->command.version = GRN_COMMAND_VERSION_STABLE;
+ return GRN_SUCCESS;
+ default :
+ if (GRN_COMMAND_VERSION_MIN <= version &&
+ version <= GRN_COMMAND_VERSION_MAX) {
+ ctx->impl->command.version = version;
+ return GRN_SUCCESS;
+ } else {
+ return GRN_UNSUPPORTED_COMMAND_VERSION;
+ }
+ }
+}
+
+grn_content_type
+grn_ctx_get_output_type(grn_ctx *ctx)
+{
+ if (ctx->impl) {
+ return ctx->impl->output.type;
+ } else {
+ return GRN_CONTENT_NONE;
+ }
+}
+
+grn_rc
+grn_ctx_set_output_type(grn_ctx *ctx, grn_content_type type)
+{
+ grn_rc rc = GRN_SUCCESS;
+
+ if (ctx->impl) {
+ ctx->impl->output.type = type;
+ switch (ctx->impl->output.type) {
+ case GRN_CONTENT_NONE :
+ ctx->impl->output.mime_type = "application/octet-stream";
+ break;
+ case GRN_CONTENT_TSV :
+ ctx->impl->output.mime_type = "text/tab-separated-values";
+ break;
+ case GRN_CONTENT_JSON :
+ ctx->impl->output.mime_type = "application/json";
+ break;
+ case GRN_CONTENT_XML :
+ ctx->impl->output.mime_type = "text/xml";
+ break;
+ case GRN_CONTENT_MSGPACK :
+ ctx->impl->output.mime_type = "application/x-msgpack";
+ break;
+ case GRN_CONTENT_GROONGA_COMMAND_LIST :
+ ctx->impl->output.mime_type = "text/x-groonga-command-list";
+ break;
+ }
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+
+ return rc;
+}
+
+const char *
+grn_ctx_get_mime_type(grn_ctx *ctx)
+{
+ if (ctx->impl) {
+ return ctx->impl->output.mime_type;
+ } else {
+ return NULL;
+ }
+}
+
+long long int
+grn_ctx_get_match_escalation_threshold(grn_ctx *ctx)
+{
+ if (ctx->impl) {
+ return ctx->impl->match_escalation_threshold;
+ } else {
+ return GRN_DEFAULT_MATCH_ESCALATION_THRESHOLD;
+ }
+}
+
+grn_rc
+grn_ctx_set_match_escalation_threshold(grn_ctx *ctx, long long int threshold)
+{
+ ctx->impl->match_escalation_threshold = threshold;
+ return GRN_SUCCESS;
+}
+
+grn_content_type
+grn_get_ctype(grn_obj *var)
+{
+ return grn_content_type_parse(NULL, var, GRN_CONTENT_JSON);
+}
+
+grn_content_type
+grn_content_type_parse(grn_ctx *ctx,
+ grn_obj *var,
+ grn_content_type default_value)
+{
+ grn_content_type ct = default_value;
+ if (var->header.domain == GRN_DB_INT32) {
+ ct = GRN_INT32_VALUE(var);
+ } else if (GRN_TEXT_LEN(var)) {
+ switch (*(GRN_TEXT_VALUE(var))) {
+ case 't' :
+ case 'T' :
+ ct = GRN_CONTENT_TSV;
+ break;
+ case 'j' :
+ case 'J' :
+ ct = GRN_CONTENT_JSON;
+ break;
+ case 'x' :
+ case 'X' :
+ ct = GRN_CONTENT_XML;
+ break;
+ }
+ }
+ return ct;
+}
+
+static void
+get_content_mime_type(grn_ctx *ctx, const char *p, const char *pe)
+{
+ ctx->impl->output.type = GRN_CONTENT_NONE;
+ ctx->impl->output.mime_type = "application/octet-stream";
+
+ if (p + 2 <= pe) {
+ switch (*p) {
+ case 'c' :
+ if (p + 3 == pe && !memcmp(p, "css", 3)) {
+ ctx->impl->output.type = GRN_CONTENT_NONE;
+ ctx->impl->output.mime_type = "text/css";
+ }
+ break;
+ case 'g' :
+ if (p + 3 == pe && !memcmp(p, "gif", 3)) {
+ ctx->impl->output.type = GRN_CONTENT_NONE;
+ ctx->impl->output.mime_type = "image/gif";
+ }
+ break;
+ case 'h' :
+ if (p + 4 == pe && !memcmp(p, "html", 4)) {
+ ctx->impl->output.type = GRN_CONTENT_NONE;
+ ctx->impl->output.mime_type = "text/html";
+ }
+ break;
+ case 'j' :
+ if (!memcmp(p, "js", 2)) {
+ if (p + 2 == pe) {
+ ctx->impl->output.type = GRN_CONTENT_NONE;
+ ctx->impl->output.mime_type = "text/javascript";
+ } else if (p + 4 == pe && !memcmp(p + 2, "on", 2)) {
+ ctx->impl->output.type = GRN_CONTENT_JSON;
+ ctx->impl->output.mime_type = "application/json";
+ }
+ } else if (p + 3 == pe && !memcmp(p, "jpg", 3)) {
+ ctx->impl->output.type = GRN_CONTENT_NONE;
+ ctx->impl->output.mime_type = "image/jpeg";
+ }
+ break;
+#ifdef GRN_WITH_MESSAGE_PACK
+ case 'm' :
+ if (p + 7 == pe && !memcmp(p, "msgpack", 7)) {
+ ctx->impl->output.type = GRN_CONTENT_MSGPACK;
+ ctx->impl->output.mime_type = "application/x-msgpack";
+ }
+ break;
+#endif
+ case 'p' :
+ if (p + 3 == pe && !memcmp(p, "png", 3)) {
+ ctx->impl->output.type = GRN_CONTENT_NONE;
+ ctx->impl->output.mime_type = "image/png";
+ }
+ break;
+ case 't' :
+ if (p + 3 == pe && !memcmp(p, "txt", 3)) {
+ ctx->impl->output.type = GRN_CONTENT_NONE;
+ ctx->impl->output.mime_type = "text/plain";
+ } else if (p + 3 == pe && !memcmp(p, "tsv", 3)) {
+ ctx->impl->output.type = GRN_CONTENT_TSV;
+ ctx->impl->output.mime_type = "text/tab-separated-values";
+ }
+ break;
+ case 'x':
+ if (p + 3 == pe && !memcmp(p, "xml", 3)) {
+ ctx->impl->output.type = GRN_CONTENT_XML;
+ ctx->impl->output.mime_type = "text/xml";
+ }
+ break;
+ }
+ }
+}
+
+static void
+grn_str_get_mime_type(grn_ctx *ctx, const char *p, const char *pe,
+ const char **key_end, const char **filename_end)
+{
+ const char *pd = NULL;
+ for (; p < pe && *p != '?' && *p != '#'; p++) {
+ if (*p == '.') { pd = p; }
+ }
+ *filename_end = p;
+ if (pd && pd < p) {
+ get_content_mime_type(ctx, pd + 1, p);
+ *key_end = pd;
+ } else {
+ *key_end = pe;
+ }
+}
+
+static void
+get_command_version(grn_ctx *ctx, const char *p, const char *pe)
+{
+ grn_command_version version;
+ const char *rest;
+
+ version = grn_atoui(p, pe, &rest);
+ if (pe == rest) {
+ grn_rc rc;
+ rc = grn_ctx_set_command_version(ctx, version);
+ if (rc == GRN_UNSUPPORTED_COMMAND_VERSION) {
+ ERR(rc,
+ "unsupported command version is specified: %d: "
+ "stable command version: %d: "
+ "available command versions: %d-%d",
+ version,
+ GRN_COMMAND_VERSION_STABLE,
+ GRN_COMMAND_VERSION_MIN, GRN_COMMAND_VERSION_MAX);
+ }
+ }
+}
+
+#define INDEX_HTML "index.html"
+#define OUTPUT_TYPE "output_type"
+#define COMMAND_VERSION "command_version"
+#define REQUEST_ID "request_id"
+#define REQUEST_TIMEOUT "request_timeout"
+#define OUTPUT_PRETTY "output_pretty"
+#define EXPR_MISSING "expr_missing"
+#define OUTPUT_TYPE_LEN (sizeof(OUTPUT_TYPE) - 1)
+#define COMMAND_VERSION_LEN (sizeof(COMMAND_VERSION) - 1)
+#define REQUEST_ID_LEN (sizeof(REQUEST_ID) - 1)
+#define REQUEST_TIMEOUT_LEN (sizeof(REQUEST_TIMEOUT) - 1)
+#define OUTPUT_PRETTY_LEN (sizeof(OUTPUT_PRETTY) - 1)
+
+#define HTTP_QUERY_PAIR_DELIMITER "="
+#define HTTP_QUERY_PAIRS_DELIMITERS "&;"
+
+static inline int
+command_proc_p(grn_obj *expr)
+{
+ return (expr->header.type == GRN_PROC &&
+ ((grn_proc *)expr)->type == GRN_PROC_COMMAND);
+}
+
+grn_obj *
+grn_ctx_qe_exec_uri(grn_ctx *ctx, const char *path, uint32_t path_len)
+{
+ grn_obj buf, *expr, *val;
+ grn_obj request_id;
+ double request_timeout;
+ const char *p = path, *e = path + path_len, *v, *key_end, *filename_end;
+
+ request_timeout = grn_get_default_request_timeout();
+
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_INIT(&request_id, 0);
+ p = grn_text_urldec(ctx, &buf, p, e, '?');
+ if (!GRN_TEXT_LEN(&buf)) { GRN_TEXT_SETS(ctx, &buf, INDEX_HTML); }
+ v = GRN_TEXT_VALUE(&buf);
+ grn_str_get_mime_type(ctx, v, GRN_BULK_CURR(&buf), &key_end, &filename_end);
+ if ((GRN_TEXT_LEN(&buf) >= 2 && v[0] == 'd' && v[1] == '/')) {
+ const char *command_name = v + 2;
+ int command_name_size = key_end - command_name;
+ expr = grn_ctx_get(ctx, command_name, command_name_size);
+ if (expr && command_proc_p(expr)) {
+ while (p < e) {
+ int l;
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_cgidec(ctx, &buf, p, e, HTTP_QUERY_PAIR_DELIMITER);
+ v = GRN_TEXT_VALUE(&buf);
+ l = GRN_TEXT_LEN(&buf);
+ if (l == OUTPUT_TYPE_LEN && !memcmp(v, OUTPUT_TYPE, OUTPUT_TYPE_LEN)) {
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_cgidec(ctx, &buf, p, e, HTTP_QUERY_PAIRS_DELIMITERS);
+ v = GRN_TEXT_VALUE(&buf);
+ get_content_mime_type(ctx, v, GRN_BULK_CURR(&buf));
+ } else if (l == COMMAND_VERSION_LEN &&
+ !memcmp(v, COMMAND_VERSION, COMMAND_VERSION_LEN)) {
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_cgidec(ctx, &buf, p, e, HTTP_QUERY_PAIRS_DELIMITERS);
+ get_command_version(ctx, GRN_TEXT_VALUE(&buf), GRN_BULK_CURR(&buf));
+ if (ctx->rc) { goto exit; }
+ } else if (l == REQUEST_ID_LEN &&
+ !memcmp(v, REQUEST_ID, REQUEST_ID_LEN)) {
+ GRN_BULK_REWIND(&request_id);
+ p = grn_text_cgidec(ctx, &request_id, p, e,
+ HTTP_QUERY_PAIRS_DELIMITERS);
+ } else if (l == REQUEST_TIMEOUT_LEN &&
+ !memcmp(v, REQUEST_TIMEOUT, REQUEST_TIMEOUT_LEN)) {
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_cgidec(ctx, &buf, p, e, HTTP_QUERY_PAIRS_DELIMITERS);
+ GRN_TEXT_PUTC(ctx, &buf, '\0');
+ request_timeout = strtod(GRN_TEXT_VALUE(&buf), NULL);
+ } else if (l == OUTPUT_PRETTY_LEN &&
+ !memcmp(v, OUTPUT_PRETTY, OUTPUT_PRETTY_LEN)) {
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_cgidec(ctx, &buf, p, e, HTTP_QUERY_PAIRS_DELIMITERS);
+ if (GRN_TEXT_LEN(&buf) == strlen("yes") &&
+ !memcmp(GRN_TEXT_VALUE(&buf), "yes", GRN_TEXT_LEN(&buf))) {
+ ctx->impl->output.is_pretty = GRN_TRUE;
+ } else {
+ ctx->impl->output.is_pretty = GRN_FALSE;
+ }
+ } else {
+ if (!(val = grn_expr_get_or_add_var(ctx, expr, v, l))) {
+ val = &buf;
+ }
+ grn_obj_reinit(ctx, val, GRN_DB_TEXT, 0);
+ p = grn_text_cgidec(ctx, val, p, e, HTTP_QUERY_PAIRS_DELIMITERS);
+ }
+ }
+ if (request_timeout > 0 && GRN_TEXT_LEN(&request_id) == 0) {
+ grn_text_printf(ctx, &request_id, "%p", ctx);
+ }
+ if (GRN_TEXT_LEN(&request_id) > 0) {
+ GRN_TEXT_SET(ctx, &ctx->impl->current_request_id,
+ GRN_TEXT_VALUE(&request_id),
+ GRN_TEXT_LEN(&request_id));
+ grn_request_canceler_register(ctx,
+ GRN_TEXT_VALUE(&request_id),
+ GRN_TEXT_LEN(&request_id));
+ if (request_timeout > 0.0) {
+ ctx->impl->current_request_timer_id =
+ grn_request_timer_register(GRN_TEXT_VALUE(&request_id),
+ GRN_TEXT_LEN(&request_id),
+ request_timeout);
+ }
+ }
+ ctx->impl->curr_expr = expr;
+ grn_expr_exec(ctx, expr, 0);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "invalid command name: %.*s",
+ command_name_size, command_name);
+ }
+ } else if ((expr = grn_ctx_get(ctx, GRN_EXPR_MISSING_NAME,
+ strlen(GRN_EXPR_MISSING_NAME)))) {
+ if ((val = grn_expr_get_var_by_offset(ctx, expr, 0))) {
+ grn_obj_reinit(ctx, val, GRN_DB_TEXT, 0);
+ GRN_TEXT_SET(ctx, val, v, filename_end - v);
+ }
+ ctx->impl->curr_expr = expr;
+ grn_expr_exec(ctx, expr, 0);
+ }
+exit :
+ GRN_OBJ_FIN(ctx, &request_id);
+ GRN_OBJ_FIN(ctx, &buf);
+
+ return expr;
+}
+
+grn_obj *
+grn_ctx_qe_exec(grn_ctx *ctx, const char *str, uint32_t str_len)
+{
+ char tok_type;
+ int offset = 0;
+ grn_obj buf, *expr = NULL, *val = NULL;
+ grn_obj request_id;
+ double request_timeout;
+ const char *p = str, *e = str + str_len, *v;
+
+ request_timeout = grn_get_default_request_timeout();
+
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_INIT(&request_id, 0);
+ p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
+ expr = grn_ctx_get(ctx, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ while (p < e) {
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
+ v = GRN_TEXT_VALUE(&buf);
+ switch (tok_type) {
+ case GRN_TOK_VOID :
+ p = e;
+ break;
+ case GRN_TOK_SYMBOL :
+ if (GRN_TEXT_LEN(&buf) > 2 && v[0] == '-' && v[1] == '-') {
+ int l = GRN_TEXT_LEN(&buf) - 2;
+ v += 2;
+ if (l == OUTPUT_TYPE_LEN && !memcmp(v, OUTPUT_TYPE, OUTPUT_TYPE_LEN)) {
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
+ v = GRN_TEXT_VALUE(&buf);
+ get_content_mime_type(ctx, v, GRN_BULK_CURR(&buf));
+ } else if (l == COMMAND_VERSION_LEN &&
+ !memcmp(v, COMMAND_VERSION, COMMAND_VERSION_LEN)) {
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
+ get_command_version(ctx, GRN_TEXT_VALUE(&buf), GRN_BULK_CURR(&buf));
+ if (ctx->rc) { goto exit; }
+ } else if (l == REQUEST_ID_LEN &&
+ !memcmp(v, REQUEST_ID, REQUEST_ID_LEN)) {
+ GRN_BULK_REWIND(&request_id);
+ p = grn_text_unesc_tok(ctx, &request_id, p, e, &tok_type);
+ } else if (l == REQUEST_TIMEOUT_LEN &&
+ !memcmp(v, REQUEST_TIMEOUT, REQUEST_TIMEOUT_LEN)) {
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
+ GRN_TEXT_PUTC(ctx, &buf, '\0');
+ request_timeout = strtod(GRN_TEXT_VALUE(&buf), NULL);
+ } else if (l == OUTPUT_PRETTY_LEN &&
+ !memcmp(v, OUTPUT_PRETTY, OUTPUT_PRETTY_LEN)) {
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
+ if (GRN_TEXT_LEN(&buf) == strlen("yes") &&
+ !memcmp(GRN_TEXT_VALUE(&buf), "yes", GRN_TEXT_LEN(&buf))) {
+ ctx->impl->output.is_pretty = GRN_TRUE;
+ } else {
+ ctx->impl->output.is_pretty = GRN_FALSE;
+ }
+ } else if (expr && (val = grn_expr_get_or_add_var(ctx, expr, v, l))) {
+ grn_obj_reinit(ctx, val, GRN_DB_TEXT, 0);
+ p = grn_text_unesc_tok(ctx, val, p, e, &tok_type);
+ } else {
+ p = e;
+ }
+ break;
+ }
+ // fallthru
+ case GRN_TOK_STRING :
+ case GRN_TOK_QUOTE :
+ if (expr && (val = grn_expr_get_var_by_offset(ctx, expr, offset++))) {
+ grn_obj_reinit(ctx, val, GRN_DB_TEXT, 0);
+ GRN_TEXT_PUT(ctx, val, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ } else {
+ p = e;
+ }
+ break;
+ }
+ }
+ if (request_timeout > 0 && GRN_TEXT_LEN(&request_id) == 0) {
+ grn_text_printf(ctx, &request_id, "%p", ctx);
+ }
+ if (GRN_TEXT_LEN(&request_id) > 0) {
+ GRN_TEXT_SET(ctx, &ctx->impl->current_request_id,
+ GRN_TEXT_VALUE(&request_id),
+ GRN_TEXT_LEN(&request_id));
+ grn_request_canceler_register(ctx,
+ GRN_TEXT_VALUE(&request_id),
+ GRN_TEXT_LEN(&request_id));
+ if (request_timeout > 0.0) {
+ ctx->impl->current_request_timer_id =
+ grn_request_timer_register(GRN_TEXT_VALUE(&request_id),
+ GRN_TEXT_LEN(&request_id),
+ request_timeout);
+ }
+ }
+ ctx->impl->curr_expr = expr;
+ if (expr && command_proc_p(expr)) {
+ grn_expr_exec(ctx, expr, 0);
+ } else {
+ GRN_BULK_REWIND(&buf);
+ grn_text_unesc_tok(ctx, &buf, str, str + str_len, &tok_type);
+ if (GRN_TEXT_LEN(&buf)) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid command name: %.*s",
+ (int)GRN_TEXT_LEN(&buf), GRN_TEXT_VALUE(&buf));
+ }
+ }
+exit :
+ GRN_OBJ_FIN(ctx, &request_id);
+ GRN_OBJ_FIN(ctx, &buf);
+
+ return expr;
+}
+
+grn_rc
+grn_ctx_sendv(grn_ctx *ctx, int argc, char **argv, int flags)
+{
+ grn_obj buf;
+ GRN_API_ENTER;
+ GRN_TEXT_INIT(&buf, 0);
+ while (argc--) {
+ // todo : encode into json like syntax
+ GRN_TEXT_PUTS(ctx, &buf, *argv);
+ argv++;
+ if (argc) { GRN_TEXT_PUTC(ctx, &buf, ' '); }
+ }
+ grn_ctx_send(ctx, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf), flags);
+ GRN_OBJ_FIN(ctx, &buf);
+ GRN_API_RETURN(ctx->rc);
+}
+
+static int
+comment_command_p(const char *command, unsigned int length)
+{
+ const char *p, *e;
+
+ e = command + length;
+ for (p = command; p < e; p++) {
+ switch (*p) {
+ case '#' :
+ return GRN_TRUE;
+ case ' ' :
+ case '\t' :
+ break;
+ default :
+ return GRN_FALSE;
+ }
+ }
+ return GRN_FALSE;
+}
+
+unsigned int
+grn_ctx_send(grn_ctx *ctx, const char *str, unsigned int str_len, int flags)
+{
+ if (!ctx) { return 0; }
+ GRN_API_ENTER;
+ if (ctx->impl) {
+ if ((flags & GRN_CTX_MORE)) { flags |= GRN_CTX_QUIET; }
+ if (ctx->stat == GRN_CTX_QUIT) { flags |= GRN_CTX_QUIT; }
+
+ ctx->impl->command.flags = flags;
+ if (ctx->impl->com) {
+ grn_rc rc;
+ grn_com_header sheader;
+ grn_timeval_now(ctx, &ctx->impl->tv);
+ sheader.proto = GRN_COM_PROTO_GQTP;
+ sheader.qtype = 0;
+ sheader.keylen = 0;
+ sheader.level = 0;
+ sheader.flags = flags;
+ sheader.status = 0;
+ sheader.opaque = 0;
+ sheader.cas = 0;
+ if ((rc = grn_com_send(ctx, ctx->impl->com, &sheader, (char *)str, str_len, 0))) {
+ ERR(rc, "grn_com_send failed");
+ }
+ goto exit;
+ } else {
+ grn_command_version command_version;
+ grn_obj *expr = NULL;
+
+ command_version = grn_ctx_get_command_version(ctx);
+ if (ctx->impl->command.keep.command) {
+ grn_obj *val;
+ expr = ctx->impl->command.keep.command;
+ ctx->impl->command.keep.command = NULL;
+ grn_ctx_set_command_version(ctx, ctx->impl->command.keep.version);
+ if ((val = grn_expr_get_var_by_offset(ctx, expr, 0))) {
+ grn_obj_reinit(ctx, val, GRN_DB_TEXT, 0);
+ GRN_TEXT_PUT(ctx, val, str, str_len);
+ }
+ grn_expr_exec(ctx, expr, 0);
+ } else {
+ if (comment_command_p(str, str_len)) { goto output; };
+ GRN_BULK_REWIND(ctx->impl->output.buf);
+ ctx->impl->output.type = GRN_CONTENT_JSON;
+ ctx->impl->output.mime_type = "application/json";
+ ctx->impl->output.is_pretty = GRN_FALSE;
+ grn_timeval_now(ctx, &ctx->impl->tv);
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_COMMAND,
+ ">", "%.*s", str_len, str);
+ if (str_len && *str == '/') {
+ expr = grn_ctx_qe_exec_uri(ctx, str + 1, str_len - 1);
+ } else {
+ expr = grn_ctx_qe_exec(ctx, str, str_len);
+ }
+ }
+ if (ctx->stat == GRN_CTX_QUITTING) { ctx->stat = GRN_CTX_QUIT; }
+ if (ctx->impl->command.keep.command) {
+ ERRCLR(ctx);
+ } else {
+ if (ctx->impl->current_request_timer_id) {
+ void *timer_id = ctx->impl->current_request_timer_id;
+ ctx->impl->current_request_timer_id = NULL;
+ grn_request_timer_unregister(timer_id);
+ }
+ if (GRN_TEXT_LEN(&ctx->impl->current_request_id) > 0) {
+ grn_obj *request_id = &ctx->impl->current_request_id;
+ grn_request_canceler_unregister(ctx,
+ GRN_TEXT_VALUE(request_id),
+ GRN_TEXT_LEN(request_id));
+ GRN_BULK_REWIND(&ctx->impl->current_request_id);
+ }
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_RESULT_CODE,
+ "<", "rc=%d", ctx->rc);
+ }
+ output :
+ if (!(ctx->impl->command.flags & GRN_CTX_QUIET) &&
+ ctx->impl->output.func) {
+ ctx->impl->output.func(ctx, GRN_CTX_TAIL, ctx->impl->output.data.ptr);
+ }
+ if (expr) { grn_expr_clear_vars(ctx, expr); }
+ grn_ctx_set_command_version(ctx, command_version);
+ goto exit;
+ }
+ }
+ ERR(GRN_INVALID_ARGUMENT, "invalid ctx assigned");
+exit :
+ GRN_API_RETURN(0);
+}
+
+unsigned int
+grn_ctx_recv(grn_ctx *ctx, char **str, unsigned int *str_len, int *flags)
+{
+ if (!ctx) { return GRN_INVALID_ARGUMENT; }
+
+ *flags = 0;
+
+ if (ctx->stat == GRN_CTX_QUIT) {
+ grn_bool have_buffer = GRN_FALSE;
+
+ if (ctx->impl &&
+ !ctx->impl->com &&
+ GRN_TEXT_LEN(ctx->impl->output.buf) > 0) {
+ have_buffer = GRN_TRUE;
+ }
+
+ *flags |= GRN_CTX_QUIT;
+ if (!have_buffer) {
+ *str = NULL;
+ *str_len = 0;
+ return 0;
+ }
+ }
+
+ GRN_API_ENTER;
+ if (ctx->impl) {
+ if (ctx->impl->com) {
+ grn_com_header header;
+ if (grn_com_recv(ctx, ctx->impl->com, &header, ctx->impl->output.buf)) {
+ *str = NULL;
+ *str_len = 0;
+ *flags = 0;
+ } else {
+ *str = GRN_BULK_HEAD(ctx->impl->output.buf);
+ *str_len = GRN_BULK_VSIZE(ctx->impl->output.buf);
+ if (header.flags & GRN_CTX_QUIT) {
+ ctx->stat = GRN_CTX_QUIT;
+ *flags |= GRN_CTX_QUIT;
+ } else {
+ if (!(header.flags & GRN_CTX_TAIL)) {
+ *flags |= GRN_CTX_MORE;
+ }
+ }
+ ctx->impl->output.type = header.qtype;
+ ctx->rc = (int16_t)ntohs(header.status);
+ ctx->errbuf[0] = '\0';
+ ctx->errline = 0;
+ ctx->errfile = NULL;
+ ctx->errfunc = NULL;
+ }
+ goto exit;
+ } else {
+ grn_obj *buf = ctx->impl->output.buf;
+ unsigned int head = 0, tail = GRN_BULK_VSIZE(buf);
+ *str = GRN_BULK_HEAD(buf) + head;
+ *str_len = tail - head;
+ GRN_BULK_REWIND(ctx->impl->output.buf);
+ goto exit;
+ }
+ }
+ ERR(GRN_INVALID_ARGUMENT, "invalid ctx assigned");
+exit :
+ GRN_API_RETURN(0);
+}
+
+void
+grn_ctx_stream_out_func(grn_ctx *ctx, int flags, void *stream)
+{
+ if (ctx && ctx->impl) {
+ grn_obj *buf = ctx->impl->output.buf;
+ uint32_t size = GRN_BULK_VSIZE(buf);
+ if (size) {
+ if (fwrite(GRN_BULK_HEAD(buf), 1, size, (FILE *)stream)) {
+ fputc('\n', (FILE *)stream);
+ fflush((FILE *)stream);
+ }
+ GRN_BULK_REWIND(buf);
+ }
+ }
+}
+
+void
+grn_ctx_recv_handler_set(grn_ctx *ctx, void (*func)(grn_ctx *, int, void *), void *func_arg)
+{
+ if (ctx && ctx->impl) {
+ ctx->impl->output.func = func;
+ ctx->impl->output.data.ptr = func_arg;
+ }
+}
+
+grn_rc
+grn_ctx_info_get(grn_ctx *ctx, grn_ctx_info *info)
+{
+ if (!ctx || !ctx->impl) { return GRN_INVALID_ARGUMENT; }
+ if (ctx->impl->com) {
+ info->fd = ctx->impl->com->fd;
+ info->com_status = ctx->impl->com_status;
+ info->outbuf = ctx->impl->output.buf;
+ info->stat = ctx->stat;
+ } else {
+ info->fd = -1;
+ info->com_status = 0;
+ info->outbuf = ctx->impl->output.buf;
+ info->stat = ctx->stat;
+ }
+ return GRN_SUCCESS;
+}
+
+#define DB_P(s) ((s) && (s)->header.type == GRN_DB)
+
+grn_rc
+grn_ctx_use(grn_ctx *ctx, grn_obj *db)
+{
+ GRN_API_ENTER;
+ if (db && !DB_P(db)) {
+ ctx->rc = GRN_INVALID_ARGUMENT;
+ } else {
+ if (!ctx->rc) {
+ ctx->impl->db = db;
+ if (db) {
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ grn_obj_get_info(ctx, db, GRN_INFO_ENCODING, &buf);
+ ctx->encoding = *(grn_encoding *)GRN_BULK_HEAD(&buf);
+ grn_obj_close(ctx, &buf);
+ }
+ }
+ }
+ GRN_API_RETURN(ctx->rc);
+}
+
+/* don't handle error inside logger functions */
+
+void
+grn_ctx_log(grn_ctx *ctx, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ grn_ctx_logv(ctx, fmt, ap);
+ va_end(ap);
+}
+
+void
+grn_ctx_logv(grn_ctx *ctx, const char *fmt, va_list ap)
+{
+ char buffer[GRN_CTX_MSGSIZE];
+ grn_vsnprintf(buffer, GRN_CTX_MSGSIZE, fmt, ap);
+ grn_strcpy(ctx->errbuf, GRN_CTX_MSGSIZE, buffer);
+}
+
+void
+grn_assert(grn_ctx *ctx, int cond, const char* file, int line, const char* func)
+{
+ if (!cond) {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "ASSERT fail on %s %s:%d", func, file, line);
+ }
+}
+
+const char *
+grn_get_version(void)
+{
+ return GRN_VERSION;
+}
+
+const char *
+grn_get_package(void)
+{
+ return PACKAGE;
+}
+
+const char *
+grn_get_package_label(void)
+{
+ return PACKAGE_LABEL;
+}
+
+#if defined(HAVE_SIGNAL_H) && !defined(WIN32)
+static int segv_received = 0;
+static void
+segv_handler(int signal_number, siginfo_t *info, void *context)
+{
+ grn_ctx *ctx = &grn_gctx;
+
+ if (segv_received) {
+ GRN_LOG(ctx, GRN_LOG_CRIT, "SEGV received in SEGV handler.");
+ exit(EXIT_FAILURE);
+ }
+ segv_received = 1;
+
+ GRN_LOG(ctx, GRN_LOG_CRIT, "-- CRASHED!!! --");
+#ifdef HAVE_BACKTRACE
+# define N_TRACE_LEVEL 1024
+ {
+ static void *trace[N_TRACE_LEVEL];
+ int n = backtrace(trace, N_TRACE_LEVEL);
+ char **symbols = backtrace_symbols(trace, n);
+ int i;
+
+ if (symbols) {
+ for (i = 0; i < n; i++) {
+ GRN_LOG(ctx, GRN_LOG_CRIT, "%s", symbols[i]);
+ }
+ free(symbols);
+ }
+ }
+#else /* HAVE_BACKTRACE */
+ GRN_LOG(ctx, GRN_LOG_CRIT, "backtrace() isn't available.");
+#endif /* HAVE_BACKTRACE */
+ GRN_LOG(ctx, GRN_LOG_CRIT, "----------------");
+ abort();
+}
+#endif /* defined(HAVE_SIGNAL_H) && !defined(WIN32) */
+
+grn_rc
+grn_set_segv_handler(void)
+{
+ grn_rc rc = GRN_SUCCESS;
+#if defined(HAVE_SIGNAL_H) && !defined(WIN32)
+ grn_ctx *ctx = &grn_gctx;
+ struct sigaction action;
+
+ sigemptyset(&action.sa_mask);
+ action.sa_sigaction = segv_handler;
+ action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+
+ if (sigaction(SIGSEGV, &action, NULL)) {
+ SERR("failed to set SIGSEGV action");
+ rc = ctx->rc;
+ };
+#endif
+ return rc;
+}
+
+#if defined(HAVE_SIGNAL_H) && !defined(WIN32)
+static struct sigaction old_int_handler;
+static void
+int_handler(int signal_number, siginfo_t *info, void *context)
+{
+ grn_gctx.stat = GRN_CTX_QUIT;
+ sigaction(signal_number, &old_int_handler, NULL);
+}
+
+static struct sigaction old_term_handler;
+static void
+term_handler(int signal_number, siginfo_t *info, void *context)
+{
+ grn_gctx.stat = GRN_CTX_QUIT;
+ sigaction(signal_number, &old_term_handler, NULL);
+}
+#endif /* defined(HAVE_SIGNAL_H) && !defined(WIN32) */
+
+grn_rc
+grn_set_int_handler(void)
+{
+ grn_rc rc = GRN_SUCCESS;
+#if defined(HAVE_SIGNAL_H) && !defined(WIN32)
+ grn_ctx *ctx = &grn_gctx;
+ struct sigaction action;
+
+ sigemptyset(&action.sa_mask);
+ action.sa_sigaction = int_handler;
+ action.sa_flags = SA_SIGINFO;
+
+ if (sigaction(SIGINT, &action, &old_int_handler)) {
+ SERR("failed to set SIGINT action");
+ rc = ctx->rc;
+ }
+#endif
+ return rc;
+}
+
+grn_rc
+grn_set_term_handler(void)
+{
+ grn_rc rc = GRN_SUCCESS;
+#if defined(HAVE_SIGNAL_H) && !defined(WIN32)
+ grn_ctx *ctx = &grn_gctx;
+ struct sigaction action;
+
+ sigemptyset(&action.sa_mask);
+ action.sa_sigaction = term_handler;
+ action.sa_flags = SA_SIGINFO;
+
+ if (sigaction(SIGTERM, &action, &old_term_handler)) {
+ SERR("failed to set SIGTERM action");
+ rc = ctx->rc;
+ }
+#endif
+ return rc;
+}
+
+void
+grn_ctx_output_flush(grn_ctx *ctx, int flags)
+{
+ if (flags & GRN_CTX_QUIET) {
+ return;
+ }
+ if (!ctx->impl->output.func) {
+ return;
+ }
+ ctx->impl->output.func(ctx, 0, ctx->impl->output.data.ptr);
+}
+
+void
+grn_ctx_output_array_open(grn_ctx *ctx, const char *name, int nelements)
+{
+ grn_output_array_open(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type,
+ name, nelements);
+}
+
+void
+grn_ctx_output_array_close(grn_ctx *ctx)
+{
+ grn_output_array_close(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type);
+}
+
+void
+grn_ctx_output_map_open(grn_ctx *ctx, const char *name, int nelements)
+{
+ grn_output_map_open(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type,
+ name, nelements);
+}
+
+void
+grn_ctx_output_map_close(grn_ctx *ctx)
+{
+ grn_output_map_close(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type);
+}
+
+void
+grn_ctx_output_null(grn_ctx *ctx)
+{
+ grn_output_null(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type);
+}
+
+void
+grn_ctx_output_int32(grn_ctx *ctx, int value)
+{
+ grn_output_int32(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type,
+ value);
+}
+
+void
+grn_ctx_output_int64(grn_ctx *ctx, int64_t value)
+{
+ grn_output_int64(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type,
+ value);
+}
+
+void
+grn_ctx_output_uint64(grn_ctx *ctx, uint64_t value)
+{
+ grn_output_uint64(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type,
+ value);
+}
+
+void
+grn_ctx_output_float(grn_ctx *ctx, double value)
+{
+ grn_output_float(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type,
+ value);
+}
+
+void
+grn_ctx_output_cstr(grn_ctx *ctx, const char *value)
+{
+ grn_output_cstr(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type,
+ value);
+}
+
+void
+grn_ctx_output_str(grn_ctx *ctx, const char *value, unsigned int value_len)
+{
+ grn_output_str(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type,
+ value, value_len);
+}
+
+void
+grn_ctx_output_bool(grn_ctx *ctx, grn_bool value)
+{
+ grn_output_bool(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type,
+ value);
+}
+
+void
+grn_ctx_output_obj(grn_ctx *ctx, grn_obj *value, grn_obj_format *format)
+{
+ grn_output_obj(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type,
+ value, format);
+}
+
+void
+grn_ctx_output_result_set_open(grn_ctx *ctx,
+ grn_obj *result_set,
+ grn_obj_format *format,
+ uint32_t n_additional_elements)
+{
+ grn_output_result_set_open(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type,
+ result_set,
+ format,
+ n_additional_elements);
+}
+
+void
+grn_ctx_output_result_set_close(grn_ctx *ctx,
+ grn_obj *result_set,
+ grn_obj_format *format)
+{
+ grn_output_result_set_close(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type,
+ result_set,
+ format);
+}
+
+void
+grn_ctx_output_result_set(grn_ctx *ctx,
+ grn_obj *result_set,
+ grn_obj_format *format)
+{
+ grn_output_result_set(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type,
+ result_set,
+ format);
+}
+
+void
+grn_ctx_output_table_columns(grn_ctx *ctx, grn_obj *table,
+ grn_obj_format *format)
+{
+ grn_output_table_columns(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type,
+ table,
+ format);
+}
+
+void
+grn_ctx_output_table_records(grn_ctx *ctx, grn_obj *table,
+ grn_obj_format *format)
+{
+ grn_output_table_records(ctx,
+ ctx->impl->output.buf,
+ ctx->impl->output.type,
+ table,
+ format);
+}