1/* -*- c-basic-offset: 2 -*- */
2/*
3 Copyright(C) 2009-2017 Brazil
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License version 2.1 as published by the Free Software Foundation.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17*/
18
19#include "grn.h"
20#include <string.h>
21#include "grn_request_canceler.h"
22#include "grn_request_timer.h"
23#include "grn_tokenizers.h"
24#include "grn_ctx_impl.h"
25#include "grn_ii.h"
26#include "grn_pat.h"
27#include "grn_index_column.h"
28#include "grn_proc.h"
29#include "grn_plugin.h"
30#include "grn_snip.h"
31#include "grn_output.h"
32#include "grn_normalizer.h"
33#include "grn_mrb.h"
34#include "grn_ctx_impl_mrb.h"
35#include "grn_logger.h"
36#include "grn_cache.h"
37#include "grn_expr.h"
38#include <stdio.h>
39#include <stdarg.h>
40#include <time.h>
41
42#ifdef GRN_WITH_ONIGMO
43# define GRN_SUPPORT_REGEXP
44#endif /* GRN_WITH_ONIGMO */
45
46#ifdef GRN_SUPPORT_REGEXP
47# include <onigmo.h>
48#endif /* GRN_SUPPORT_REGEXP */
49
50#ifdef WIN32
51# include <share.h>
52#else /* WIN32 */
53# include <netinet/in.h>
54#endif /* WIN32 */
55
56#define GRN_CTX_INITIALIZER(enc) \
57 { GRN_SUCCESS, 0, enc, 0, GRN_LOG_NOTICE,\
58 GRN_CTX_FIN, 0, 0, 0, 0, {0}, NULL, NULL, NULL, NULL, NULL, \
59 {NULL, NULL,NULL, NULL,NULL, NULL,NULL, NULL,NULL, NULL,NULL, NULL,NULL, NULL,NULL, NULL}, ""}
60
61#define GRN_CTX_CLOSED(ctx) ((ctx)->stat == GRN_CTX_FIN)
62
63grn_ctx grn_gctx = GRN_CTX_INITIALIZER(GRN_ENC_DEFAULT);
64int grn_pagesize;
65grn_critical_section grn_glock;
66uint32_t grn_gtick;
67int grn_lock_timeout = GRN_LOCK_TIMEOUT;
68
69#ifdef USE_UYIELD
70int grn_uyield_count = 0;
71#endif
72
73static grn_bool grn_ctx_per_db = GRN_FALSE;
74
75static void
76grn_init_from_env(void)
77{
78 {
79 char grn_ctx_per_db_env[GRN_ENV_BUFFER_SIZE];
80 grn_getenv("GRN_CTX_PER_DB",
81 grn_ctx_per_db_env,
82 GRN_ENV_BUFFER_SIZE);
83 if (grn_ctx_per_db_env[0] && strcmp(grn_ctx_per_db_env, "yes") == 0) {
84 grn_ctx_per_db = GRN_TRUE;
85 }
86 }
87
88 grn_alloc_init_from_env();
89 grn_mrb_init_from_env();
90 grn_ctx_impl_mrb_init_from_env();
91 grn_io_init_from_env();
92 grn_ii_init_from_env();
93 grn_db_init_from_env();
94 grn_expr_init_from_env();
95 grn_index_column_init_from_env();
96 grn_proc_init_from_env();
97 grn_plugin_init_from_env();
98}
99
100static void
101grn_init_external_libraries(void)
102{
103#ifdef GRN_SUPPORT_REGEXP
104 onig_init();
105#endif /* GRN_SUPPORT_REGEXP */
106}
107
108static void
109grn_fin_external_libraries(void)
110{
111#ifdef GRN_SUPPORT_REGEXP
112 onig_end();
113#endif /* GRN_SUPPORT_REGEXP */
114}
115
116void
117grn_sleep(uint32_t seconds)
118{
119#ifdef WIN32
120 Sleep(seconds * 1000);
121#else // WIN32
122 sleep(seconds);
123#endif // WIN32
124}
125
126void
127grn_nanosleep(uint64_t nanoseconds)
128{
129#ifdef WIN32
130 Sleep((DWORD)(nanoseconds / 1000000));
131#else // WIN32
132 struct timespec interval;
133 interval.tv_sec = (time_t)(nanoseconds / 1000000000);
134 interval.tv_nsec = (long)(nanoseconds % 1000000000);
135 nanosleep(&interval, NULL);
136#endif // WIN32
137}
138
139const char *
140grn_get_global_error_message(void)
141{
142 return grn_gctx.errbuf;
143}
144
145static void
146grn_loader_init(grn_loader *loader)
147{
148 GRN_TEXT_INIT(&loader->values, 0);
149 GRN_UINT32_INIT(&loader->level, GRN_OBJ_VECTOR);
150 GRN_PTR_INIT(&loader->columns, GRN_OBJ_VECTOR, GRN_ID_NIL);
151 GRN_UINT32_INIT(&loader->ids, GRN_OBJ_VECTOR);
152 GRN_INT32_INIT(&loader->return_codes, GRN_OBJ_VECTOR);
153 GRN_TEXT_INIT(&loader->error_messages, GRN_OBJ_VECTOR);
154 loader->id_offset = -1;
155 loader->key_offset = -1;
156 loader->table = NULL;
157 loader->last = NULL;
158 loader->ifexists = NULL;
159 loader->each = NULL;
160 loader->values_size = 0;
161 loader->nrecords = 0;
162 loader->stat = GRN_LOADER_BEGIN;
163 loader->columns_status = GRN_LOADER_COLUMNS_UNSET;
164 loader->rc = GRN_SUCCESS;
165 loader->errbuf[0] = '\0';
166 loader->output_ids = GRN_FALSE;
167 loader->output_errors = GRN_FALSE;
168}
169
170void
171grn_ctx_loader_clear(grn_ctx *ctx)
172{
173 grn_loader *loader = &ctx->impl->loader;
174 grn_obj *v = (grn_obj *)(GRN_BULK_HEAD(&loader->values));
175 grn_obj *ve = (grn_obj *)(GRN_BULK_CURR(&loader->values));
176 grn_obj **p = (grn_obj **)GRN_BULK_HEAD(&loader->columns);
177 uint32_t i = GRN_BULK_VSIZE(&loader->columns) / sizeof(grn_obj *);
178 if (ctx->impl->db) { while (i--) { grn_obj_unlink(ctx, *p++); } }
179 if (loader->ifexists) { grn_obj_unlink(ctx, loader->ifexists); }
180 if (loader->each) { grn_obj_unlink(ctx, loader->each); }
181 while (v < ve) { GRN_OBJ_FIN(ctx, v++); }
182 GRN_OBJ_FIN(ctx, &loader->values);
183 GRN_OBJ_FIN(ctx, &loader->level);
184 GRN_OBJ_FIN(ctx, &loader->columns);
185 GRN_OBJ_FIN(ctx, &loader->ids);
186 GRN_OBJ_FIN(ctx, &loader->return_codes);
187 GRN_OBJ_FIN(ctx, &loader->error_messages);
188 grn_loader_init(loader);
189}
190
191#define IMPL_SIZE ((sizeof(struct _grn_ctx_impl) + (grn_pagesize - 1)) & ~(grn_pagesize - 1))
192
193#ifdef GRN_WITH_MESSAGE_PACK
194static int
195grn_msgpack_buffer_write(void *data, const char *buf, msgpack_size_t len)
196{
197 grn_ctx *ctx = (grn_ctx *)data;
198 return grn_bulk_write(ctx, ctx->impl->output.buf, buf, len);
199}
200#endif
201
202static grn_rc
203grn_ctx_impl_init(grn_ctx *ctx)
204{
205 grn_io_mapinfo mi;
206 if (!(ctx->impl = grn_io_anon_map(ctx, &mi, IMPL_SIZE))) {
207 return ctx->rc;
208 }
209 grn_alloc_init_ctx_impl(ctx);
210 ctx->impl->encoding = ctx->encoding;
211 ctx->impl->lifoseg = -1;
212 ctx->impl->currseg = -1;
213 CRITICAL_SECTION_INIT(ctx->impl->lock);
214 if (!(ctx->impl->values = grn_array_create(ctx, NULL, sizeof(grn_db_obj *),
215 GRN_ARRAY_TINY))) {
216 CRITICAL_SECTION_FIN(ctx->impl->lock);
217 grn_io_anon_unmap(ctx, &mi, IMPL_SIZE);
218 ctx->impl = NULL;
219 return ctx->rc;
220 }
221 if (!(ctx->impl->temporary_columns = grn_pat_create(ctx, NULL,
222 GRN_TABLE_MAX_KEY_SIZE,
223 sizeof(grn_obj *),
224 0))) {
225 grn_array_close(ctx, ctx->impl->values);
226 CRITICAL_SECTION_FIN(ctx->impl->lock);
227 grn_io_anon_unmap(ctx, &mi, IMPL_SIZE);
228 ctx->impl = NULL;
229 return ctx->rc;
230 }
231 if (!(ctx->impl->ios = grn_hash_create(ctx, NULL, GRN_TABLE_MAX_KEY_SIZE,
232 sizeof(grn_io *),
233 GRN_OBJ_KEY_VAR_SIZE|GRN_HASH_TINY))) {
234 grn_array_close(ctx, ctx->impl->values);
235 grn_pat_close(ctx, ctx->impl->temporary_columns);
236 CRITICAL_SECTION_FIN(ctx->impl->lock);
237 grn_io_anon_unmap(ctx, &mi, IMPL_SIZE);
238 ctx->impl = NULL;
239 return ctx->rc;
240 }
241 ctx->impl->db = NULL;
242
243 ctx->impl->expr_vars = grn_hash_create(ctx, NULL, sizeof(grn_id), sizeof(grn_obj *), 0);
244 ctx->impl->stack_curr = 0;
245 ctx->impl->curr_expr = NULL;
246 GRN_TEXT_INIT(&ctx->impl->current_request_id, 0);
247 ctx->impl->current_request_timer_id = NULL;
248 ctx->impl->parser = NULL;
249
250 GRN_TEXT_INIT(&ctx->impl->output.names, GRN_OBJ_VECTOR);
251 GRN_UINT32_INIT(&ctx->impl->output.levels, GRN_OBJ_VECTOR);
252
253 ctx->impl->command.flags = 0;
254 if (ctx == &grn_gctx) {
255 ctx->impl->command.version = GRN_COMMAND_VERSION_STABLE;
256 } else {
257 ctx->impl->command.version = grn_get_default_command_version();
258 }
259 ctx->impl->command.keep.command = NULL;
260 ctx->impl->command.keep.version = ctx->impl->command.version;
261
262 if (ctx == &grn_gctx) {
263 ctx->impl->match_escalation_threshold =
264 GRN_DEFAULT_MATCH_ESCALATION_THRESHOLD;
265 } else {
266 ctx->impl->match_escalation_threshold =
267 grn_get_default_match_escalation_threshold();
268 }
269
270 ctx->impl->finalizer = NULL;
271
272 ctx->impl->com = NULL;
273 ctx->impl->output.buf = grn_obj_open(ctx, GRN_BULK, 0, GRN_DB_TEXT);
274 ctx->impl->output.func = NULL;
275 ctx->impl->output.data.ptr = NULL;
276#ifdef GRN_WITH_MESSAGE_PACK
277 msgpack_packer_init(&ctx->impl->output.msgpacker,
278 ctx, grn_msgpack_buffer_write);
279#endif
280 ctx->impl->tv.tv_sec = 0;
281 ctx->impl->tv.tv_nsec = 0;
282 ctx->impl->edge = NULL;
283 grn_loader_init(&ctx->impl->loader);
284 ctx->impl->plugin_path = NULL;
285
286 GRN_TEXT_INIT(&ctx->impl->query_log_buf, 0);
287
288 ctx->impl->previous_errbuf[0] = '\0';
289 ctx->impl->n_same_error_messages = 0;
290
291 grn_ctx_impl_mrb_init(ctx);
292
293 GRN_TEXT_INIT(&(ctx->impl->temporary_open_spaces.stack), 0);
294 ctx->impl->temporary_open_spaces.current = NULL;
295
296 return ctx->rc;
297}
298
299void
300grn_ctx_set_keep_command(grn_ctx *ctx, grn_obj *command)
301{
302 ctx->impl->command.keep.command = command;
303 ctx->impl->command.keep.version = ctx->impl->command.version;
304}
305
306static void
307grn_ctx_impl_clear_n_same_error_messagges(grn_ctx *ctx)
308{
309 if (ctx->impl->n_same_error_messages == 0) {
310 return;
311 }
312
313 GRN_LOG(ctx, GRN_LOG_NOTICE, "(%u same messages are truncated)",
314 ctx->impl->n_same_error_messages);
315 ctx->impl->n_same_error_messages = 0;
316}
317
318grn_bool
319grn_ctx_impl_should_log(grn_ctx *ctx)
320{
321 if (!ctx->impl) {
322 return GRN_TRUE;
323 }
324
325 if (strcmp(ctx->errbuf, ctx->impl->previous_errbuf) == 0) {
326 ctx->impl->n_same_error_messages++;
327 return GRN_FALSE;
328 }
329
330 return GRN_TRUE;
331}
332
333void
334grn_ctx_impl_set_current_error_message(grn_ctx *ctx)
335{
336 if (!ctx->impl) {
337 return;
338 }
339
340 grn_ctx_impl_clear_n_same_error_messagges(ctx);
341 grn_strcpy(ctx->impl->previous_errbuf, GRN_CTX_MSGSIZE, ctx->errbuf);
342}
343
344static grn_rc
345grn_ctx_init_internal(grn_ctx *ctx, int flags)
346{
347 if (!ctx) { return GRN_INVALID_ARGUMENT; }
348 // if (ctx->stat != GRN_CTX_FIN) { return GRN_INVALID_ARGUMENT; }
349 ctx->rc = GRN_SUCCESS;
350 ERRCLR(ctx);
351 ctx->flags = flags;
352 if (grn_ctx_per_db) {
353 ctx->flags |= GRN_CTX_PER_DB;
354 }
355 ctx->stat = GRN_CTX_INITED;
356 ctx->encoding = grn_gctx.encoding;
357 ctx->seqno = 0;
358 ctx->seqno2 = 0;
359 ctx->subno = 0;
360 ctx->impl = NULL;
361 ctx->user_data.ptr = NULL;
362 CRITICAL_SECTION_ENTER(grn_glock);
363 ctx->next = grn_gctx.next;
364 ctx->prev = &grn_gctx;
365 grn_gctx.next->prev = ctx;
366 grn_gctx.next = ctx;
367 CRITICAL_SECTION_LEAVE(grn_glock);
368 ctx->errline = 0;
369 ctx->errfile = "";
370 ctx->errfunc = "";
371 ctx->trace[0] = NULL;
372 ctx->errbuf[0] = '\0';
373 return GRN_SUCCESS;
374}
375
376grn_rc
377grn_ctx_init(grn_ctx *ctx, int flags)
378{
379 grn_rc rc;
380
381 rc = grn_ctx_init_internal(ctx, flags);
382 if (rc == GRN_SUCCESS) {
383 grn_ctx_impl_init(ctx);
384 rc = ctx->rc;
385 if (rc != GRN_SUCCESS) {
386 grn_ctx_fin(ctx);
387 if (flags & GRN_CTX_ALLOCATED) {
388 CRITICAL_SECTION_ENTER(grn_glock);
389 ctx->next->prev = ctx->prev;
390 ctx->prev->next = ctx->next;
391 CRITICAL_SECTION_LEAVE(grn_glock);
392 }
393 }
394 }
395
396 return rc;
397}
398
399grn_ctx *
400grn_ctx_open(int flags)
401{
402 grn_ctx *ctx = GRN_GMALLOCN(grn_ctx, 1);
403 if (ctx) {
404 grn_ctx_init(ctx, flags|GRN_CTX_ALLOCATED);
405 if (ERRP(ctx, GRN_ERROR)) {
406 GRN_GFREE(ctx);
407 ctx = NULL;
408 }
409 }
410 return ctx;
411}
412
413grn_rc
414grn_ctx_fin(grn_ctx *ctx)
415{
416 grn_rc rc = GRN_SUCCESS;
417 if (!ctx) { return GRN_INVALID_ARGUMENT; }
418 if (ctx->stat == GRN_CTX_FIN) { return GRN_INVALID_ARGUMENT; }
419 if (!(ctx->flags & GRN_CTX_ALLOCATED)) {
420 CRITICAL_SECTION_ENTER(grn_glock);
421 ctx->next->prev = ctx->prev;
422 ctx->prev->next = ctx->next;
423 CRITICAL_SECTION_LEAVE(grn_glock);
424 }
425 if (ctx->impl) {
426 grn_ctx_impl_clear_n_same_error_messagges(ctx);
427 if (ctx->impl->finalizer) {
428 ctx->impl->finalizer(ctx, 0, NULL, &(ctx->user_data));
429 }
430 {
431 grn_obj *stack;
432 grn_obj *spaces;
433 unsigned int i, n_spaces;
434
435 stack = &(ctx->impl->temporary_open_spaces.stack);
436 spaces = (grn_obj *)GRN_BULK_HEAD(stack);
437 n_spaces = GRN_BULK_VSIZE(stack) / sizeof(grn_obj);
438 for (i = 0; i < n_spaces; i++) {
439 grn_obj *space = spaces + (n_spaces - i - 1);
440 GRN_OBJ_FIN(ctx, space);
441 }
442 GRN_OBJ_FIN(ctx, stack);
443 }
444 grn_ctx_impl_mrb_fin(ctx);
445 grn_ctx_loader_clear(ctx);
446 if (ctx->impl->parser) {
447 grn_expr_parser_close(ctx);
448 }
449 GRN_OBJ_FIN(ctx, &ctx->impl->current_request_id);
450 if (ctx->impl->values) {
451#ifndef USE_MEMORY_DEBUG
452 grn_db_obj *o;
453 GRN_ARRAY_EACH(ctx, ctx->impl->values, 0, 0, id, &o, {
454 grn_obj_close(ctx, *((grn_obj **)o));
455 });
456#endif
457 grn_array_close(ctx, ctx->impl->values);
458 }
459 if (ctx->impl->temporary_columns) {
460#ifndef USE_MEMORY_DEBUG
461 grn_obj *value;
462 GRN_PAT_EACH(ctx, ctx->impl->temporary_columns, id, NULL, NULL, &value, {
463 grn_obj_close(ctx, *((grn_obj **)value));
464 });
465#endif
466 grn_pat_close(ctx, ctx->impl->temporary_columns);
467 }
468 if (ctx->impl->ios) {
469 grn_hash_close(ctx, ctx->impl->ios);
470 }
471 if (ctx->impl->com) {
472 if (ctx->stat != GRN_CTX_QUIT) {
473 int flags;
474 char *str;
475 unsigned int str_len;
476 grn_ctx_send(ctx, "quit", 4, GRN_CTX_HEAD);
477 grn_ctx_recv(ctx, &str, &str_len, &flags);
478 }
479 grn_ctx_send(ctx, "ACK", 3, GRN_CTX_HEAD);
480 rc = grn_com_close(ctx, ctx->impl->com);
481 }
482 GRN_OBJ_FIN(ctx, &ctx->impl->query_log_buf);
483 GRN_OBJ_FIN(ctx, &ctx->impl->output.names);
484 GRN_OBJ_FIN(ctx, &ctx->impl->output.levels);
485 rc = grn_obj_close(ctx, ctx->impl->output.buf);
486 {
487 grn_hash **vp;
488 grn_obj *value;
489 GRN_HASH_EACH(ctx, ctx->impl->expr_vars, eid, NULL, NULL, &vp, {
490 if (*vp) {
491 GRN_HASH_EACH(ctx, *vp, id, NULL, NULL, &value, {
492 GRN_OBJ_FIN(ctx, value);
493 });
494 }
495 grn_hash_close(ctx, *vp);
496 });
497 }
498 grn_hash_close(ctx, ctx->impl->expr_vars);
499 if (ctx->impl->db && ctx->flags & GRN_CTX_PER_DB) {
500 grn_obj *db = ctx->impl->db;
501 ctx->impl->db = NULL;
502 grn_obj_close(ctx, db);
503 }
504 grn_alloc_fin_ctx_impl(ctx);
505 grn_alloc_info_dump(ctx);
506 grn_alloc_info_free(ctx);
507 CRITICAL_SECTION_FIN(ctx->impl->lock);
508 {
509 grn_io_mapinfo mi;
510 mi.map = (void *)ctx->impl;
511 grn_io_anon_unmap(ctx, &mi, IMPL_SIZE);
512 }
513 ctx->impl = NULL;
514 }
515 ctx->stat = GRN_CTX_FIN;
516 return rc;
517}
518
519grn_rc
520grn_ctx_set_finalizer(grn_ctx *ctx, grn_proc_func *finalizer)
521{
522 if (!ctx) { return GRN_INVALID_ARGUMENT; }
523 if (!ctx->impl) {
524 if (ERRP(ctx, GRN_ERROR)) { return ctx->rc; }
525 }
526 ctx->impl->finalizer = finalizer;
527 return GRN_SUCCESS;
528}
529
530grn_timeval grn_starttime;
531
532static void
533check_overcommit_memory(grn_ctx *ctx)
534{
535 FILE *file;
536 int value;
537 file = grn_fopen("/proc/sys/vm/overcommit_memory", "r");
538 if (!file) { return; }
539 value = fgetc(file);
540 if (value != '1') {
541 GRN_LOG(ctx, GRN_LOG_NOTICE,
542 "vm.overcommit_memory kernel parameter should be 1: <%c>: "
543 "See INFO level log to resolve this",
544 value);
545 GRN_LOG(ctx, GRN_LOG_INFO,
546 "Some processings with vm.overcommit_memory != 1 "
547 "may break DB under low memory condition.");
548 GRN_LOG(ctx, GRN_LOG_INFO,
549 "To set vm.overcommit_memory to 1");
550 GRN_LOG(ctx, GRN_LOG_INFO,
551 "add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and "
552 "restart your system or");
553 GRN_LOG(ctx, GRN_LOG_INFO,
554 "run 'sudo /sbin/sysctl vm.overcommit_memory=1' command.");
555 }
556 fclose(file);
557}
558
559grn_rc
560grn_init(void)
561{
562 grn_rc rc;
563 grn_ctx *ctx = &grn_gctx;
564 grn_init_from_env();
565 grn_init_external_libraries();
566 grn_alloc_info_init();
567 grn_logger_init();
568 grn_query_logger_init();
569 CRITICAL_SECTION_INIT(grn_glock);
570 grn_gtick = 0;
571 ctx->next = ctx;
572 ctx->prev = ctx;
573 rc = grn_ctx_init_internal(ctx, 0);
574 if (rc) {
575 goto fail_ctx_init_internal;
576 }
577 ctx->encoding = grn_encoding_parse(GRN_DEFAULT_ENCODING);
578 rc = grn_timeval_now(ctx, &grn_starttime);
579 if (rc) {
580 goto fail_start_time;
581 }
582#ifdef WIN32
583 {
584 SYSTEM_INFO si;
585 GetSystemInfo(&si);
586 grn_pagesize = si.dwAllocationGranularity;
587 }
588#else /* WIN32 */
589 if ((grn_pagesize = sysconf(_SC_PAGESIZE)) == -1) {
590 SERR("_SC_PAGESIZE");
591 rc = ctx->rc;
592 goto fail_page_size;
593 }
594#endif /* WIN32 */
595 if (grn_pagesize & (grn_pagesize - 1)) {
596 GRN_LOG(ctx, GRN_LOG_CRIT, "pagesize=%x", grn_pagesize);
597 }
598 // expand_stack();
599 if ((rc = grn_com_init())) {
600 GRN_LOG(ctx, GRN_LOG_ALERT, "grn_com_init failed (%d)", rc);
601 goto fail_com;
602 }
603 if ((rc = grn_ctx_impl_init(ctx))) {
604 GRN_LOG(ctx, GRN_LOG_ALERT, "grn_ctx_impl_init failed (%d)", rc);
605 goto fail_ctx_impl;
606 }
607 if ((rc = grn_plugins_init())) {
608 GRN_LOG(ctx, GRN_LOG_ALERT, "grn_plugins_init failed (%d)", rc);
609 goto fail_plugins;
610 }
611 if ((rc = grn_normalizer_init())) {
612 GRN_LOG(ctx, GRN_LOG_ALERT, "grn_normalizer_init failed (%d)", rc);
613 goto fail_normalizer;
614 }
615 if ((rc = grn_tokenizers_init())) {
616 GRN_LOG(ctx, GRN_LOG_ALERT, "grn_tokenizers_init failed (%d)", rc);
617 goto fail_tokenizer;
618 }
619 grn_cache_init();
620 if (!grn_request_canceler_init()) {
621 rc = ctx->rc;
622 GRN_LOG(ctx, GRN_LOG_ALERT,
623 "failed to initialize request canceler (%d)", rc);
624 goto fail_request_canceler;
625 }
626 if (!grn_request_timer_init()) {
627 rc = ctx->rc;
628 GRN_LOG(ctx, GRN_LOG_ALERT,
629 "failed to initialize request timer (%d)", rc);
630 goto fail_request_timer;
631 }
632 GRN_LOG(ctx, GRN_LOG_NOTICE, "grn_init: <%s>", grn_get_version());
633 check_overcommit_memory(ctx);
634 return rc;
635
636fail_request_timer:
637 grn_request_canceler_fin();
638fail_request_canceler:
639 grn_cache_fin();
640fail_tokenizer:
641 grn_normalizer_fin();
642fail_normalizer:
643 grn_plugins_fin();
644fail_plugins:
645 grn_ctx_fin(ctx);
646fail_ctx_impl:
647 grn_com_fin();
648fail_com:
649#ifndef WIN32
650fail_page_size:
651#endif /* WIN32 */
652fail_start_time:
653fail_ctx_init_internal:
654 GRN_LOG(ctx, GRN_LOG_NOTICE, "grn_init: <%s>: failed", grn_get_version());
655 grn_query_logger_fin(ctx);
656 grn_logger_fin(ctx);
657 CRITICAL_SECTION_FIN(grn_glock);
658 grn_alloc_info_fin();
659 grn_fin_external_libraries();
660 return rc;
661}
662
663grn_encoding
664grn_get_default_encoding(void)
665{
666 return grn_gctx.encoding;
667}
668
669grn_rc
670grn_set_default_encoding(grn_encoding encoding)
671{
672 switch (encoding) {
673 case GRN_ENC_DEFAULT :
674 grn_gctx.encoding = grn_encoding_parse(GRN_DEFAULT_ENCODING);
675 return GRN_SUCCESS;
676 case GRN_ENC_NONE :
677 case GRN_ENC_EUC_JP :
678 case GRN_ENC_UTF8 :
679 case GRN_ENC_SJIS :
680 case GRN_ENC_LATIN1 :
681 case GRN_ENC_KOI8R :
682 grn_gctx.encoding = encoding;
683 return GRN_SUCCESS;
684 default :
685 return GRN_INVALID_ARGUMENT;
686 }
687}
688
689grn_command_version
690grn_get_default_command_version(void)
691{
692 return grn_ctx_get_command_version(&grn_gctx);
693}
694
695grn_rc
696grn_set_default_command_version(grn_command_version version)
697{
698 return grn_ctx_set_command_version(&grn_gctx, version);
699}
700
701long long int
702grn_get_default_match_escalation_threshold(void)
703{
704 return grn_ctx_get_match_escalation_threshold(&grn_gctx);
705}
706
707grn_rc
708grn_set_default_match_escalation_threshold(long long int threshold)
709{
710 return grn_ctx_set_match_escalation_threshold(&grn_gctx, threshold);
711}
712
713int
714grn_get_lock_timeout(void)
715{
716 return grn_lock_timeout;
717}
718
719grn_rc
720grn_set_lock_timeout(int timeout)
721{
722 grn_lock_timeout = timeout;
723 return GRN_SUCCESS;
724}
725
726grn_rc
727grn_fin(void)
728{
729 grn_ctx *ctx, *ctx_;
730 if (grn_gctx.stat == GRN_CTX_FIN) { return GRN_INVALID_ARGUMENT; }
731 for (ctx = grn_gctx.next; ctx != &grn_gctx; ctx = ctx_) {
732 ctx_ = ctx->next;
733 if (ctx->stat != GRN_CTX_FIN) { grn_ctx_fin(ctx); }
734 if (ctx->flags & GRN_CTX_ALLOCATED) {
735 ctx->next->prev = ctx->prev;
736 ctx->prev->next = ctx->next;
737 GRN_GFREE(ctx);
738 }
739 }
740 grn_query_logger_fin(ctx);
741 grn_request_timer_fin();
742 grn_request_canceler_fin();
743 grn_cache_fin();
744 grn_tokenizers_fin();
745 grn_normalizer_fin();
746 grn_plugins_fin();
747 grn_ctx_fin(ctx);
748 grn_com_fin();
749 GRN_LOG(ctx, GRN_LOG_NOTICE, "grn_fin (%d)", grn_alloc_count());
750 grn_logger_fin(ctx);
751 CRITICAL_SECTION_FIN(grn_glock);
752 grn_alloc_info_fin();
753 grn_fin_external_libraries();
754 return GRN_SUCCESS;
755}
756
757grn_rc
758grn_ctx_connect(grn_ctx *ctx, const char *host, int port, int flags)
759{
760 GRN_API_ENTER;
761 if (!ctx->impl) { goto exit; }
762 {
763 grn_com *com = grn_com_copen(ctx, NULL, host, port);
764 if (com) {
765 ctx->impl->com = com;
766 }
767 }
768exit :
769 GRN_API_RETURN(ctx->rc);
770}
771
772grn_rc
773grn_ctx_close(grn_ctx *ctx)
774{
775 grn_rc rc = grn_ctx_fin(ctx);
776 CRITICAL_SECTION_ENTER(grn_glock);
777 ctx->next->prev = ctx->prev;
778 ctx->prev->next = ctx->next;
779 CRITICAL_SECTION_LEAVE(grn_glock);
780 GRN_GFREE(ctx);
781 return rc;
782}
783
784grn_command_version
785grn_ctx_get_command_version(grn_ctx *ctx)
786{
787 if (ctx->impl) {
788 return ctx->impl->command.version;
789 } else {
790 return GRN_COMMAND_VERSION_STABLE;
791 }
792}
793
794grn_rc
795grn_ctx_set_command_version(grn_ctx *ctx, grn_command_version version)
796{
797 switch (version) {
798 case GRN_COMMAND_VERSION_DEFAULT :
799 ctx->impl->command.version = GRN_COMMAND_VERSION_STABLE;
800 return GRN_SUCCESS;
801 default :
802 if (GRN_COMMAND_VERSION_MIN <= version &&
803 version <= GRN_COMMAND_VERSION_MAX) {
804 ctx->impl->command.version = version;
805 return GRN_SUCCESS;
806 } else {
807 return GRN_UNSUPPORTED_COMMAND_VERSION;
808 }
809 }
810}
811
812grn_content_type
813grn_ctx_get_output_type(grn_ctx *ctx)
814{
815 if (ctx->impl) {
816 return ctx->impl->output.type;
817 } else {
818 return GRN_CONTENT_NONE;
819 }
820}
821
822grn_rc
823grn_ctx_set_output_type(grn_ctx *ctx, grn_content_type type)
824{
825 grn_rc rc = GRN_SUCCESS;
826
827 if (ctx->impl) {
828 ctx->impl->output.type = type;
829 switch (ctx->impl->output.type) {
830 case GRN_CONTENT_NONE :
831 ctx->impl->output.mime_type = "application/octet-stream";
832 break;
833 case GRN_CONTENT_TSV :
834 ctx->impl->output.mime_type = "text/tab-separated-values";
835 break;
836 case GRN_CONTENT_JSON :
837 ctx->impl->output.mime_type = "application/json";
838 break;
839 case GRN_CONTENT_XML :
840 ctx->impl->output.mime_type = "text/xml";
841 break;
842 case GRN_CONTENT_MSGPACK :
843 ctx->impl->output.mime_type = "application/x-msgpack";
844 break;
845 case GRN_CONTENT_GROONGA_COMMAND_LIST :
846 ctx->impl->output.mime_type = "text/x-groonga-command-list";
847 break;
848 }
849 } else {
850 rc = GRN_INVALID_ARGUMENT;
851 }
852
853 return rc;
854}
855
856const char *
857grn_ctx_get_mime_type(grn_ctx *ctx)
858{
859 if (ctx->impl) {
860 return ctx->impl->output.mime_type;
861 } else {
862 return NULL;
863 }
864}
865
866long long int
867grn_ctx_get_match_escalation_threshold(grn_ctx *ctx)
868{
869 if (ctx->impl) {
870 return ctx->impl->match_escalation_threshold;
871 } else {
872 return GRN_DEFAULT_MATCH_ESCALATION_THRESHOLD;
873 }
874}
875
876grn_rc
877grn_ctx_set_match_escalation_threshold(grn_ctx *ctx, long long int threshold)
878{
879 ctx->impl->match_escalation_threshold = threshold;
880 return GRN_SUCCESS;
881}
882
883grn_content_type
884grn_get_ctype(grn_obj *var)
885{
886 return grn_content_type_parse(NULL, var, GRN_CONTENT_JSON);
887}
888
889grn_content_type
890grn_content_type_parse(grn_ctx *ctx,
891 grn_obj *var,
892 grn_content_type default_value)
893{
894 grn_content_type ct = default_value;
895 if (var->header.domain == GRN_DB_INT32) {
896 ct = GRN_INT32_VALUE(var);
897 } else if (GRN_TEXT_LEN(var)) {
898 switch (*(GRN_TEXT_VALUE(var))) {
899 case 't' :
900 case 'T' :
901 ct = GRN_CONTENT_TSV;
902 break;
903 case 'j' :
904 case 'J' :
905 ct = GRN_CONTENT_JSON;
906 break;
907 case 'x' :
908 case 'X' :
909 ct = GRN_CONTENT_XML;
910 break;
911 }
912 }
913 return ct;
914}
915
916static void
917get_content_mime_type(grn_ctx *ctx, const char *p, const char *pe)
918{
919 ctx->impl->output.type = GRN_CONTENT_NONE;
920 ctx->impl->output.mime_type = "application/octet-stream";
921
922 if (p + 2 <= pe) {
923 switch (*p) {
924 case 'c' :
925 if (p + 3 == pe && !memcmp(p, "css", 3)) {
926 ctx->impl->output.type = GRN_CONTENT_NONE;
927 ctx->impl->output.mime_type = "text/css";
928 }
929 break;
930 case 'g' :
931 if (p + 3 == pe && !memcmp(p, "gif", 3)) {
932 ctx->impl->output.type = GRN_CONTENT_NONE;
933 ctx->impl->output.mime_type = "image/gif";
934 }
935 break;
936 case 'h' :
937 if (p + 4 == pe && !memcmp(p, "html", 4)) {
938 ctx->impl->output.type = GRN_CONTENT_NONE;
939 ctx->impl->output.mime_type = "text/html";
940 }
941 break;
942 case 'j' :
943 if (!memcmp(p, "js", 2)) {
944 if (p + 2 == pe) {
945 ctx->impl->output.type = GRN_CONTENT_NONE;
946 ctx->impl->output.mime_type = "text/javascript";
947 } else if (p + 4 == pe && !memcmp(p + 2, "on", 2)) {
948 ctx->impl->output.type = GRN_CONTENT_JSON;
949 ctx->impl->output.mime_type = "application/json";
950 }
951 } else if (p + 3 == pe && !memcmp(p, "jpg", 3)) {
952 ctx->impl->output.type = GRN_CONTENT_NONE;
953 ctx->impl->output.mime_type = "image/jpeg";
954 }
955 break;
956#ifdef GRN_WITH_MESSAGE_PACK
957 case 'm' :
958 if (p + 7 == pe && !memcmp(p, "msgpack", 7)) {
959 ctx->impl->output.type = GRN_CONTENT_MSGPACK;
960 ctx->impl->output.mime_type = "application/x-msgpack";
961 }
962 break;
963#endif
964 case 'p' :
965 if (p + 3 == pe && !memcmp(p, "png", 3)) {
966 ctx->impl->output.type = GRN_CONTENT_NONE;
967 ctx->impl->output.mime_type = "image/png";
968 }
969 break;
970 case 't' :
971 if (p + 3 == pe && !memcmp(p, "txt", 3)) {
972 ctx->impl->output.type = GRN_CONTENT_NONE;
973 ctx->impl->output.mime_type = "text/plain";
974 } else if (p + 3 == pe && !memcmp(p, "tsv", 3)) {
975 ctx->impl->output.type = GRN_CONTENT_TSV;
976 ctx->impl->output.mime_type = "text/tab-separated-values";
977 }
978 break;
979 case 'x':
980 if (p + 3 == pe && !memcmp(p, "xml", 3)) {
981 ctx->impl->output.type = GRN_CONTENT_XML;
982 ctx->impl->output.mime_type = "text/xml";
983 }
984 break;
985 }
986 }
987}
988
989static void
990grn_str_get_mime_type(grn_ctx *ctx, const char *p, const char *pe,
991 const char **key_end, const char **filename_end)
992{
993 const char *pd = NULL;
994 for (; p < pe && *p != '?' && *p != '#'; p++) {
995 if (*p == '.') { pd = p; }
996 }
997 *filename_end = p;
998 if (pd && pd < p) {
999 get_content_mime_type(ctx, pd + 1, p);
1000 *key_end = pd;
1001 } else {
1002 *key_end = pe;
1003 }
1004}
1005
1006static void
1007get_command_version(grn_ctx *ctx, const char *p, const char *pe)
1008{
1009 grn_command_version version;
1010 const char *rest;
1011
1012 version = grn_atoui(p, pe, &rest);
1013 if (pe == rest) {
1014 grn_rc rc;
1015 rc = grn_ctx_set_command_version(ctx, version);
1016 if (rc == GRN_UNSUPPORTED_COMMAND_VERSION) {
1017 ERR(rc,
1018 "unsupported command version is specified: %d: "
1019 "stable command version: %d: "
1020 "available command versions: %d-%d",
1021 version,
1022 GRN_COMMAND_VERSION_STABLE,
1023 GRN_COMMAND_VERSION_MIN, GRN_COMMAND_VERSION_MAX);
1024 }
1025 }
1026}
1027
1028#define INDEX_HTML "index.html"
1029#define OUTPUT_TYPE "output_type"
1030#define COMMAND_VERSION "command_version"
1031#define REQUEST_ID "request_id"
1032#define REQUEST_TIMEOUT "request_timeout"
1033#define OUTPUT_PRETTY "output_pretty"
1034#define EXPR_MISSING "expr_missing"
1035#define OUTPUT_TYPE_LEN (sizeof(OUTPUT_TYPE) - 1)
1036#define COMMAND_VERSION_LEN (sizeof(COMMAND_VERSION) - 1)
1037#define REQUEST_ID_LEN (sizeof(REQUEST_ID) - 1)
1038#define REQUEST_TIMEOUT_LEN (sizeof(REQUEST_TIMEOUT) - 1)
1039#define OUTPUT_PRETTY_LEN (sizeof(OUTPUT_PRETTY) - 1)
1040
1041#define HTTP_QUERY_PAIR_DELIMITER "="
1042#define HTTP_QUERY_PAIRS_DELIMITERS "&;"
1043
1044static inline int
1045command_proc_p(grn_obj *expr)
1046{
1047 return (expr->header.type == GRN_PROC &&
1048 ((grn_proc *)expr)->type == GRN_PROC_COMMAND);
1049}
1050
1051grn_obj *
1052grn_ctx_qe_exec_uri(grn_ctx *ctx, const char *path, uint32_t path_len)
1053{
1054 grn_obj buf, *expr, *val;
1055 grn_obj request_id;
1056 double request_timeout;
1057 const char *p = path, *e = path + path_len, *v, *key_end, *filename_end;
1058
1059 request_timeout = grn_get_default_request_timeout();
1060
1061 GRN_TEXT_INIT(&buf, 0);
1062 GRN_TEXT_INIT(&request_id, 0);
1063 p = grn_text_urldec(ctx, &buf, p, e, '?');
1064 if (!GRN_TEXT_LEN(&buf)) { GRN_TEXT_SETS(ctx, &buf, INDEX_HTML); }
1065 v = GRN_TEXT_VALUE(&buf);
1066 grn_str_get_mime_type(ctx, v, GRN_BULK_CURR(&buf), &key_end, &filename_end);
1067 if ((GRN_TEXT_LEN(&buf) >= 2 && v[0] == 'd' && v[1] == '/')) {
1068 const char *command_name = v + 2;
1069 int command_name_size = key_end - command_name;
1070 expr = grn_ctx_get(ctx, command_name, command_name_size);
1071 if (expr && command_proc_p(expr)) {
1072 while (p < e) {
1073 int l;
1074 GRN_BULK_REWIND(&buf);
1075 p = grn_text_cgidec(ctx, &buf, p, e, HTTP_QUERY_PAIR_DELIMITER);
1076 v = GRN_TEXT_VALUE(&buf);
1077 l = GRN_TEXT_LEN(&buf);
1078 if (l == OUTPUT_TYPE_LEN && !memcmp(v, OUTPUT_TYPE, OUTPUT_TYPE_LEN)) {
1079 GRN_BULK_REWIND(&buf);
1080 p = grn_text_cgidec(ctx, &buf, p, e, HTTP_QUERY_PAIRS_DELIMITERS);
1081 v = GRN_TEXT_VALUE(&buf);
1082 get_content_mime_type(ctx, v, GRN_BULK_CURR(&buf));
1083 } else if (l == COMMAND_VERSION_LEN &&
1084 !memcmp(v, COMMAND_VERSION, COMMAND_VERSION_LEN)) {
1085 GRN_BULK_REWIND(&buf);
1086 p = grn_text_cgidec(ctx, &buf, p, e, HTTP_QUERY_PAIRS_DELIMITERS);
1087 get_command_version(ctx, GRN_TEXT_VALUE(&buf), GRN_BULK_CURR(&buf));
1088 if (ctx->rc) { goto exit; }
1089 } else if (l == REQUEST_ID_LEN &&
1090 !memcmp(v, REQUEST_ID, REQUEST_ID_LEN)) {
1091 GRN_BULK_REWIND(&request_id);
1092 p = grn_text_cgidec(ctx, &request_id, p, e,
1093 HTTP_QUERY_PAIRS_DELIMITERS);
1094 } else if (l == REQUEST_TIMEOUT_LEN &&
1095 !memcmp(v, REQUEST_TIMEOUT, REQUEST_TIMEOUT_LEN)) {
1096 GRN_BULK_REWIND(&buf);
1097 p = grn_text_cgidec(ctx, &buf, p, e, HTTP_QUERY_PAIRS_DELIMITERS);
1098 GRN_TEXT_PUTC(ctx, &buf, '\0');
1099 request_timeout = strtod(GRN_TEXT_VALUE(&buf), NULL);
1100 } else if (l == OUTPUT_PRETTY_LEN &&
1101 !memcmp(v, OUTPUT_PRETTY, OUTPUT_PRETTY_LEN)) {
1102 GRN_BULK_REWIND(&buf);
1103 p = grn_text_cgidec(ctx, &buf, p, e, HTTP_QUERY_PAIRS_DELIMITERS);
1104 if (GRN_TEXT_LEN(&buf) == strlen("yes") &&
1105 !memcmp(GRN_TEXT_VALUE(&buf), "yes", GRN_TEXT_LEN(&buf))) {
1106 ctx->impl->output.is_pretty = GRN_TRUE;
1107 } else {
1108 ctx->impl->output.is_pretty = GRN_FALSE;
1109 }
1110 } else {
1111 if (!(val = grn_expr_get_or_add_var(ctx, expr, v, l))) {
1112 val = &buf;
1113 }
1114 grn_obj_reinit(ctx, val, GRN_DB_TEXT, 0);
1115 p = grn_text_cgidec(ctx, val, p, e, HTTP_QUERY_PAIRS_DELIMITERS);
1116 }
1117 }
1118 if (request_timeout > 0 && GRN_TEXT_LEN(&request_id) == 0) {
1119 grn_text_printf(ctx, &request_id, "%p", ctx);
1120 }
1121 if (GRN_TEXT_LEN(&request_id) > 0) {
1122 GRN_TEXT_SET(ctx, &ctx->impl->current_request_id,
1123 GRN_TEXT_VALUE(&request_id),
1124 GRN_TEXT_LEN(&request_id));
1125 grn_request_canceler_register(ctx,
1126 GRN_TEXT_VALUE(&request_id),
1127 GRN_TEXT_LEN(&request_id));
1128 if (request_timeout > 0.0) {
1129 ctx->impl->current_request_timer_id =
1130 grn_request_timer_register(GRN_TEXT_VALUE(&request_id),
1131 GRN_TEXT_LEN(&request_id),
1132 request_timeout);
1133 }
1134 }
1135 ctx->impl->curr_expr = expr;
1136 grn_expr_exec(ctx, expr, 0);
1137 } else {
1138 ERR(GRN_INVALID_ARGUMENT, "invalid command name: %.*s",
1139 command_name_size, command_name);
1140 }
1141 } else if ((expr = grn_ctx_get(ctx, GRN_EXPR_MISSING_NAME,
1142 strlen(GRN_EXPR_MISSING_NAME)))) {
1143 if ((val = grn_expr_get_var_by_offset(ctx, expr, 0))) {
1144 grn_obj_reinit(ctx, val, GRN_DB_TEXT, 0);
1145 GRN_TEXT_SET(ctx, val, v, filename_end - v);
1146 }
1147 ctx->impl->curr_expr = expr;
1148 grn_expr_exec(ctx, expr, 0);
1149 }
1150exit :
1151 GRN_OBJ_FIN(ctx, &request_id);
1152 GRN_OBJ_FIN(ctx, &buf);
1153
1154 return expr;
1155}
1156
1157grn_obj *
1158grn_ctx_qe_exec(grn_ctx *ctx, const char *str, uint32_t str_len)
1159{
1160 char tok_type;
1161 int offset = 0;
1162 grn_obj buf, *expr = NULL, *val = NULL;
1163 grn_obj request_id;
1164 double request_timeout;
1165 const char *p = str, *e = str + str_len, *v;
1166
1167 request_timeout = grn_get_default_request_timeout();
1168
1169 GRN_TEXT_INIT(&buf, 0);
1170 GRN_TEXT_INIT(&request_id, 0);
1171 p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
1172 expr = grn_ctx_get(ctx, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
1173 while (p < e) {
1174 GRN_BULK_REWIND(&buf);
1175 p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
1176 v = GRN_TEXT_VALUE(&buf);
1177 switch (tok_type) {
1178 case GRN_TOK_VOID :
1179 p = e;
1180 break;
1181 case GRN_TOK_SYMBOL :
1182 if (GRN_TEXT_LEN(&buf) > 2 && v[0] == '-' && v[1] == '-') {
1183 int l = GRN_TEXT_LEN(&buf) - 2;
1184 v += 2;
1185 if (l == OUTPUT_TYPE_LEN && !memcmp(v, OUTPUT_TYPE, OUTPUT_TYPE_LEN)) {
1186 GRN_BULK_REWIND(&buf);
1187 p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
1188 v = GRN_TEXT_VALUE(&buf);
1189 get_content_mime_type(ctx, v, GRN_BULK_CURR(&buf));
1190 } else if (l == COMMAND_VERSION_LEN &&
1191 !memcmp(v, COMMAND_VERSION, COMMAND_VERSION_LEN)) {
1192 GRN_BULK_REWIND(&buf);
1193 p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
1194 get_command_version(ctx, GRN_TEXT_VALUE(&buf), GRN_BULK_CURR(&buf));
1195 if (ctx->rc) { goto exit; }
1196 } else if (l == REQUEST_ID_LEN &&
1197 !memcmp(v, REQUEST_ID, REQUEST_ID_LEN)) {
1198 GRN_BULK_REWIND(&request_id);
1199 p = grn_text_unesc_tok(ctx, &request_id, p, e, &tok_type);
1200 } else if (l == REQUEST_TIMEOUT_LEN &&
1201 !memcmp(v, REQUEST_TIMEOUT, REQUEST_TIMEOUT_LEN)) {
1202 GRN_BULK_REWIND(&buf);
1203 p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
1204 GRN_TEXT_PUTC(ctx, &buf, '\0');
1205 request_timeout = strtod(GRN_TEXT_VALUE(&buf), NULL);
1206 } else if (l == OUTPUT_PRETTY_LEN &&
1207 !memcmp(v, OUTPUT_PRETTY, OUTPUT_PRETTY_LEN)) {
1208 GRN_BULK_REWIND(&buf);
1209 p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
1210 if (GRN_TEXT_LEN(&buf) == strlen("yes") &&
1211 !memcmp(GRN_TEXT_VALUE(&buf), "yes", GRN_TEXT_LEN(&buf))) {
1212 ctx->impl->output.is_pretty = GRN_TRUE;
1213 } else {
1214 ctx->impl->output.is_pretty = GRN_FALSE;
1215 }
1216 } else if (expr && (val = grn_expr_get_or_add_var(ctx, expr, v, l))) {
1217 grn_obj_reinit(ctx, val, GRN_DB_TEXT, 0);
1218 p = grn_text_unesc_tok(ctx, val, p, e, &tok_type);
1219 } else {
1220 p = e;
1221 }
1222 break;
1223 }
1224 // fallthru
1225 case GRN_TOK_STRING :
1226 case GRN_TOK_QUOTE :
1227 if (expr && (val = grn_expr_get_var_by_offset(ctx, expr, offset++))) {
1228 grn_obj_reinit(ctx, val, GRN_DB_TEXT, 0);
1229 GRN_TEXT_PUT(ctx, val, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
1230 } else {
1231 p = e;
1232 }
1233 break;
1234 }
1235 }
1236 if (request_timeout > 0 && GRN_TEXT_LEN(&request_id) == 0) {
1237 grn_text_printf(ctx, &request_id, "%p", ctx);
1238 }
1239 if (GRN_TEXT_LEN(&request_id) > 0) {
1240 GRN_TEXT_SET(ctx, &ctx->impl->current_request_id,
1241 GRN_TEXT_VALUE(&request_id),
1242 GRN_TEXT_LEN(&request_id));
1243 grn_request_canceler_register(ctx,
1244 GRN_TEXT_VALUE(&request_id),
1245 GRN_TEXT_LEN(&request_id));
1246 if (request_timeout > 0.0) {
1247 ctx->impl->current_request_timer_id =
1248 grn_request_timer_register(GRN_TEXT_VALUE(&request_id),
1249 GRN_TEXT_LEN(&request_id),
1250 request_timeout);
1251 }
1252 }
1253 ctx->impl->curr_expr = expr;
1254 if (expr && command_proc_p(expr)) {
1255 grn_expr_exec(ctx, expr, 0);
1256 } else {
1257 GRN_BULK_REWIND(&buf);
1258 grn_text_unesc_tok(ctx, &buf, str, str + str_len, &tok_type);
1259 if (GRN_TEXT_LEN(&buf)) {
1260 ERR(GRN_INVALID_ARGUMENT, "invalid command name: %.*s",
1261 (int)GRN_TEXT_LEN(&buf), GRN_TEXT_VALUE(&buf));
1262 }
1263 }
1264exit :
1265 GRN_OBJ_FIN(ctx, &request_id);
1266 GRN_OBJ_FIN(ctx, &buf);
1267
1268 return expr;
1269}
1270
1271grn_rc
1272grn_ctx_sendv(grn_ctx *ctx, int argc, char **argv, int flags)
1273{
1274 grn_obj buf;
1275 GRN_API_ENTER;
1276 GRN_TEXT_INIT(&buf, 0);
1277 while (argc--) {
1278 // todo : encode into json like syntax
1279 GRN_TEXT_PUTS(ctx, &buf, *argv);
1280 argv++;
1281 if (argc) { GRN_TEXT_PUTC(ctx, &buf, ' '); }
1282 }
1283 grn_ctx_send(ctx, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf), flags);
1284 GRN_OBJ_FIN(ctx, &buf);
1285 GRN_API_RETURN(ctx->rc);
1286}
1287
1288static int
1289comment_command_p(const char *command, unsigned int length)
1290{
1291 const char *p, *e;
1292
1293 e = command + length;
1294 for (p = command; p < e; p++) {
1295 switch (*p) {
1296 case '#' :
1297 return GRN_TRUE;
1298 case ' ' :
1299 case '\t' :
1300 break;
1301 default :
1302 return GRN_FALSE;
1303 }
1304 }
1305 return GRN_FALSE;
1306}
1307
1308unsigned int
1309grn_ctx_send(grn_ctx *ctx, const char *str, unsigned int str_len, int flags)
1310{
1311 if (!ctx) { return 0; }
1312 GRN_API_ENTER;
1313 if (ctx->impl) {
1314 if ((flags & GRN_CTX_MORE)) { flags |= GRN_CTX_QUIET; }
1315 if (ctx->stat == GRN_CTX_QUIT) { flags |= GRN_CTX_QUIT; }
1316
1317 ctx->impl->command.flags = flags;
1318 if (ctx->impl->com) {
1319 grn_rc rc;
1320 grn_com_header sheader;
1321 grn_timeval_now(ctx, &ctx->impl->tv);
1322 sheader.proto = GRN_COM_PROTO_GQTP;
1323 sheader.qtype = 0;
1324 sheader.keylen = 0;
1325 sheader.level = 0;
1326 sheader.flags = flags;
1327 sheader.status = 0;
1328 sheader.opaque = 0;
1329 sheader.cas = 0;
1330 if ((rc = grn_com_send(ctx, ctx->impl->com, &sheader, (char *)str, str_len, 0))) {
1331 ERR(rc, "grn_com_send failed");
1332 }
1333 goto exit;
1334 } else {
1335 grn_command_version command_version;
1336 grn_obj *expr = NULL;
1337
1338 command_version = grn_ctx_get_command_version(ctx);
1339 if (ctx->impl->command.keep.command) {
1340 grn_obj *val;
1341 expr = ctx->impl->command.keep.command;
1342 ctx->impl->command.keep.command = NULL;
1343 grn_ctx_set_command_version(ctx, ctx->impl->command.keep.version);
1344 if ((val = grn_expr_get_var_by_offset(ctx, expr, 0))) {
1345 grn_obj_reinit(ctx, val, GRN_DB_TEXT, 0);
1346 GRN_TEXT_PUT(ctx, val, str, str_len);
1347 }
1348 grn_expr_exec(ctx, expr, 0);
1349 } else {
1350 if (comment_command_p(str, str_len)) { goto output; };
1351 GRN_BULK_REWIND(ctx->impl->output.buf);
1352 ctx->impl->output.type = GRN_CONTENT_JSON;
1353 ctx->impl->output.mime_type = "application/json";
1354 ctx->impl->output.is_pretty = GRN_FALSE;
1355 grn_timeval_now(ctx, &ctx->impl->tv);
1356 GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_COMMAND,
1357 ">", "%.*s", str_len, str);
1358 if (str_len && *str == '/') {
1359 expr = grn_ctx_qe_exec_uri(ctx, str + 1, str_len - 1);
1360 } else {
1361 expr = grn_ctx_qe_exec(ctx, str, str_len);
1362 }
1363 }
1364 if (ctx->stat == GRN_CTX_QUITTING) { ctx->stat = GRN_CTX_QUIT; }
1365 if (ctx->impl->command.keep.command) {
1366 ERRCLR(ctx);
1367 } else {
1368 if (ctx->impl->current_request_timer_id) {
1369 void *timer_id = ctx->impl->current_request_timer_id;
1370 ctx->impl->current_request_timer_id = NULL;
1371 grn_request_timer_unregister(timer_id);
1372 }
1373 if (GRN_TEXT_LEN(&ctx->impl->current_request_id) > 0) {
1374 grn_obj *request_id = &ctx->impl->current_request_id;
1375 grn_request_canceler_unregister(ctx,
1376 GRN_TEXT_VALUE(request_id),
1377 GRN_TEXT_LEN(request_id));
1378 GRN_BULK_REWIND(&ctx->impl->current_request_id);
1379 }
1380 GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_RESULT_CODE,
1381 "<", "rc=%d", ctx->rc);
1382 }
1383 output :
1384 if (!(ctx->impl->command.flags & GRN_CTX_QUIET) &&
1385 ctx->impl->output.func) {
1386 ctx->impl->output.func(ctx, GRN_CTX_TAIL, ctx->impl->output.data.ptr);
1387 }
1388 if (expr) { grn_expr_clear_vars(ctx, expr); }
1389 grn_ctx_set_command_version(ctx, command_version);
1390 goto exit;
1391 }
1392 }
1393 ERR(GRN_INVALID_ARGUMENT, "invalid ctx assigned");
1394exit :
1395 GRN_API_RETURN(0);
1396}
1397
1398unsigned int
1399grn_ctx_recv(grn_ctx *ctx, char **str, unsigned int *str_len, int *flags)
1400{
1401 if (!ctx) { return GRN_INVALID_ARGUMENT; }
1402
1403 *flags = 0;
1404
1405 if (ctx->stat == GRN_CTX_QUIT) {
1406 grn_bool have_buffer = GRN_FALSE;
1407
1408 if (ctx->impl &&
1409 !ctx->impl->com &&
1410 GRN_TEXT_LEN(ctx->impl->output.buf) > 0) {
1411 have_buffer = GRN_TRUE;
1412 }
1413
1414 *flags |= GRN_CTX_QUIT;
1415 if (!have_buffer) {
1416 *str = NULL;
1417 *str_len = 0;
1418 return 0;
1419 }
1420 }
1421
1422 GRN_API_ENTER;
1423 if (ctx->impl) {
1424 if (ctx->impl->com) {
1425 grn_com_header header;
1426 if (grn_com_recv(ctx, ctx->impl->com, &header, ctx->impl->output.buf)) {
1427 *str = NULL;
1428 *str_len = 0;
1429 *flags = 0;
1430 } else {
1431 *str = GRN_BULK_HEAD(ctx->impl->output.buf);
1432 *str_len = GRN_BULK_VSIZE(ctx->impl->output.buf);
1433 if (header.flags & GRN_CTX_QUIT) {
1434 ctx->stat = GRN_CTX_QUIT;
1435 *flags |= GRN_CTX_QUIT;
1436 } else {
1437 if (!(header.flags & GRN_CTX_TAIL)) {
1438 *flags |= GRN_CTX_MORE;
1439 }
1440 }
1441 ctx->impl->output.type = header.qtype;
1442 ctx->rc = (int16_t)ntohs(header.status);
1443 ctx->errbuf[0] = '\0';
1444 ctx->errline = 0;
1445 ctx->errfile = NULL;
1446 ctx->errfunc = NULL;
1447 }
1448 goto exit;
1449 } else {
1450 grn_obj *buf = ctx->impl->output.buf;
1451 unsigned int head = 0, tail = GRN_BULK_VSIZE(buf);
1452 *str = GRN_BULK_HEAD(buf) + head;
1453 *str_len = tail - head;
1454 GRN_BULK_REWIND(ctx->impl->output.buf);
1455 goto exit;
1456 }
1457 }
1458 ERR(GRN_INVALID_ARGUMENT, "invalid ctx assigned");
1459exit :
1460 GRN_API_RETURN(0);
1461}
1462
1463void
1464grn_ctx_stream_out_func(grn_ctx *ctx, int flags, void *stream)
1465{
1466 if (ctx && ctx->impl) {
1467 grn_obj *buf = ctx->impl->output.buf;
1468 uint32_t size = GRN_BULK_VSIZE(buf);
1469 if (size) {
1470 if (fwrite(GRN_BULK_HEAD(buf), 1, size, (FILE *)stream)) {
1471 fputc('\n', (FILE *)stream);
1472 fflush((FILE *)stream);
1473 }
1474 GRN_BULK_REWIND(buf);
1475 }
1476 }
1477}
1478
1479void
1480grn_ctx_recv_handler_set(grn_ctx *ctx, void (*func)(grn_ctx *, int, void *), void *func_arg)
1481{
1482 if (ctx && ctx->impl) {
1483 ctx->impl->output.func = func;
1484 ctx->impl->output.data.ptr = func_arg;
1485 }
1486}
1487
1488grn_rc
1489grn_ctx_info_get(grn_ctx *ctx, grn_ctx_info *info)
1490{
1491 if (!ctx || !ctx->impl) { return GRN_INVALID_ARGUMENT; }
1492 if (ctx->impl->com) {
1493 info->fd = ctx->impl->com->fd;
1494 info->com_status = ctx->impl->com_status;
1495 info->outbuf = ctx->impl->output.buf;
1496 info->stat = ctx->stat;
1497 } else {
1498 info->fd = -1;
1499 info->com_status = 0;
1500 info->outbuf = ctx->impl->output.buf;
1501 info->stat = ctx->stat;
1502 }
1503 return GRN_SUCCESS;
1504}
1505
1506#define DB_P(s) ((s) && (s)->header.type == GRN_DB)
1507
1508grn_rc
1509grn_ctx_use(grn_ctx *ctx, grn_obj *db)
1510{
1511 GRN_API_ENTER;
1512 if (db && !DB_P(db)) {
1513 ctx->rc = GRN_INVALID_ARGUMENT;
1514 } else {
1515 if (!ctx->rc) {
1516 ctx->impl->db = db;
1517 if (db) {
1518 grn_obj buf;
1519 GRN_TEXT_INIT(&buf, 0);
1520 grn_obj_get_info(ctx, db, GRN_INFO_ENCODING, &buf);
1521 ctx->encoding = *(grn_encoding *)GRN_BULK_HEAD(&buf);
1522 grn_obj_close(ctx, &buf);
1523 }
1524 }
1525 }
1526 GRN_API_RETURN(ctx->rc);
1527}
1528
1529/* don't handle error inside logger functions */
1530
1531void
1532grn_ctx_log(grn_ctx *ctx, const char *fmt, ...)
1533{
1534 va_list ap;
1535 va_start(ap, fmt);
1536 grn_ctx_logv(ctx, fmt, ap);
1537 va_end(ap);
1538}
1539
1540void
1541grn_ctx_logv(grn_ctx *ctx, const char *fmt, va_list ap)
1542{
1543 char buffer[GRN_CTX_MSGSIZE];
1544 grn_vsnprintf(buffer, GRN_CTX_MSGSIZE, fmt, ap);
1545 grn_strcpy(ctx->errbuf, GRN_CTX_MSGSIZE, buffer);
1546}
1547
1548void
1549grn_assert(grn_ctx *ctx, int cond, const char* file, int line, const char* func)
1550{
1551 if (!cond) {
1552 GRN_LOG(ctx, GRN_LOG_WARNING, "ASSERT fail on %s %s:%d", func, file, line);
1553 }
1554}
1555
1556const char *
1557grn_get_version(void)
1558{
1559 return GRN_VERSION;
1560}
1561
1562const char *
1563grn_get_package(void)
1564{
1565 return PACKAGE;
1566}
1567
1568const char *
1569grn_get_package_label(void)
1570{
1571 return PACKAGE_LABEL;
1572}
1573
1574#if defined(HAVE_SIGNAL_H) && !defined(WIN32)
1575static int segv_received = 0;
1576static void
1577segv_handler(int signal_number, siginfo_t *info, void *context)
1578{
1579 grn_ctx *ctx = &grn_gctx;
1580
1581 if (segv_received) {
1582 GRN_LOG(ctx, GRN_LOG_CRIT, "SEGV received in SEGV handler.");
1583 exit(EXIT_FAILURE);
1584 }
1585 segv_received = 1;
1586
1587 GRN_LOG(ctx, GRN_LOG_CRIT, "-- CRASHED!!! --");
1588#ifdef HAVE_BACKTRACE
1589# define N_TRACE_LEVEL 1024
1590 {
1591 static void *trace[N_TRACE_LEVEL];
1592 int n = backtrace(trace, N_TRACE_LEVEL);
1593 char **symbols = backtrace_symbols(trace, n);
1594 int i;
1595
1596 if (symbols) {
1597 for (i = 0; i < n; i++) {
1598 GRN_LOG(ctx, GRN_LOG_CRIT, "%s", symbols[i]);
1599 }
1600 free(symbols);
1601 }
1602 }
1603#else /* HAVE_BACKTRACE */
1604 GRN_LOG(ctx, GRN_LOG_CRIT, "backtrace() isn't available.");
1605#endif /* HAVE_BACKTRACE */
1606 GRN_LOG(ctx, GRN_LOG_CRIT, "----------------");
1607 abort();
1608}
1609#endif /* defined(HAVE_SIGNAL_H) && !defined(WIN32) */
1610
1611grn_rc
1612grn_set_segv_handler(void)
1613{
1614 grn_rc rc = GRN_SUCCESS;
1615#if defined(HAVE_SIGNAL_H) && !defined(WIN32)
1616 grn_ctx *ctx = &grn_gctx;
1617 struct sigaction action;
1618
1619 sigemptyset(&action.sa_mask);
1620 action.sa_sigaction = segv_handler;
1621 action.sa_flags = SA_SIGINFO | SA_ONSTACK;
1622
1623 if (sigaction(SIGSEGV, &action, NULL)) {
1624 SERR("failed to set SIGSEGV action");
1625 rc = ctx->rc;
1626 };
1627#endif
1628 return rc;
1629}
1630
1631#if defined(HAVE_SIGNAL_H) && !defined(WIN32)
1632static struct sigaction old_int_handler;
1633static void
1634int_handler(int signal_number, siginfo_t *info, void *context)
1635{
1636 grn_gctx.stat = GRN_CTX_QUIT;
1637 sigaction(signal_number, &old_int_handler, NULL);
1638}
1639
1640static struct sigaction old_term_handler;
1641static void
1642term_handler(int signal_number, siginfo_t *info, void *context)
1643{
1644 grn_gctx.stat = GRN_CTX_QUIT;
1645 sigaction(signal_number, &old_term_handler, NULL);
1646}
1647#endif /* defined(HAVE_SIGNAL_H) && !defined(WIN32) */
1648
1649grn_rc
1650grn_set_int_handler(void)
1651{
1652 grn_rc rc = GRN_SUCCESS;
1653#if defined(HAVE_SIGNAL_H) && !defined(WIN32)
1654 grn_ctx *ctx = &grn_gctx;
1655 struct sigaction action;
1656
1657 sigemptyset(&action.sa_mask);
1658 action.sa_sigaction = int_handler;
1659 action.sa_flags = SA_SIGINFO;
1660
1661 if (sigaction(SIGINT, &action, &old_int_handler)) {
1662 SERR("failed to set SIGINT action");
1663 rc = ctx->rc;
1664 }
1665#endif
1666 return rc;
1667}
1668
1669grn_rc
1670grn_set_term_handler(void)
1671{
1672 grn_rc rc = GRN_SUCCESS;
1673#if defined(HAVE_SIGNAL_H) && !defined(WIN32)
1674 grn_ctx *ctx = &grn_gctx;
1675 struct sigaction action;
1676
1677 sigemptyset(&action.sa_mask);
1678 action.sa_sigaction = term_handler;
1679 action.sa_flags = SA_SIGINFO;
1680
1681 if (sigaction(SIGTERM, &action, &old_term_handler)) {
1682 SERR("failed to set SIGTERM action");
1683 rc = ctx->rc;
1684 }
1685#endif
1686 return rc;
1687}
1688
1689void
1690grn_ctx_output_flush(grn_ctx *ctx, int flags)
1691{
1692 if (flags & GRN_CTX_QUIET) {
1693 return;
1694 }
1695 if (!ctx->impl->output.func) {
1696 return;
1697 }
1698 ctx->impl->output.func(ctx, 0, ctx->impl->output.data.ptr);
1699}
1700
1701void
1702grn_ctx_output_array_open(grn_ctx *ctx, const char *name, int nelements)
1703{
1704 grn_output_array_open(ctx,
1705 ctx->impl->output.buf,
1706 ctx->impl->output.type,
1707 name, nelements);
1708}
1709
1710void
1711grn_ctx_output_array_close(grn_ctx *ctx)
1712{
1713 grn_output_array_close(ctx,
1714 ctx->impl->output.buf,
1715 ctx->impl->output.type);
1716}
1717
1718void
1719grn_ctx_output_map_open(grn_ctx *ctx, const char *name, int nelements)
1720{
1721 grn_output_map_open(ctx,
1722 ctx->impl->output.buf,
1723 ctx->impl->output.type,
1724 name, nelements);
1725}
1726
1727void
1728grn_ctx_output_map_close(grn_ctx *ctx)
1729{
1730 grn_output_map_close(ctx,
1731 ctx->impl->output.buf,
1732 ctx->impl->output.type);
1733}
1734
1735void
1736grn_ctx_output_null(grn_ctx *ctx)
1737{
1738 grn_output_null(ctx,
1739 ctx->impl->output.buf,
1740 ctx->impl->output.type);
1741}
1742
1743void
1744grn_ctx_output_int32(grn_ctx *ctx, int value)
1745{
1746 grn_output_int32(ctx,
1747 ctx->impl->output.buf,
1748 ctx->impl->output.type,
1749 value);
1750}
1751
1752void
1753grn_ctx_output_int64(grn_ctx *ctx, int64_t value)
1754{
1755 grn_output_int64(ctx,
1756 ctx->impl->output.buf,
1757 ctx->impl->output.type,
1758 value);
1759}
1760
1761void
1762grn_ctx_output_uint64(grn_ctx *ctx, uint64_t value)
1763{
1764 grn_output_uint64(ctx,
1765 ctx->impl->output.buf,
1766 ctx->impl->output.type,
1767 value);
1768}
1769
1770void
1771grn_ctx_output_float(grn_ctx *ctx, double value)
1772{
1773 grn_output_float(ctx,
1774 ctx->impl->output.buf,
1775 ctx->impl->output.type,
1776 value);
1777}
1778
1779void
1780grn_ctx_output_cstr(grn_ctx *ctx, const char *value)
1781{
1782 grn_output_cstr(ctx,
1783 ctx->impl->output.buf,
1784 ctx->impl->output.type,
1785 value);
1786}
1787
1788void
1789grn_ctx_output_str(grn_ctx *ctx, const char *value, unsigned int value_len)
1790{
1791 grn_output_str(ctx,
1792 ctx->impl->output.buf,
1793 ctx->impl->output.type,
1794 value, value_len);
1795}
1796
1797void
1798grn_ctx_output_bool(grn_ctx *ctx, grn_bool value)
1799{
1800 grn_output_bool(ctx,
1801 ctx->impl->output.buf,
1802 ctx->impl->output.type,
1803 value);
1804}
1805
1806void
1807grn_ctx_output_obj(grn_ctx *ctx, grn_obj *value, grn_obj_format *format)
1808{
1809 grn_output_obj(ctx,
1810 ctx->impl->output.buf,
1811 ctx->impl->output.type,
1812 value, format);
1813}
1814
1815void
1816grn_ctx_output_result_set_open(grn_ctx *ctx,
1817 grn_obj *result_set,
1818 grn_obj_format *format,
1819 uint32_t n_additional_elements)
1820{
1821 grn_output_result_set_open(ctx,
1822 ctx->impl->output.buf,
1823 ctx->impl->output.type,
1824 result_set,
1825 format,
1826 n_additional_elements);
1827}
1828
1829void
1830grn_ctx_output_result_set_close(grn_ctx *ctx,
1831 grn_obj *result_set,
1832 grn_obj_format *format)
1833{
1834 grn_output_result_set_close(ctx,
1835 ctx->impl->output.buf,
1836 ctx->impl->output.type,
1837 result_set,
1838 format);
1839}
1840
1841void
1842grn_ctx_output_result_set(grn_ctx *ctx,
1843 grn_obj *result_set,
1844 grn_obj_format *format)
1845{
1846 grn_output_result_set(ctx,
1847 ctx->impl->output.buf,
1848 ctx->impl->output.type,
1849 result_set,
1850 format);
1851}
1852
1853void
1854grn_ctx_output_table_columns(grn_ctx *ctx, grn_obj *table,
1855 grn_obj_format *format)
1856{
1857 grn_output_table_columns(ctx,
1858 ctx->impl->output.buf,
1859 ctx->impl->output.type,
1860 table,
1861 format);
1862}
1863
1864void
1865grn_ctx_output_table_records(grn_ctx *ctx, grn_obj *table,
1866 grn_obj_format *format)
1867{
1868 grn_output_table_records(ctx,
1869 ctx->impl->output.buf,
1870 ctx->impl->output.type,
1871 table,
1872 format);
1873}
1874