diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:07:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:07:14 +0000 |
commit | a175314c3e5827eb193872241446f2f8f5c9d33c (patch) | |
tree | cd3d60ca99ae00829c52a6ca79150a5b6e62528b /storage/mroonga/vendor/groonga/lib/proc | |
parent | Initial commit. (diff) | |
download | mariadb-10.5-a175314c3e5827eb193872241446f2f8f5c9d33c.tar.xz mariadb-10.5-a175314c3e5827eb193872241446f2f8f5c9d33c.zip |
Adding upstream version 1:10.5.12.upstream/1%10.5.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
20 files changed, 16403 insertions, 0 deletions
diff --git a/storage/mroonga/vendor/groonga/lib/proc.c b/storage/mroonga/vendor/groonga/lib/proc.c new file mode 100644 index 00000000..8ed39961 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc.c @@ -0,0 +1,4211 @@ +/* -*- 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_proc.h" +#include "grn_ctx.h" +#include "grn_ii.h" +#include "grn_db.h" +#include "grn_util.h" +#include "grn_output.h" +#include "grn_pat.h" +#include "grn_geo.h" +#include "grn_expr.h" +#include "grn_cache.h" +#include "grn_load.h" + +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/stat.h> + +#ifdef WIN32 +# include <io.h> +# include <share.h> +#endif /* WIN32 */ + +#ifndef O_NOFOLLOW +#define O_NOFOLLOW 0 +#endif + +/**** globals for procs ****/ +const char *grn_document_root = NULL; + +#define VAR GRN_PROC_GET_VAR_BY_OFFSET + +static double grn_between_too_many_index_match_ratio = 0.01; +static double grn_in_values_too_many_index_match_ratio = 0.01; + +void +grn_proc_init_from_env(void) +{ + { + char grn_between_too_many_index_match_ratio_env[GRN_ENV_BUFFER_SIZE]; + grn_getenv("GRN_BETWEEN_TOO_MANY_INDEX_MATCH_RATIO", + grn_between_too_many_index_match_ratio_env, + GRN_ENV_BUFFER_SIZE); + if (grn_between_too_many_index_match_ratio_env[0]) { + grn_between_too_many_index_match_ratio = + atof(grn_between_too_many_index_match_ratio_env); + } + } + + { + char grn_in_values_too_many_index_match_ratio_env[GRN_ENV_BUFFER_SIZE]; + grn_getenv("GRN_IN_VALUES_TOO_MANY_INDEX_MATCH_RATIO", + grn_in_values_too_many_index_match_ratio_env, + GRN_ENV_BUFFER_SIZE); + if (grn_in_values_too_many_index_match_ratio_env[0]) { + grn_in_values_too_many_index_match_ratio = + atof(grn_in_values_too_many_index_match_ratio_env); + } + } +} + +/* bulk must be initialized grn_bulk or grn_msg */ +static int +grn_bulk_put_from_file(grn_ctx *ctx, grn_obj *bulk, const char *path) +{ + /* FIXME: implement more smartly with grn_bulk */ + int fd, ret = 0; + struct stat stat; + grn_open(fd, path, O_RDONLY|O_NOFOLLOW|GRN_OPEN_FLAG_BINARY); + if (fd == -1) { + switch (errno) { + case EACCES : + ERR(GRN_OPERATION_NOT_PERMITTED, "request is not allowed: <%s>", path); + break; + case ENOENT : + ERR(GRN_NO_SUCH_FILE_OR_DIRECTORY, "no such file: <%s>", path); + break; +#ifndef WIN32 + case ELOOP : + ERR(GRN_NO_SUCH_FILE_OR_DIRECTORY, + "symbolic link is not allowed: <%s>", path); + break; +#endif /* WIN32 */ + default : + ERRNO_ERR("failed to open file: <%s>", path); + break; + } + return 0; + } + if (fstat(fd, &stat) != -1) { + char *buf, *bp; + off_t rest = stat.st_size; + if ((buf = GRN_MALLOC(rest))) { + ssize_t ss; + for (bp = buf; rest; rest -= ss, bp += ss) { + if ((ss = grn_read(fd, bp, rest)) == -1) { goto exit; } + } + GRN_TEXT_PUT(ctx, bulk, buf, stat.st_size); + ret = 1; + } + GRN_FREE(buf); + } else { + ERR(GRN_INVALID_ARGUMENT, "cannot stat file: <%s>", path); + } +exit : + grn_close(fd); + return ret; +} + +#ifdef stat +# undef stat +#endif /* stat */ + +/**** procs ****/ + +static grn_obj * +proc_load(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_load_input input; + + input.type = grn_plugin_proc_get_var_content_type(ctx, + user_data, + "input_type", + -1, + GRN_CONTENT_JSON); +#define INIT_STRING_ARGUMENT(member_name, arg_name) \ + input.member_name.value = \ + grn_plugin_proc_get_var_string(ctx, \ + user_data, \ + arg_name, \ + -1, \ + &(input.member_name.length)) + + INIT_STRING_ARGUMENT(table, "table"); + INIT_STRING_ARGUMENT(columns, "columns"); + INIT_STRING_ARGUMENT(values, "values"); + INIT_STRING_ARGUMENT(if_exists, "ifexists"); + INIT_STRING_ARGUMENT(each, "each"); + +#undef INIT_STRING_ARGUMENT + + input.output_ids = grn_plugin_proc_get_var_bool(ctx, + user_data, + "output_ids", -1, + GRN_FALSE); + input.output_errors = grn_plugin_proc_get_var_bool(ctx, + user_data, + "output_errors", -1, + GRN_FALSE); + input.emit_level = 1; + + grn_load_internal(ctx, &input); + if (ctx->rc == GRN_CANCEL) { + ctx->impl->loader.stat = GRN_LOADER_END; + ctx->impl->loader.rc = GRN_SUCCESS; + } + if (ctx->impl->loader.stat != GRN_LOADER_END && + !(ctx->impl->command.flags & GRN_CTX_TAIL)) { + grn_obj *command = grn_proc_get_info(ctx, user_data, NULL, NULL, NULL); + grn_ctx_set_keep_command(ctx, command); + } else { + if (ctx->impl->loader.rc != GRN_SUCCESS) { + ctx->rc = ctx->impl->loader.rc; + grn_strcpy(ctx->errbuf, GRN_CTX_MSGSIZE, ctx->impl->loader.errbuf); + } + if (grn_ctx_get_command_version(ctx) >= GRN_COMMAND_VERSION_3) { + int n_elements = 1; + if (ctx->impl->loader.output_ids) { + n_elements++; + } + if (ctx->impl->loader.output_errors) { + n_elements++; + } + GRN_OUTPUT_MAP_OPEN("result", n_elements); + GRN_OUTPUT_CSTR("n_loaded_records"); + GRN_OUTPUT_INT64(ctx->impl->loader.nrecords); + if (ctx->impl->loader.output_ids) { + grn_obj *ids = &(ctx->impl->loader.ids); + int i, n_ids; + + GRN_OUTPUT_CSTR("loaded_ids"); + n_ids = GRN_BULK_VSIZE(ids) / sizeof(uint32_t); + GRN_OUTPUT_ARRAY_OPEN("loaded_ids", n_ids); + for (i = 0; i < n_ids; i++) { + GRN_OUTPUT_UINT64(GRN_UINT32_VALUE_AT(ids, i)); + } + GRN_OUTPUT_ARRAY_CLOSE(); + } + if (ctx->impl->loader.output_errors) { + grn_obj *return_codes = &(ctx->impl->loader.return_codes); + grn_obj *error_messages = &(ctx->impl->loader.error_messages); + int i, n; + + GRN_OUTPUT_CSTR("errors"); + n = GRN_BULK_VSIZE(return_codes) / sizeof(int32_t); + GRN_OUTPUT_ARRAY_OPEN("errors", n); + for (i = 0; i < n; i++) { + const char *message; + unsigned int message_size; + + message_size = grn_vector_get_element(ctx, + error_messages, + i, + &message, + NULL, + NULL); + + GRN_OUTPUT_MAP_OPEN("error", 2); + GRN_OUTPUT_CSTR("return_code"); + GRN_OUTPUT_INT64(GRN_INT32_VALUE_AT(return_codes, i)); + GRN_OUTPUT_CSTR("message"); + if (message_size == 0) { + GRN_OUTPUT_NULL(); + } else { + GRN_OUTPUT_STR(message, message_size); + } + GRN_OUTPUT_MAP_CLOSE(); + } + GRN_OUTPUT_ARRAY_CLOSE(); + } + GRN_OUTPUT_MAP_CLOSE(); + } else { + GRN_OUTPUT_INT64(ctx->impl->loader.nrecords); + } + if (ctx->impl->loader.table) { + grn_db_touch(ctx, DB_OBJ(ctx->impl->loader.table)->db); + } + grn_ctx_loader_clear(ctx); + } + return NULL; +} + +static grn_obj * +proc_status(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_timeval now; + grn_cache *cache; + grn_cache_statistics statistics; + + grn_timeval_now(ctx, &now); + cache = grn_cache_current_get(ctx); + grn_cache_get_statistics(ctx, cache, &statistics); + GRN_OUTPUT_MAP_OPEN("RESULT", 10); + GRN_OUTPUT_CSTR("alloc_count"); + GRN_OUTPUT_INT32(grn_alloc_count()); + GRN_OUTPUT_CSTR("starttime"); + GRN_OUTPUT_INT32(grn_starttime.tv_sec); + GRN_OUTPUT_CSTR("start_time"); + GRN_OUTPUT_INT32(grn_starttime.tv_sec); + GRN_OUTPUT_CSTR("uptime"); + GRN_OUTPUT_INT32(now.tv_sec - grn_starttime.tv_sec); + GRN_OUTPUT_CSTR("version"); + GRN_OUTPUT_CSTR(grn_get_version()); + GRN_OUTPUT_CSTR("n_queries"); + GRN_OUTPUT_INT64(statistics.nfetches); + GRN_OUTPUT_CSTR("cache_hit_rate"); + if (statistics.nfetches == 0) { + GRN_OUTPUT_FLOAT(0.0); + } else { + double cache_hit_rate; + cache_hit_rate = (double)statistics.nhits / (double)statistics.nfetches; + GRN_OUTPUT_FLOAT(cache_hit_rate * 100.0); + } + GRN_OUTPUT_CSTR("command_version"); + GRN_OUTPUT_INT32(grn_ctx_get_command_version(ctx)); + GRN_OUTPUT_CSTR("default_command_version"); + GRN_OUTPUT_INT32(grn_get_default_command_version()); + GRN_OUTPUT_CSTR("max_command_version"); + GRN_OUTPUT_INT32(GRN_COMMAND_VERSION_MAX); + GRN_OUTPUT_MAP_CLOSE(); + +#ifdef USE_MEMORY_DEBUG + grn_alloc_info_dump(&grn_gctx); +#endif /* USE_MEMORY_DEBUG */ + + return NULL; +} + +#define GRN_STRLEN(s) ((s) ? strlen(s) : 0) + +void +grn_proc_output_object_name(grn_ctx *ctx, grn_obj *obj) +{ + grn_obj bulk; + int name_len; + char name[GRN_TABLE_MAX_KEY_SIZE]; + + if (obj) { + GRN_TEXT_INIT(&bulk, GRN_OBJ_DO_SHALLOW_COPY); + name_len = grn_obj_name(ctx, obj, name, GRN_TABLE_MAX_KEY_SIZE); + GRN_TEXT_SET(ctx, &bulk, name, name_len); + } else { + GRN_VOID_INIT(&bulk); + } + + GRN_OUTPUT_OBJ(&bulk, NULL); + GRN_OBJ_FIN(ctx, &bulk); +} + +void +grn_proc_output_object_id_name(grn_ctx *ctx, grn_id id) +{ + grn_obj *obj = NULL; + + if (id != GRN_ID_NIL) { + obj = grn_ctx_at(ctx, id); + } + + grn_proc_output_object_name(ctx, obj); +} + +static grn_obj * +proc_missing(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + uint32_t plen; + grn_obj *outbuf = ctx->impl->output.buf; + static int grn_document_root_len = -1; + if (!grn_document_root) { return NULL; } + if (grn_document_root_len < 0) { + size_t l; + if ((l = strlen(grn_document_root)) > PATH_MAX) { + return NULL; + } + grn_document_root_len = (int)l; + if (l > 0 && grn_document_root[l - 1] == '/') { grn_document_root_len--; } + } + if ((plen = GRN_TEXT_LEN(VAR(0))) + grn_document_root_len < PATH_MAX) { + char path[PATH_MAX]; + grn_memcpy(path, grn_document_root, grn_document_root_len); + path[grn_document_root_len] = '/'; + grn_str_url_path_normalize(ctx, + GRN_TEXT_VALUE(VAR(0)), + GRN_TEXT_LEN(VAR(0)), + path + grn_document_root_len + 1, + PATH_MAX - grn_document_root_len - 1); + grn_bulk_put_from_file(ctx, outbuf, path); + } else { + uint32_t abbrlen = 32; + ERR(GRN_INVALID_ARGUMENT, + "too long path name: <%s/%.*s...> %u(%u)", + grn_document_root, + abbrlen < plen ? abbrlen : plen, GRN_TEXT_VALUE(VAR(0)), + plen + grn_document_root_len, PATH_MAX); + } + return NULL; +} + +static grn_obj * +proc_quit(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + ctx->stat = GRN_CTX_QUITTING; + GRN_OUTPUT_BOOL(!ctx->rc); + return NULL; +} + +static grn_obj * +proc_shutdown(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + const char *mode; + size_t mode_size; + + mode = grn_plugin_proc_get_var_string(ctx, user_data, "mode", -1, &mode_size); +#define MODE_EQUAL(name) \ + (mode_size == strlen(name) && memcmp(mode, name, mode_size) == 0) + if (mode_size == 0 || MODE_EQUAL("graceful")) { + /* Do nothing. This is the default. */ + } else if (MODE_EQUAL("immediate")) { + grn_request_canceler_cancel_all(); + if (ctx->rc == GRN_INTERRUPTED_FUNCTION_CALL) { + ctx->rc = GRN_SUCCESS; + } + } else { + ERR(GRN_INVALID_ARGUMENT, + "[shutdown] mode must be <graceful> or <immediate>: <%.*s>", + (int)mode_size, mode); + } +#undef MODE_EQUAL + + if (ctx->rc == GRN_SUCCESS) { + grn_gctx.stat = GRN_CTX_QUIT; + ctx->stat = GRN_CTX_QUITTING; + } + + GRN_OUTPUT_BOOL(!ctx->rc); + + return NULL; +} + +static grn_obj * +proc_defrag(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *obj; + int olen, threshold; + olen = GRN_TEXT_LEN(VAR(0)); + + if (olen) { + obj = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(0)), olen); + } else { + obj = ctx->impl->db; + } + + threshold = GRN_TEXT_LEN(VAR(1)) + ? grn_atoi(GRN_TEXT_VALUE(VAR(1)), GRN_BULK_CURR(VAR(1)), NULL) + : 0; + + if (obj) { + grn_obj_defrag(ctx, obj, threshold); + } else { + ERR(GRN_INVALID_ARGUMENT, "defrag object not found"); + } + GRN_OUTPUT_BOOL(!ctx->rc); + return NULL; +} + +static grn_obj * +proc_log_level(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *level_name = VAR(0); + if (GRN_TEXT_LEN(level_name) > 0) { + grn_log_level max_level; + GRN_TEXT_PUTC(ctx, level_name, '\0'); + if (grn_log_level_parse(GRN_TEXT_VALUE(level_name), &max_level)) { + grn_logger_set_max_level(ctx, max_level); + } else { + ERR(GRN_INVALID_ARGUMENT, + "invalid log level: <%s>", GRN_TEXT_VALUE(level_name)); + } + } else { + ERR(GRN_INVALID_ARGUMENT, "log level is missing"); + } + GRN_OUTPUT_BOOL(!ctx->rc); + return NULL; +} + +static grn_obj * +proc_log_put(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *level_name = VAR(0); + grn_obj *message = VAR(1); + if (GRN_TEXT_LEN(level_name) > 0) { + grn_log_level level; + GRN_TEXT_PUTC(ctx, level_name, '\0'); + if (grn_log_level_parse(GRN_TEXT_VALUE(level_name), &level)) { + GRN_LOG(ctx, level, "%.*s", + (int)GRN_TEXT_LEN(message), + GRN_TEXT_VALUE(message)); + } else { + ERR(GRN_INVALID_ARGUMENT, + "invalid log level: <%s>", GRN_TEXT_VALUE(level_name)); + } + } else { + ERR(GRN_INVALID_ARGUMENT, "log level is missing"); + } + GRN_OUTPUT_BOOL(!ctx->rc); + return NULL; +} + +static grn_obj * +proc_log_reopen(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_log_reopen(ctx); + GRN_OUTPUT_BOOL(!ctx->rc); + return NULL; +} + +static grn_rc +proc_delete_validate_selector(grn_ctx *ctx, grn_obj *table, grn_obj *table_name, + grn_obj *key, grn_obj *id, grn_obj *filter) +{ + grn_rc rc = GRN_SUCCESS; + + if (!table) { + rc = GRN_INVALID_ARGUMENT; + ERR(rc, + "[table][record][delete] table doesn't exist: <%.*s>", + (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name)); + return rc; + } + + if (GRN_TEXT_LEN(key) == 0 && + GRN_TEXT_LEN(id) == 0 && + GRN_TEXT_LEN(filter) == 0) { + rc = GRN_INVALID_ARGUMENT; + ERR(rc, + "[table][record][delete] either key, id or filter must be specified: " + "table: <%.*s>", + (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name)); + return rc; + } + + if (GRN_TEXT_LEN(key) && GRN_TEXT_LEN(id) && GRN_TEXT_LEN(filter)) { + rc = GRN_INVALID_ARGUMENT; + ERR(rc, + "[table][record][delete] " + "record selector must be one of key, id and filter: " + "table: <%.*s>, key: <%.*s>, id: <%.*s>, filter: <%.*s>", + (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name), + (int)GRN_TEXT_LEN(key), GRN_TEXT_VALUE(key), + (int)GRN_TEXT_LEN(id), GRN_TEXT_VALUE(id), + (int)GRN_TEXT_LEN(filter), GRN_TEXT_VALUE(filter)); + return rc; + } + + if (GRN_TEXT_LEN(key) && GRN_TEXT_LEN(id) && GRN_TEXT_LEN(filter) == 0) { + rc = GRN_INVALID_ARGUMENT; + ERR(rc, + "[table][record][delete] " + "can't use both key and id: table: <%.*s>, key: <%.*s>, id: <%.*s>", + (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name), + (int)GRN_TEXT_LEN(key), GRN_TEXT_VALUE(key), + (int)GRN_TEXT_LEN(id), GRN_TEXT_VALUE(id)); + return rc; + } + + if (GRN_TEXT_LEN(key) && GRN_TEXT_LEN(id) == 0 && GRN_TEXT_LEN(filter)) { + rc = GRN_INVALID_ARGUMENT; + ERR(rc, + "[table][record][delete] " + "can't use both key and filter: " + "table: <%.*s>, key: <%.*s>, filter: <%.*s>", + (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name), + (int)GRN_TEXT_LEN(key), GRN_TEXT_VALUE(key), + (int)GRN_TEXT_LEN(filter), GRN_TEXT_VALUE(filter)); + return rc; + } + + if (GRN_TEXT_LEN(key) == 0 && GRN_TEXT_LEN(id) && GRN_TEXT_LEN(filter)) { + rc = GRN_INVALID_ARGUMENT; + ERR(rc, + "[table][record][delete] " + "can't use both id and filter: " + "table: <%.*s>, id: <%.*s>, filter: <%.*s>", + (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name), + (int)GRN_TEXT_LEN(id), GRN_TEXT_VALUE(id), + (int)GRN_TEXT_LEN(filter), GRN_TEXT_VALUE(filter)); + return rc; + } + + return rc; +} + +static grn_obj * +proc_delete(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_rc rc = GRN_INVALID_ARGUMENT; + grn_obj *table_name = VAR(0); + grn_obj *key = VAR(1); + grn_obj *id = VAR(2); + grn_obj *filter = VAR(3); + grn_obj *table = NULL; + + if (GRN_TEXT_LEN(table_name) == 0) { + rc = GRN_INVALID_ARGUMENT; + ERR(rc, "[table][record][delete] table name isn't specified"); + goto exit; + } + + table = grn_ctx_get(ctx, + GRN_TEXT_VALUE(table_name), + GRN_TEXT_LEN(table_name)); + rc = proc_delete_validate_selector(ctx, table, table_name, key, id, filter); + if (rc != GRN_SUCCESS) { goto exit; } + + if (GRN_TEXT_LEN(key)) { + grn_obj casted_key; + if (key->header.domain != table->header.domain) { + GRN_OBJ_INIT(&casted_key, GRN_BULK, 0, table->header.domain); + grn_obj_cast(ctx, key, &casted_key, GRN_FALSE); + key = &casted_key; + } + if (ctx->rc) { + rc = ctx->rc; + } else { + rc = grn_table_delete(ctx, table, GRN_BULK_HEAD(key), GRN_BULK_VSIZE(key)); + if (key == &casted_key) { + GRN_OBJ_FIN(ctx, &casted_key); + } + } + } else if (GRN_TEXT_LEN(id)) { + const char *end; + grn_id parsed_id = grn_atoui(GRN_TEXT_VALUE(id), GRN_BULK_CURR(id), &end); + if (end == GRN_BULK_CURR(id)) { + rc = grn_table_delete_by_id(ctx, table, parsed_id); + } else { + rc = GRN_INVALID_ARGUMENT; + ERR(rc, + "[table][record][delete] id should be number: " + "table: <%.*s>, id: <%.*s>, detail: <%.*s|%c|%.*s>", + (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name), + (int)GRN_TEXT_LEN(id), GRN_TEXT_VALUE(id), + (int)(end - GRN_TEXT_VALUE(id)), GRN_TEXT_VALUE(id), + end[0], + (int)(GRN_TEXT_VALUE(id) - end - 1), end + 1); + } + } else if (GRN_TEXT_LEN(filter)) { + grn_obj *cond, *v; + + GRN_EXPR_CREATE_FOR_QUERY(ctx, table, cond, v); + grn_expr_parse(ctx, cond, + GRN_TEXT_VALUE(filter), + GRN_TEXT_LEN(filter), + NULL, GRN_OP_MATCH, GRN_OP_AND, + GRN_EXPR_SYNTAX_SCRIPT); + if (ctx->rc) { + rc = ctx->rc; + ERR(rc, + "[table][record][delete] failed to parse filter: " + "table: <%.*s>, filter: <%.*s>, detail: <%s>", + (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name), + (int)GRN_TEXT_LEN(filter), GRN_TEXT_VALUE(filter), + ctx->errbuf); + } else { + grn_obj *records; + + records = grn_table_select(ctx, table, cond, NULL, GRN_OP_OR); + if (records) { + GRN_TABLE_EACH_BEGIN(ctx, records, cursor, result_id) { + void *key; + grn_id id; + grn_rc sub_rc; + + if (grn_table_cursor_get_key(ctx, cursor, &key) == 0) { + continue; + } + + id = *(grn_id *)key; + sub_rc = grn_table_delete_by_id(ctx, table, id); + if (rc == GRN_SUCCESS) { + rc = sub_rc; + } + if (ctx->rc == GRN_CANCEL) { + break; + } + if (ctx->rc != GRN_SUCCESS) { + ERRCLR(ctx); + } + } GRN_TABLE_EACH_END(ctx, cursor); + grn_obj_unlink(ctx, records); + } + } + grn_obj_unlink(ctx, cond); + } + +exit : + if (table) { + grn_obj_unlink(ctx, table); + } + GRN_OUTPUT_BOOL(rc == GRN_SUCCESS); + return NULL; +} + +grn_bool +grn_proc_option_value_bool(grn_ctx *ctx, + grn_obj *option, + grn_bool default_value) +{ + const char *value; + size_t value_length; + + if (!option) { + return default_value; + } + + value = GRN_TEXT_VALUE(option); + value_length = GRN_TEXT_LEN(option); + + if (value_length == 0) { + return default_value; + } + + if (value_length == strlen("yes") && + strncmp(value, "yes", value_length) == 0) { + return GRN_TRUE; + } else if (value_length == strlen("no") && + strncmp(value, "no", value_length) == 0) { + return GRN_FALSE; + } else { + return default_value; + } +} + +int32_t +grn_proc_option_value_int32(grn_ctx *ctx, + grn_obj *option, + int32_t default_value) +{ + const char *value; + size_t value_length; + int32_t int32_value; + const char *rest; + + if (!option) { + return default_value; + } + + value = GRN_TEXT_VALUE(option); + value_length = GRN_TEXT_LEN(option); + + if (value_length == 0) { + return default_value; + } + + int32_value = grn_atoi(value, value + value_length, &rest); + if (rest == value + value_length) { + return int32_value; + } else { + return default_value; + } +} + +const char * +grn_proc_option_value_string(grn_ctx *ctx, + grn_obj *option, + size_t *size) +{ + const char *value; + size_t value_length; + + if (!option) { + if (size) { + *size = 0; + } + return NULL; + } + + value = GRN_TEXT_VALUE(option); + value_length = GRN_TEXT_LEN(option); + + if (size) { + *size = value_length; + } + + if (value_length == 0) { + return NULL; + } else { + return value; + } +} + +grn_content_type +grn_proc_option_value_content_type(grn_ctx *ctx, + grn_obj *option, + grn_content_type default_value) +{ + if (!option) { + return default_value; + } + + return grn_content_type_parse(ctx, option, default_value); +} + +static grn_obj * +proc_cache_limit(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_cache *cache; + unsigned int current_max_n_entries; + + cache = grn_cache_current_get(ctx); + current_max_n_entries = grn_cache_get_max_n_entries(ctx, cache); + if (GRN_TEXT_LEN(VAR(0))) { + const char *rest; + uint32_t max = grn_atoui(GRN_TEXT_VALUE(VAR(0)), + GRN_BULK_CURR(VAR(0)), &rest); + if (GRN_BULK_CURR(VAR(0)) == rest) { + grn_cache_set_max_n_entries(ctx, cache, max); + } else { + ERR(GRN_INVALID_ARGUMENT, + "max value is invalid unsigned integer format: <%.*s>", + (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0))); + } + } + if (ctx->rc == GRN_SUCCESS) { + GRN_OUTPUT_INT64(current_max_n_entries); + } + return NULL; +} + +static grn_obj * +proc_register(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + if (GRN_TEXT_LEN(VAR(0))) { + const char *name; + GRN_TEXT_PUTC(ctx, VAR(0), '\0'); + name = GRN_TEXT_VALUE(VAR(0)); + grn_plugin_register(ctx, name); + } else { + ERR(GRN_INVALID_ARGUMENT, "path is required"); + } + GRN_OUTPUT_BOOL(!ctx->rc); + return NULL; +} + +void grn_ii_buffer_check(grn_ctx *ctx, grn_ii *ii, uint32_t seg); + +static grn_obj * +proc_check(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *obj = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(0)), GRN_TEXT_LEN(VAR(0))); + if (!obj) { + ERR(GRN_INVALID_ARGUMENT, + "no such object: <%.*s>", (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0))); + GRN_OUTPUT_BOOL(!ctx->rc); + } else { + switch (obj->header.type) { + case GRN_DB : + GRN_OUTPUT_BOOL(!ctx->rc); + break; + case GRN_TABLE_PAT_KEY : + grn_pat_check(ctx, (grn_pat *)obj); + break; + case GRN_TABLE_HASH_KEY : + grn_hash_check(ctx, (grn_hash *)obj); + break; + case GRN_TABLE_DAT_KEY : + case GRN_TABLE_NO_KEY : + case GRN_COLUMN_FIX_SIZE : + GRN_OUTPUT_BOOL(!ctx->rc); + break; + case GRN_COLUMN_VAR_SIZE : + grn_ja_check(ctx, (grn_ja *)obj); + break; + case GRN_COLUMN_INDEX : + { + grn_ii *ii = (grn_ii *)obj; + struct grn_ii_header *h = ii->header; + char buf[8]; + GRN_OUTPUT_ARRAY_OPEN("RESULT", 8); + { + uint32_t i, j, g =0, a = 0, b = 0; + uint32_t max = 0; + for (i = h->bgqtail; i != h->bgqhead; i = ((i + 1) & (GRN_II_BGQSIZE - 1))) { + j = h->bgqbody[i]; + g++; + if (j > max) { max = j; } + } + for (i = 0; i < GRN_II_MAX_LSEG; i++) { + j = h->binfo[i]; + if (j != GRN_II_PSEG_NOT_ASSIGNED) { + if (j > max) { max = j; } + b++; + } + } + for (i = 0; i < GRN_II_MAX_LSEG; i++) { + j = h->ainfo[i]; + if (j != GRN_II_PSEG_NOT_ASSIGNED) { + if (j > max) { max = j; } + a++; + } + } + GRN_OUTPUT_MAP_OPEN("SUMMARY", 12); + GRN_OUTPUT_CSTR("flags"); + grn_itoh(h->flags, buf, 8); + GRN_OUTPUT_STR(buf, 8); + GRN_OUTPUT_CSTR("max sid"); + GRN_OUTPUT_INT64(h->smax); + GRN_OUTPUT_CSTR("number of garbage segments"); + GRN_OUTPUT_INT64(g); + GRN_OUTPUT_CSTR("number of array segments"); + GRN_OUTPUT_INT64(a); + GRN_OUTPUT_CSTR("max id of array segment"); + GRN_OUTPUT_INT64(h->amax); + GRN_OUTPUT_CSTR("number of buffer segments"); + GRN_OUTPUT_INT64(b); + GRN_OUTPUT_CSTR("max id of buffer segment"); + GRN_OUTPUT_INT64(h->bmax); + GRN_OUTPUT_CSTR("max id of physical segment in use"); + GRN_OUTPUT_INT64(max); + GRN_OUTPUT_CSTR("number of unmanaged segments"); + GRN_OUTPUT_INT64(h->pnext - a - b - g); + GRN_OUTPUT_CSTR("total chunk size"); + GRN_OUTPUT_INT64(h->total_chunk_size); + for (max = 0, i = 0; i < (GRN_II_MAX_CHUNK >> 3); i++) { + if ((j = h->chunks[i])) { + int k; + for (k = 0; k < 8; k++) { + if ((j & (1 << k))) { max = (i << 3) + j; } + } + } + } + GRN_OUTPUT_CSTR("max id of chunk segments in use"); + GRN_OUTPUT_INT64(max); + GRN_OUTPUT_CSTR("number of garbage chunk"); + GRN_OUTPUT_ARRAY_OPEN("NGARBAGES", GRN_II_N_CHUNK_VARIATION); + for (i = 0; i <= GRN_II_N_CHUNK_VARIATION; i++) { + GRN_OUTPUT_INT64(h->ngarbages[i]); + } + GRN_OUTPUT_ARRAY_CLOSE(); + GRN_OUTPUT_MAP_CLOSE(); + for (i = 0; i < GRN_II_MAX_LSEG; i++) { + if (h->binfo[i] < 0x20000) { grn_ii_buffer_check(ctx, ii, i); } + } + } + GRN_OUTPUT_ARRAY_CLOSE(); + } + break; + } + } + return NULL; +} + +static grn_obj * +proc_truncate(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + const char *target_name; + int target_name_len; + + target_name_len = GRN_TEXT_LEN(VAR(0)); + if (target_name_len > 0) { + target_name = GRN_TEXT_VALUE(VAR(0)); + } else { + target_name_len = GRN_TEXT_LEN(VAR(1)); + if (target_name_len == 0) { + ERR(GRN_INVALID_ARGUMENT, "[truncate] table name is missing"); + goto exit; + } + target_name = GRN_TEXT_VALUE(VAR(1)); + } + + { + grn_obj *target = grn_ctx_get(ctx, target_name, target_name_len); + if (!target) { + ERR(GRN_INVALID_ARGUMENT, + "[truncate] no such target: <%.*s>", target_name_len, target_name); + goto exit; + } + + switch (target->header.type) { + case GRN_TABLE_HASH_KEY : + case GRN_TABLE_PAT_KEY : + case GRN_TABLE_DAT_KEY : + case GRN_TABLE_NO_KEY : + grn_table_truncate(ctx, target); + break; + case GRN_COLUMN_FIX_SIZE : + case GRN_COLUMN_VAR_SIZE : + case GRN_COLUMN_INDEX : + grn_column_truncate(ctx, target); + break; + default: + { + grn_obj buffer; + GRN_TEXT_INIT(&buffer, 0); + grn_inspect(ctx, &buffer, target); + ERR(GRN_INVALID_ARGUMENT, + "[truncate] not a table nor column object: <%.*s>", + (int)GRN_TEXT_LEN(&buffer), GRN_TEXT_VALUE(&buffer)); + GRN_OBJ_FIN(ctx, &buffer); + } + break; + } + } + +exit : + GRN_OUTPUT_BOOL(!ctx->rc); + return NULL; +} + +static int +parse_normalize_flags(grn_ctx *ctx, grn_obj *flag_names) +{ + int flags = 0; + const char *names, *names_end; + int length; + + names = GRN_TEXT_VALUE(flag_names); + length = GRN_TEXT_LEN(flag_names); + names_end = names + length; + while (names < names_end) { + if (*names == '|' || *names == ' ') { + names += 1; + continue; + } + +#define CHECK_FLAG(name)\ + if (((unsigned long) (names_end - names) >= (unsigned long) (sizeof(#name) - 1)) && \ + (!memcmp(names, #name, sizeof(#name) - 1))) {\ + flags |= GRN_STRING_ ## name;\ + names += sizeof(#name) - 1;\ + continue;\ + } + + CHECK_FLAG(REMOVE_BLANK); + CHECK_FLAG(WITH_TYPES); + CHECK_FLAG(WITH_CHECKS); + CHECK_FLAG(REMOVE_TOKENIZED_DELIMITER); + +#define GRN_STRING_NONE 0 + CHECK_FLAG(NONE); +#undef GRN_STRING_NONE + + ERR(GRN_INVALID_ARGUMENT, "[normalize] invalid flag: <%.*s>", + (int)(names_end - names), names); + return 0; +#undef CHECK_FLAG + } + + return flags; +} + +static grn_bool +is_normalizer(grn_ctx *ctx, grn_obj *object) +{ + if (object->header.type != GRN_PROC) { + return GRN_FALSE; + } + + if (grn_proc_get_type(ctx, object) != GRN_PROC_NORMALIZER) { + return GRN_FALSE; + } + + return GRN_TRUE; +} + +static const char * +char_type_name(grn_char_type type) +{ + const char *name = "unknown"; + +#define CHAR_TYPE_NAME_WITH_BLANK(type_name) do { \ + if (GRN_CHAR_IS_BLANK(type)) { \ + name = type_name "|blank"; \ + } else { \ + name = type_name; \ + } \ + } while (GRN_FALSE) + + switch (GRN_CHAR_TYPE(type)) { + case GRN_CHAR_NULL : + CHAR_TYPE_NAME_WITH_BLANK("null"); + break; + case GRN_CHAR_ALPHA : + CHAR_TYPE_NAME_WITH_BLANK("alpha"); + break; + case GRN_CHAR_DIGIT : + CHAR_TYPE_NAME_WITH_BLANK("digit"); + break; + case GRN_CHAR_SYMBOL : + CHAR_TYPE_NAME_WITH_BLANK("symbol"); + break; + case GRN_CHAR_HIRAGANA : + CHAR_TYPE_NAME_WITH_BLANK("hiragana"); + break; + case GRN_CHAR_KATAKANA : + CHAR_TYPE_NAME_WITH_BLANK("katakana"); + break; + case GRN_CHAR_KANJI : + CHAR_TYPE_NAME_WITH_BLANK("kanji"); + break; + case GRN_CHAR_OTHERS : + CHAR_TYPE_NAME_WITH_BLANK("others"); + break; + default : + CHAR_TYPE_NAME_WITH_BLANK("unknown"); + break; + } + +#undef CHAR_TYPE_NAME_WITH_BLANK + + return name; +} + +static grn_obj * +proc_normalize(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *normalizer_name; + grn_obj *string; + grn_obj *flag_names; + + normalizer_name = VAR(0); + string = VAR(1); + flag_names = VAR(2); + if (GRN_TEXT_LEN(normalizer_name) == 0) { + ERR(GRN_INVALID_ARGUMENT, "normalizer name is missing"); + return NULL; + } + + { + grn_obj *normalizer; + grn_obj *grn_string; + int flags; + unsigned int normalized_length_in_bytes; + unsigned int normalized_n_characters; + + flags = parse_normalize_flags(ctx, flag_names); + normalizer = grn_ctx_get(ctx, + GRN_TEXT_VALUE(normalizer_name), + GRN_TEXT_LEN(normalizer_name)); + if (!normalizer) { + ERR(GRN_INVALID_ARGUMENT, + "[normalize] nonexistent normalizer: <%.*s>", + (int)GRN_TEXT_LEN(normalizer_name), + GRN_TEXT_VALUE(normalizer_name)); + return NULL; + } + + if (!is_normalizer(ctx, normalizer)) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, normalizer); + ERR(GRN_INVALID_ARGUMENT, + "[normalize] not normalizer: %.*s", + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + grn_obj_unlink(ctx, normalizer); + return NULL; + } + + grn_string = grn_string_open(ctx, + GRN_TEXT_VALUE(string), GRN_TEXT_LEN(string), + normalizer, flags); + grn_obj_unlink(ctx, normalizer); + + GRN_OUTPUT_MAP_OPEN("RESULT", 3); + { + const char *normalized; + + grn_string_get_normalized(ctx, grn_string, + &normalized, + &normalized_length_in_bytes, + &normalized_n_characters); + GRN_OUTPUT_CSTR("normalized"); + GRN_OUTPUT_STR(normalized, normalized_length_in_bytes); + } + { + const unsigned char *types; + + types = grn_string_get_types(ctx, grn_string); + GRN_OUTPUT_CSTR("types"); + if (types) { + unsigned int i; + GRN_OUTPUT_ARRAY_OPEN("types", normalized_n_characters); + for (i = 0; i < normalized_n_characters; i++) { + GRN_OUTPUT_CSTR(char_type_name(types[i])); + } + GRN_OUTPUT_ARRAY_CLOSE(); + } else { + GRN_OUTPUT_ARRAY_OPEN("types", 0); + GRN_OUTPUT_ARRAY_CLOSE(); + } + } + { + const short *checks; + + checks = grn_string_get_checks(ctx, grn_string); + GRN_OUTPUT_CSTR("checks"); + if (checks) { + unsigned int i; + GRN_OUTPUT_ARRAY_OPEN("checks", normalized_length_in_bytes); + for (i = 0; i < normalized_length_in_bytes; i++) { + GRN_OUTPUT_INT32(checks[i]); + } + GRN_OUTPUT_ARRAY_CLOSE(); + } else { + GRN_OUTPUT_ARRAY_OPEN("checks", 0); + GRN_OUTPUT_ARRAY_CLOSE(); + } + } + GRN_OUTPUT_MAP_CLOSE(); + + grn_obj_unlink(ctx, grn_string); + } + + return NULL; +} + +static void +list_proc(grn_ctx *ctx, grn_proc_type target_proc_type, + const char *name, const char *plural_name) +{ + grn_obj *db; + grn_table_cursor *cursor; + grn_obj target_procs; + + db = grn_ctx_db(ctx); + cursor = grn_table_cursor_open(ctx, db, NULL, 0, NULL, 0, 0, -1, + GRN_CURSOR_BY_ID); + if (!cursor) { + return; + } + + GRN_PTR_INIT(&target_procs, GRN_OBJ_VECTOR, GRN_ID_NIL); + { + grn_id id; + + while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) { + grn_obj *obj; + grn_proc_type proc_type; + + obj = grn_ctx_at(ctx, id); + if (!obj) { + continue; + } + + if (obj->header.type != GRN_PROC) { + grn_obj_unlink(ctx, obj); + continue; + } + + proc_type = grn_proc_get_type(ctx, obj); + if (proc_type != target_proc_type) { + grn_obj_unlink(ctx, obj); + continue; + } + + GRN_PTR_PUT(ctx, &target_procs, obj); + } + grn_table_cursor_close(ctx, cursor); + + { + int i, n_procs; + + n_procs = GRN_BULK_VSIZE(&target_procs) / sizeof(grn_obj *); + GRN_OUTPUT_ARRAY_OPEN(plural_name, n_procs); + for (i = 0; i < n_procs; i++) { + grn_obj *proc; + char name[GRN_TABLE_MAX_KEY_SIZE]; + int name_size; + + proc = GRN_PTR_VALUE_AT(&target_procs, i); + name_size = grn_obj_name(ctx, proc, name, GRN_TABLE_MAX_KEY_SIZE); + GRN_OUTPUT_MAP_OPEN(name, 1); + GRN_OUTPUT_CSTR("name"); + GRN_OUTPUT_STR(name, name_size); + GRN_OUTPUT_MAP_CLOSE(); + + grn_obj_unlink(ctx, proc); + } + GRN_OUTPUT_ARRAY_CLOSE(); + } + + grn_obj_unlink(ctx, &target_procs); + } +} + +static grn_obj * +proc_tokenizer_list(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + list_proc(ctx, GRN_PROC_TOKENIZER, "tokenizer", "tokenizers"); + return NULL; +} + +static grn_obj * +proc_normalizer_list(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + list_proc(ctx, GRN_PROC_NORMALIZER, "normalizer", "normalizers"); + return NULL; +} + +static grn_obj * +func_rand(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + int val; + grn_obj *obj; + if (nargs > 0) { + int max = GRN_INT32_VALUE(args[0]); + val = (int) (1.0 * max * rand() / (RAND_MAX + 1.0)); + } else { + val = rand(); + } + if ((obj = GRN_PROC_ALLOC(GRN_DB_INT32, 0))) { + GRN_INT32_SET(ctx, obj, val); + } + return obj; +} + +static grn_obj * +func_now(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *obj; + if ((obj = GRN_PROC_ALLOC(GRN_DB_TIME, 0))) { + GRN_TIME_NOW(ctx, obj); + } + return obj; +} + +static inline grn_bool +is_comparable_number_type(grn_id type) +{ + return GRN_DB_INT8 <= type && type <= GRN_DB_TIME; +} + +static inline grn_id +larger_number_type(grn_id type1, grn_id type2) +{ + if (type1 == type2) { + return type1; + } + + switch (type1) { + case GRN_DB_FLOAT : + return type1; + case GRN_DB_TIME : + if (type2 == GRN_DB_FLOAT) { + return type2; + } else { + return type1; + } + default : + if (type2 > type1) { + return type2; + } else { + return type1; + } + } +} + +static inline grn_id +smaller_number_type(grn_id type1, grn_id type2) +{ + if (type1 == type2) { + return type1; + } + + switch (type1) { + case GRN_DB_FLOAT : + return type1; + case GRN_DB_TIME : + if (type2 == GRN_DB_FLOAT) { + return type2; + } else { + return type1; + } + default : + { + grn_id smaller_number_type; + if (type2 > type1) { + smaller_number_type = type2; + } else { + smaller_number_type = type1; + } + switch (smaller_number_type) { + case GRN_DB_UINT8 : + return GRN_DB_INT8; + case GRN_DB_UINT16 : + return GRN_DB_INT16; + case GRN_DB_UINT32 : + return GRN_DB_INT32; + case GRN_DB_UINT64 : + return GRN_DB_INT64; + default : + return smaller_number_type; + } + } + } +} + +static inline grn_bool +is_negative_value(grn_obj *number) +{ + switch (number->header.domain) { + case GRN_DB_INT8 : + return GRN_INT8_VALUE(number) < 0; + case GRN_DB_INT16 : + return GRN_INT16_VALUE(number) < 0; + case GRN_DB_INT32 : + return GRN_INT32_VALUE(number) < 0; + case GRN_DB_INT64 : + return GRN_INT64_VALUE(number) < 0; + case GRN_DB_TIME : + return GRN_TIME_VALUE(number) < 0; + case GRN_DB_FLOAT : + return GRN_FLOAT_VALUE(number) < 0; + default : + return GRN_FALSE; + } +} + +static inline grn_bool +number_safe_cast(grn_ctx *ctx, grn_obj *src, grn_obj *dest, grn_id type) +{ + grn_obj_reinit(ctx, dest, type, 0); + if (src->header.domain == type) { + GRN_TEXT_SET(ctx, dest, GRN_TEXT_VALUE(src), GRN_TEXT_LEN(src)); + return GRN_TRUE; + } + + switch (type) { + case GRN_DB_UINT8 : + if (is_negative_value(src)) { + GRN_UINT8_SET(ctx, dest, 0); + return GRN_TRUE; + } + break; + case GRN_DB_UINT16 : + if (is_negative_value(src)) { + GRN_UINT16_SET(ctx, dest, 0); + return GRN_TRUE; + } + break; + case GRN_DB_UINT32 : + if (is_negative_value(src)) { + GRN_UINT32_SET(ctx, dest, 0); + return GRN_TRUE; + } + break; + case GRN_DB_UINT64 : + if (is_negative_value(src)) { + GRN_UINT64_SET(ctx, dest, 0); + return GRN_TRUE; + } + break; + } + return grn_obj_cast(ctx, src, dest, GRN_FALSE) == GRN_SUCCESS; +} + +static inline int +compare_number(grn_ctx *ctx, grn_obj *number1, grn_obj *number2, grn_id type) +{ +#define COMPARE_AND_RETURN(type, value1, value2)\ + {\ + type computed_value1 = value1;\ + type computed_value2 = value2;\ + if (computed_value1 > computed_value2) {\ + return 1;\ + } else if (computed_value1 < computed_value2) {\ + return -1;\ + } else {\ + return 0;\ + }\ + } + + switch (type) { + case GRN_DB_INT8 : + COMPARE_AND_RETURN(int8_t, + GRN_INT8_VALUE(number1), + GRN_INT8_VALUE(number2)); + case GRN_DB_UINT8 : + COMPARE_AND_RETURN(uint8_t, + GRN_UINT8_VALUE(number1), + GRN_UINT8_VALUE(number2)); + case GRN_DB_INT16 : + COMPARE_AND_RETURN(int16_t, + GRN_INT16_VALUE(number1), + GRN_INT16_VALUE(number2)); + case GRN_DB_UINT16 : + COMPARE_AND_RETURN(uint16_t, + GRN_UINT16_VALUE(number1), + GRN_UINT16_VALUE(number2)); + case GRN_DB_INT32 : + COMPARE_AND_RETURN(int32_t, + GRN_INT32_VALUE(number1), + GRN_INT32_VALUE(number2)); + case GRN_DB_UINT32 : + COMPARE_AND_RETURN(uint32_t, + GRN_UINT32_VALUE(number1), + GRN_UINT32_VALUE(number2)); + case GRN_DB_INT64 : + COMPARE_AND_RETURN(int64_t, + GRN_INT64_VALUE(number1), + GRN_INT64_VALUE(number2)); + case GRN_DB_UINT64 : + COMPARE_AND_RETURN(uint64_t, + GRN_UINT64_VALUE(number1), + GRN_UINT64_VALUE(number2)); + case GRN_DB_FLOAT : + COMPARE_AND_RETURN(double, + GRN_FLOAT_VALUE(number1), + GRN_FLOAT_VALUE(number2)); + case GRN_DB_TIME : + COMPARE_AND_RETURN(int64_t, + GRN_TIME_VALUE(number1), + GRN_TIME_VALUE(number2)); + default : + return 0; + } + +#undef COMPARE_AND_RETURN +} + +inline static void +get_number_in_grn_uvector(grn_ctx *ctx, grn_obj *uvector, unsigned int offset, + grn_obj *buf) +{ +#define GET_UVECTOR_ELEMENT_AS(type) do { \ + GRN_ ## type ## _SET(ctx, \ + buf, \ + GRN_ ## type ## _VALUE_AT(uvector, offset)); \ + } while (GRN_FALSE) + switch (uvector->header.domain) { + case GRN_DB_BOOL : + GET_UVECTOR_ELEMENT_AS(BOOL); + break; + case GRN_DB_INT8 : + GET_UVECTOR_ELEMENT_AS(INT8); + break; + case GRN_DB_UINT8 : + GET_UVECTOR_ELEMENT_AS(UINT8); + break; + case GRN_DB_INT16 : + GET_UVECTOR_ELEMENT_AS(INT16); + break; + case GRN_DB_UINT16 : + GET_UVECTOR_ELEMENT_AS(UINT16); + break; + case GRN_DB_INT32 : + GET_UVECTOR_ELEMENT_AS(INT32); + break; + case GRN_DB_UINT32 : + GET_UVECTOR_ELEMENT_AS(UINT32); + break; + case GRN_DB_INT64 : + GET_UVECTOR_ELEMENT_AS(INT64); + break; + case GRN_DB_UINT64 : + GET_UVECTOR_ELEMENT_AS(UINT64); + break; + case GRN_DB_FLOAT : + GET_UVECTOR_ELEMENT_AS(FLOAT); + break; + case GRN_DB_TIME : + GET_UVECTOR_ELEMENT_AS(TIME); + break; + default : + GET_UVECTOR_ELEMENT_AS(RECORD); + break; + } +#undef GET_UVECTOR_ELEMENT_AS +} + +inline static void +apply_max(grn_ctx *ctx, grn_obj *number, grn_obj *max, + grn_obj *casted_number, grn_obj *casted_max, grn_id cast_type) +{ + grn_id domain = number->header.domain; + if (!is_comparable_number_type(domain)) { + return; + } + cast_type = larger_number_type(cast_type, domain); + if (!number_safe_cast(ctx, number, casted_number, cast_type)) { + return; + } + if (max->header.domain == GRN_DB_VOID) { + grn_obj_reinit(ctx, max, cast_type, 0); + GRN_TEXT_SET(ctx, max, + GRN_TEXT_VALUE(casted_number), + GRN_TEXT_LEN(casted_number)); + return; + } + + if (max->header.domain != cast_type) { + if (!number_safe_cast(ctx, max, casted_max, cast_type)) { + return; + } + grn_obj_reinit(ctx, max, cast_type, 0); + GRN_TEXT_SET(ctx, max, + GRN_TEXT_VALUE(casted_max), + GRN_TEXT_LEN(casted_max)); + } + if (compare_number(ctx, casted_number, max, cast_type) > 0) { + grn_obj_reinit(ctx, max, cast_type, 0); + GRN_TEXT_SET(ctx, max, + GRN_TEXT_VALUE(casted_number), + GRN_TEXT_LEN(casted_number)); + } +} + +static grn_obj * +func_max(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *max; + grn_id cast_type = GRN_DB_INT8; + grn_obj casted_max, casted_number; + int i; + + max = GRN_PROC_ALLOC(GRN_DB_VOID, 0); + if (!max) { + return max; + } + + GRN_VOID_INIT(&casted_max); + GRN_VOID_INIT(&casted_number); + + for (i = 0; i < nargs; i++) { + switch (args[i]->header.type) { + case GRN_BULK : + apply_max(ctx, args[i], max, &casted_number, &casted_max, cast_type); + break; + case GRN_UVECTOR : + { + unsigned int j; + unsigned int n_elements; + grn_obj number_in_uvector; + grn_obj *domain; + + domain = grn_ctx_at(ctx, args[i]->header.domain); + GRN_OBJ_INIT(&number_in_uvector, GRN_BULK, 0, args[i]->header.domain); + n_elements = grn_uvector_size(ctx, args[i]); + for (j = 0; j < n_elements; j++) { + get_number_in_grn_uvector(ctx, args[i], j, &number_in_uvector); + if (grn_obj_is_table(ctx, domain)) { + grn_obj_reinit(ctx, &number_in_uvector, domain->header.domain, 0); + grn_table_get_key2(ctx, domain, + GRN_RECORD_VALUE(&number_in_uvector), + &number_in_uvector); + } + apply_max(ctx, &number_in_uvector, max, &casted_number, &casted_max, cast_type); + } + GRN_OBJ_FIN(ctx, &number_in_uvector); + } + break; + default : + continue; + } + } + GRN_OBJ_FIN(ctx, &casted_max); + GRN_OBJ_FIN(ctx, &casted_number); + + return max; +} + +static void +apply_min(grn_ctx *ctx, grn_obj *number, grn_obj *min, + grn_obj *casted_number, grn_obj *casted_min, grn_id cast_type) +{ + grn_id domain = number->header.domain; + if (!is_comparable_number_type(domain)) { + return; + } + cast_type = smaller_number_type(cast_type, domain); + if (!number_safe_cast(ctx, number, casted_number, cast_type)) { + return; + } + if (min->header.domain == GRN_DB_VOID) { + grn_obj_reinit(ctx, min, cast_type, 0); + GRN_TEXT_SET(ctx, min, + GRN_TEXT_VALUE(casted_number), + GRN_TEXT_LEN(casted_number)); + return; + } + + if (min->header.domain != cast_type) { + if (!number_safe_cast(ctx, min, casted_min, cast_type)) { + return; + } + grn_obj_reinit(ctx, min, cast_type, 0); + GRN_TEXT_SET(ctx, min, + GRN_TEXT_VALUE(casted_min), + GRN_TEXT_LEN(casted_min)); + } + if (compare_number(ctx, casted_number, min, cast_type) < 0) { + grn_obj_reinit(ctx, min, cast_type, 0); + GRN_TEXT_SET(ctx, min, + GRN_TEXT_VALUE(casted_number), + GRN_TEXT_LEN(casted_number)); + } +} + +static grn_obj * +func_min(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *min; + grn_id cast_type = GRN_DB_INT8; + grn_obj casted_min, casted_number; + int i; + + min = GRN_PROC_ALLOC(GRN_DB_VOID, 0); + if (!min) { + return min; + } + + GRN_VOID_INIT(&casted_min); + GRN_VOID_INIT(&casted_number); + for (i = 0; i < nargs; i++) { + switch (args[i]->header.type) { + case GRN_BULK : + apply_min(ctx, args[i], min, &casted_number, &casted_min, cast_type); + break; + case GRN_UVECTOR : + { + unsigned int j; + unsigned int n_elements; + grn_obj number_in_uvector; + grn_obj *domain; + + domain = grn_ctx_at(ctx, args[i]->header.domain); + GRN_OBJ_INIT(&number_in_uvector, GRN_BULK, 0, args[i]->header.domain); + n_elements = grn_uvector_size(ctx, args[i]); + for (j = 0; j < n_elements; j++) { + get_number_in_grn_uvector(ctx, args[i], j, &number_in_uvector); + if (grn_obj_is_table(ctx, domain)) { + grn_obj_reinit(ctx, &number_in_uvector, domain->header.domain, 0); + grn_table_get_key2(ctx, domain, + GRN_RECORD_VALUE(&number_in_uvector), + &number_in_uvector); + } + apply_min(ctx, &number_in_uvector, min, &casted_number, &casted_min, cast_type); + } + GRN_OBJ_FIN(ctx, &number_in_uvector); + } + break; + default : + continue; + } + } + GRN_OBJ_FIN(ctx, &casted_min); + GRN_OBJ_FIN(ctx, &casted_number); + + return min; +} + +static grn_obj * +func_geo_in_circle(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *obj; + grn_bool r = GRN_FALSE; + grn_geo_approximate_type type = GRN_GEO_APPROXIMATE_RECTANGLE; + switch (nargs) { + case 4 : + if (grn_geo_resolve_approximate_type(ctx, args[3], &type) != GRN_SUCCESS) { + break; + } + /* fallthru */ + case 3 : + r = grn_geo_in_circle(ctx, args[0], args[1], args[2], type); + break; + default : + break; + } + if ((obj = GRN_PROC_ALLOC(GRN_DB_BOOL, 0))) { + GRN_BOOL_SET(ctx, obj, r); + } + return obj; +} + +static grn_obj * +func_geo_in_rectangle(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *obj; + grn_bool r = GRN_FALSE; + if (nargs == 3) { + r = grn_geo_in_rectangle(ctx, args[0], args[1], args[2]); + } + if ((obj = GRN_PROC_ALLOC(GRN_DB_BOOL, 0))) { + GRN_BOOL_SET(ctx, obj, r); + } + return obj; +} + +static grn_obj * +func_geo_distance(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *obj; + double d = 0.0; + grn_geo_approximate_type type = GRN_GEO_APPROXIMATE_RECTANGLE; + switch (nargs) { + case 3 : + if (grn_geo_resolve_approximate_type(ctx, args[2], &type) != GRN_SUCCESS) { + break; + } + /* fallthru */ + case 2 : + d = grn_geo_distance(ctx, args[0], args[1], type); + break; + default: + break; + } + if ((obj = GRN_PROC_ALLOC(GRN_DB_FLOAT, 0))) { + GRN_FLOAT_SET(ctx, obj, d); + } + return obj; +} + +/* deprecated. */ +static grn_obj * +func_geo_distance2(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *obj; + double d = 0; + if (nargs == 2) { + d = grn_geo_distance_sphere(ctx, args[0], args[1]); + } + if ((obj = GRN_PROC_ALLOC(GRN_DB_FLOAT, 0))) { + GRN_FLOAT_SET(ctx, obj, d); + } + return obj; +} + +/* deprecated. */ +static grn_obj * +func_geo_distance3(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *obj; + double d = 0; + if (nargs == 2) { + d = grn_geo_distance_ellipsoid(ctx, args[0], args[1]); + } + if ((obj = GRN_PROC_ALLOC(GRN_DB_FLOAT, 0))) { + GRN_FLOAT_SET(ctx, obj, d); + } + return obj; +} + +static grn_obj * +func_all_records(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *true_value; + if ((true_value = GRN_PROC_ALLOC(GRN_DB_BOOL, 0))) { + GRN_BOOL_SET(ctx, true_value, GRN_TRUE); + } + return true_value; +} + +static grn_rc +selector_all_records(grn_ctx *ctx, grn_obj *table, grn_obj *index, + int nargs, grn_obj **args, + grn_obj *res, grn_operator op) +{ + grn_posting posting; + + memset(&posting, 0, sizeof(grn_posting)); + GRN_TABLE_EACH(ctx, table, 0, 0, id, NULL, NULL, NULL, { + posting.rid = id; + grn_ii_posting_add(ctx, &posting, (grn_hash *)res, GRN_OP_OR); + }); + + return ctx->rc; +} + +typedef struct { + grn_obj *found; + grn_obj *table; + grn_obj *records; +} selector_to_function_data; + +static grn_bool +selector_to_function_data_init(grn_ctx *ctx, + selector_to_function_data *data, + grn_user_data *user_data) +{ + grn_obj *condition = NULL; + grn_obj *variable; + + data->table = NULL; + data->records = NULL; + + data->found = GRN_PROC_ALLOC(GRN_DB_BOOL, 0); + if (!data->found) { + return GRN_FALSE; + } + GRN_BOOL_SET(ctx, data->found, GRN_FALSE); + + grn_proc_get_info(ctx, user_data, NULL, NULL, &condition); + if (!condition) { + return GRN_FALSE; + } + + variable = grn_expr_get_var_by_offset(ctx, condition, 0); + if (!variable) { + return GRN_FALSE; + } + + data->table = grn_ctx_at(ctx, variable->header.domain); + if (!data->table) { + return GRN_FALSE; + } + + data->records = grn_table_create(ctx, NULL, 0, NULL, + GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, + data->table, NULL); + if (!data->records) { + return GRN_FALSE; + } + + { + grn_rset_posinfo pi; + unsigned int key_size; + memset(&pi, 0, sizeof(grn_rset_posinfo)); + pi.rid = GRN_RECORD_VALUE(variable); + key_size = ((grn_hash *)(data->records))->key_size; + if (grn_table_add(ctx, data->records, &pi, key_size, NULL) == GRN_ID_NIL) { + return GRN_FALSE; + } + } + + return GRN_TRUE; +} + +static void +selector_to_function_data_selected(grn_ctx *ctx, + selector_to_function_data *data) +{ + GRN_BOOL_SET(ctx, data->found, grn_table_size(ctx, data->records) > 0); +} + +static void +selector_to_function_data_fin(grn_ctx *ctx, + selector_to_function_data *data) +{ + if (data->records) { + grn_obj_unlink(ctx, data->records); + } +} + +grn_operator +grn_proc_option_value_mode(grn_ctx *ctx, + grn_obj *option, + grn_operator default_mode, + const char *context) +{ + if (option->header.domain != GRN_DB_TEXT) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, option); + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "%s: mode must be text: <%.*s>", + context, + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + return GRN_OP_NOP; + } + + if (GRN_TEXT_LEN(option) == 0) { + return default_mode; + } + +#define EQUAL_MODE(name) \ + (GRN_TEXT_LEN(option) == strlen(name) && \ + memcmp(GRN_TEXT_VALUE(option), name, strlen(name)) == 0) + + if (EQUAL_MODE("==") || EQUAL_MODE("EQUAL")) { + return GRN_OP_EQUAL; + } else if (EQUAL_MODE("!=") || EQUAL_MODE("NOT_EQUAL")) { + return GRN_OP_NOT_EQUAL; + } else if (EQUAL_MODE("<") || EQUAL_MODE("LESS")) { + return GRN_OP_LESS; + } else if (EQUAL_MODE(">") || EQUAL_MODE("GREATER")) { + return GRN_OP_GREATER; + } else if (EQUAL_MODE("<=") || EQUAL_MODE("LESS_EQUAL")) { + return GRN_OP_LESS_EQUAL; + } else if (EQUAL_MODE(">=") || EQUAL_MODE("GREATER_EQUAL")) { + return GRN_OP_GREATER_EQUAL; + } else if (EQUAL_MODE("@") || EQUAL_MODE("MATCH")) { + return GRN_OP_MATCH; + } else if (EQUAL_MODE("*N") || EQUAL_MODE("NEAR")) { + return GRN_OP_NEAR; + } else if (EQUAL_MODE("*S") || EQUAL_MODE("SIMILAR")) { + return GRN_OP_SIMILAR; + } else if (EQUAL_MODE("^") || EQUAL_MODE("@^") || EQUAL_MODE("PREFIX")) { + return GRN_OP_PREFIX; + } else if (EQUAL_MODE("$") || EQUAL_MODE("@$") || EQUAL_MODE("SUFFIX")) { + return GRN_OP_SUFFIX; + } else if (EQUAL_MODE("~") || EQUAL_MODE("@~") || EQUAL_MODE("REGEXP")) { + return GRN_OP_REGEXP; + } else { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "%s: mode must be one of them: " + "[" + "\"==\", \"EQUAL\", " + "\"!=\", \"NOT_EQUAL\", " + "\"<\", \"LESS\", " + "\">\", \"GREATER\", " + "\"<=\", \"LESS_EQUAL\", " + "\">=\", \"GREATER_EQUAL\", " + "\"@\", \"MATCH\", " + "\"*N\", \"NEAR\", " + "\"*S\", \"SIMILAR\", " + "\"^\", \"@^\", \"PREFIX\", " + "\"$\", \"@$\", \"SUFFIX\", " + "\"~\", \"@~\", \"REGEXP\"" + "]: <%.*s>", + context, + (int)GRN_TEXT_LEN(option), + GRN_TEXT_VALUE(option)); + return GRN_OP_NOP; + } + +#undef EQUAL_MODE +} + +static grn_rc +run_query(grn_ctx *ctx, grn_obj *table, + int nargs, grn_obj **args, + grn_obj *res, grn_operator op) +{ + grn_rc rc = GRN_SUCCESS; + grn_obj *match_columns_string; + grn_obj *query; + grn_obj *query_expander_name = NULL; + grn_operator default_mode = GRN_OP_MATCH; + grn_expr_flags flags = GRN_EXPR_SYNTAX_QUERY; + grn_bool flags_specified = GRN_FALSE; + grn_obj *match_columns = NULL; + grn_obj *condition = NULL; + grn_obj *dummy_variable; + + if (!(2 <= nargs && nargs <= 3)) { + ERR(GRN_INVALID_ARGUMENT, + "query(): wrong number of arguments (%d for 2..3)", nargs); + rc = ctx->rc; + goto exit; + } + + match_columns_string = args[0]; + query = args[1]; + if (nargs > 2) { + grn_obj *options = args[2]; + + switch (options->header.type) { + case GRN_BULK : + query_expander_name = options; + break; + case GRN_TABLE_HASH_KEY : + { + grn_hash_cursor *cursor; + void *key; + grn_obj *value; + int key_size; + cursor = grn_hash_cursor_open(ctx, (grn_hash *)options, + NULL, 0, NULL, 0, + 0, -1, 0); + if (!cursor) { + GRN_PLUGIN_ERROR(ctx, GRN_NO_MEMORY_AVAILABLE, + "query(): failed to open cursor for options"); + rc = ctx->rc; + goto exit; + } + while (grn_hash_cursor_next(ctx, cursor) != GRN_ID_NIL) { + grn_hash_cursor_get_key_value(ctx, cursor, &key, &key_size, + (void **)&value); + +#define KEY_EQUAL(name) \ + (key_size == strlen(name) && memcmp(key, name, strlen(name)) == 0) + if (KEY_EQUAL("expander")) { + query_expander_name = value; + } else if (KEY_EQUAL("default_mode")) { + default_mode = grn_proc_option_value_mode(ctx, + value, + GRN_OP_MATCH, + "query()"); + if (ctx->rc != GRN_SUCCESS) { + grn_hash_cursor_close(ctx, cursor); + rc = ctx->rc; + goto exit; + } + } else if (KEY_EQUAL("flags")) { + flags_specified = GRN_TRUE; + flags |= grn_proc_expr_query_flags_parse(ctx, + GRN_TEXT_VALUE(value), + GRN_TEXT_LEN(value), + "query()"); + if (ctx->rc != GRN_SUCCESS) { + grn_hash_cursor_close(ctx, cursor); + rc = ctx->rc; + goto exit; + } + } else { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "query(): unknown option name: <%.*s>", + key_size, (char *)key); + grn_hash_cursor_close(ctx, cursor); + rc = ctx->rc; + goto exit; + } +#undef KEY_EQUAL + } + grn_hash_cursor_close(ctx, cursor); + } + break; + default : + { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, options); + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "query(): " + "3rd argument must be string or object literal: <%.*s>", + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + } + rc = ctx->rc; + goto exit; + } + } + + if (!flags_specified) { + flags |= GRN_EXPR_ALLOW_PRAGMA | GRN_EXPR_ALLOW_COLUMN; + } + + if (match_columns_string->header.domain == GRN_DB_TEXT && + GRN_TEXT_LEN(match_columns_string) > 0) { + GRN_EXPR_CREATE_FOR_QUERY(ctx, table, match_columns, dummy_variable); + if (!match_columns) { + rc = ctx->rc; + goto exit; + } + + grn_expr_parse(ctx, match_columns, + GRN_TEXT_VALUE(match_columns_string), + GRN_TEXT_LEN(match_columns_string), + NULL, GRN_OP_MATCH, GRN_OP_AND, + GRN_EXPR_SYNTAX_SCRIPT); + if (ctx->rc != GRN_SUCCESS) { + rc = ctx->rc; + goto exit; + } + } + + if (query->header.domain == GRN_DB_TEXT && GRN_TEXT_LEN(query) > 0) { + const char *query_string; + unsigned int query_string_len; + grn_obj expanded_query; + + GRN_EXPR_CREATE_FOR_QUERY(ctx, table, condition, dummy_variable); + if (!condition) { + rc = ctx->rc; + goto exit; + } + + query_string = GRN_TEXT_VALUE(query); + query_string_len = GRN_TEXT_LEN(query); + + GRN_TEXT_INIT(&expanded_query, 0); + if (query_expander_name && + query_expander_name->header.domain == GRN_DB_TEXT && + GRN_TEXT_LEN(query_expander_name) > 0) { + rc = grn_proc_syntax_expand_query(ctx, + query_string, query_string_len, + flags, + GRN_TEXT_VALUE(query_expander_name), + GRN_TEXT_LEN(query_expander_name), + NULL, 0, + NULL, 0, + &expanded_query, + "[query]"); + if (rc != GRN_SUCCESS) { + GRN_OBJ_FIN(ctx, &expanded_query); + goto exit; + } + query_string = GRN_TEXT_VALUE(&expanded_query); + query_string_len = GRN_TEXT_LEN(&expanded_query); + } + grn_expr_parse(ctx, condition, + query_string, + query_string_len, + match_columns, default_mode, GRN_OP_AND, flags); + rc = ctx->rc; + GRN_OBJ_FIN(ctx, &expanded_query); + if (rc != GRN_SUCCESS) { + goto exit; + } + grn_table_select(ctx, table, condition, res, op); + rc = ctx->rc; + } + +exit : + if (match_columns) { + grn_obj_unlink(ctx, match_columns); + } + if (condition) { + grn_obj_unlink(ctx, condition); + } + + return rc; +} + +static grn_obj * +func_query(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + selector_to_function_data data; + + if (selector_to_function_data_init(ctx, &data, user_data)) { + grn_rc rc; + rc = run_query(ctx, data.table, nargs, args, data.records, GRN_OP_AND); + if (rc == GRN_SUCCESS) { + selector_to_function_data_selected(ctx, &data); + } + } + selector_to_function_data_fin(ctx, &data); + + return data.found; +} + +static grn_rc +selector_query(grn_ctx *ctx, grn_obj *table, grn_obj *index, + int nargs, grn_obj **args, + grn_obj *res, grn_operator op) +{ + return run_query(ctx, table, nargs - 1, args + 1, res, op); +} + +static grn_rc +run_sub_filter(grn_ctx *ctx, grn_obj *table, + int nargs, grn_obj **args, + grn_obj *res, grn_operator op) +{ + grn_rc rc = GRN_SUCCESS; + grn_obj *scope; + grn_obj *sub_filter_string; + grn_obj *scope_domain = NULL; + grn_obj *sub_filter = NULL; + grn_obj *dummy_variable = NULL; + + if (nargs != 2) { + ERR(GRN_INVALID_ARGUMENT, + "sub_filter(): wrong number of arguments (%d for 2)", nargs); + rc = ctx->rc; + goto exit; + } + + scope = args[0]; + sub_filter_string = args[1]; + + switch (scope->header.type) { + case GRN_ACCESSOR : + case GRN_COLUMN_FIX_SIZE : + case GRN_COLUMN_VAR_SIZE : + case GRN_COLUMN_INDEX : + break; + default : + /* TODO: put inspected the 1st argument to message */ + ERR(GRN_INVALID_ARGUMENT, + "sub_filter(): the 1st argument must be column or accessor"); + rc = ctx->rc; + goto exit; + break; + } + + scope_domain = grn_ctx_at(ctx, grn_obj_get_range(ctx, scope)); + + if (sub_filter_string->header.domain != GRN_DB_TEXT) { + /* TODO: put inspected the 2nd argument to message */ + ERR(GRN_INVALID_ARGUMENT, + "sub_filter(): the 2nd argument must be String"); + rc = ctx->rc; + goto exit; + } + if (GRN_TEXT_LEN(sub_filter_string) == 0) { + ERR(GRN_INVALID_ARGUMENT, + "sub_filter(): the 2nd argument must not be empty String"); + rc = ctx->rc; + goto exit; + } + + GRN_EXPR_CREATE_FOR_QUERY(ctx, scope_domain, sub_filter, dummy_variable); + if (!sub_filter) { + rc = ctx->rc; + goto exit; + } + + grn_expr_parse(ctx, sub_filter, + GRN_TEXT_VALUE(sub_filter_string), + GRN_TEXT_LEN(sub_filter_string), + NULL, GRN_OP_MATCH, GRN_OP_AND, + GRN_EXPR_SYNTAX_SCRIPT); + if (ctx->rc != GRN_SUCCESS) { + rc = ctx->rc; + goto exit; + } + + { + grn_obj *base_res = NULL; + + base_res = grn_table_create(ctx, NULL, 0, NULL, + GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, + scope_domain, NULL); + grn_table_select(ctx, scope_domain, sub_filter, base_res, GRN_OP_OR); + if (scope->header.type == GRN_ACCESSOR) { + rc = grn_accessor_resolve(ctx, scope, -1, base_res, res, op); + } else { + grn_accessor accessor; + accessor.header.type = GRN_ACCESSOR; + accessor.obj = scope; + accessor.action = GRN_ACCESSOR_GET_COLUMN_VALUE; + accessor.next = NULL; + rc = grn_accessor_resolve(ctx, (grn_obj *)&accessor, -1, base_res, + res, op); + } + grn_obj_unlink(ctx, base_res); + } + +exit : + if (scope_domain) { + grn_obj_unlink(ctx, scope_domain); + } + if (sub_filter) { + grn_obj_unlink(ctx, sub_filter); + } + + return rc; +} + +static grn_rc +selector_sub_filter(grn_ctx *ctx, grn_obj *table, grn_obj *index, + int nargs, grn_obj **args, + grn_obj *res, grn_operator op) +{ + return run_sub_filter(ctx, table, nargs - 1, args + 1, res, op); +} + +static grn_obj * +func_html_untag(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *html_arg; + int html_arg_domain; + grn_obj html; + grn_obj *text; + const char *html_raw; + int i, length; + grn_bool in_tag = GRN_FALSE; + + if (nargs != 1) { + ERR(GRN_INVALID_ARGUMENT, "HTML is missing"); + return NULL; + } + + html_arg = args[0]; + html_arg_domain = html_arg->header.domain; + switch (html_arg_domain) { + case GRN_DB_SHORT_TEXT : + case GRN_DB_TEXT : + case GRN_DB_LONG_TEXT : + GRN_VALUE_VAR_SIZE_INIT(&html, GRN_OBJ_DO_SHALLOW_COPY, html_arg_domain); + GRN_TEXT_SET(ctx, &html, GRN_TEXT_VALUE(html_arg), GRN_TEXT_LEN(html_arg)); + break; + default : + GRN_TEXT_INIT(&html, 0); + if (grn_obj_cast(ctx, html_arg, &html, GRN_FALSE)) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, html_arg); + ERR(GRN_INVALID_ARGUMENT, "failed to cast to text: <%.*s>", + (int)GRN_TEXT_LEN(&inspected), GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + GRN_OBJ_FIN(ctx, &html); + return NULL; + } + break; + } + + text = GRN_PROC_ALLOC(html.header.domain, 0); + if (!text) { + GRN_OBJ_FIN(ctx, &html); + return NULL; + } + + html_raw = GRN_TEXT_VALUE(&html); + length = GRN_TEXT_LEN(&html); + for (i = 0; i < length; i++) { + switch (html_raw[i]) { + case '<' : + in_tag = GRN_TRUE; + break; + case '>' : + if (in_tag) { + in_tag = GRN_FALSE; + } else { + GRN_TEXT_PUTC(ctx, text, html_raw[i]); + } + break; + default : + if (!in_tag) { + GRN_TEXT_PUTC(ctx, text, html_raw[i]); + } + break; + } + } + + GRN_OBJ_FIN(ctx, &html); + + return text; +} + +static grn_bool +grn_text_equal_cstr(grn_ctx *ctx, grn_obj *text, const char *cstr) +{ + int cstr_len; + + cstr_len = strlen(cstr); + return (GRN_TEXT_LEN(text) == cstr_len && + strncmp(GRN_TEXT_VALUE(text), cstr, cstr_len) == 0); +} + +typedef enum { + BETWEEN_BORDER_INVALID, + BETWEEN_BORDER_INCLUDE, + BETWEEN_BORDER_EXCLUDE +} between_border_type; + +typedef struct { + grn_obj *value; + grn_obj *min; + grn_obj casted_min; + between_border_type min_border_type; + grn_obj *max; + grn_obj casted_max; + between_border_type max_border_type; +} between_data; + +static void +between_data_init(grn_ctx *ctx, between_data *data) +{ + GRN_VOID_INIT(&(data->casted_min)); + GRN_VOID_INIT(&(data->casted_max)); +} + +static void +between_data_fin(grn_ctx *ctx, between_data *data) +{ + GRN_OBJ_FIN(ctx, &(data->casted_min)); + GRN_OBJ_FIN(ctx, &(data->casted_max)); +} + +static between_border_type +between_parse_border(grn_ctx *ctx, grn_obj *border, + const char *argument_description) +{ + grn_obj inspected; + + /* TODO: support other text types */ + if (border->header.domain == GRN_DB_TEXT) { + if (grn_text_equal_cstr(ctx, border, "include")) { + return BETWEEN_BORDER_INCLUDE; + } else if (grn_text_equal_cstr(ctx, border, "exclude")) { + return BETWEEN_BORDER_EXCLUDE; + } + } + + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, border); + ERR(GRN_INVALID_ARGUMENT, + "between(): %s must be \"include\" or \"exclude\": <%.*s>", + argument_description, + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + grn_obj_unlink(ctx, &inspected); + + return BETWEEN_BORDER_INVALID; +} + +static grn_rc +between_cast(grn_ctx *ctx, grn_obj *source, grn_obj *destination, grn_id domain, + const char *target_argument_name) +{ + grn_rc rc; + + GRN_OBJ_INIT(destination, GRN_BULK, 0, domain); + rc = grn_obj_cast(ctx, source, destination, GRN_FALSE); + if (rc != GRN_SUCCESS) { + grn_obj inspected_source; + grn_obj *domain_object; + char domain_name[GRN_TABLE_MAX_KEY_SIZE]; + int domain_name_length; + + GRN_TEXT_INIT(&inspected_source, 0); + grn_inspect(ctx, &inspected_source, source); + + domain_object = grn_ctx_at(ctx, domain); + domain_name_length = + grn_obj_name(ctx, domain_object, domain_name, GRN_TABLE_MAX_KEY_SIZE); + + ERR(rc, "between(): failed to cast %s: <%.*s> -> <%.*s>", + target_argument_name, + (int)GRN_TEXT_LEN(&inspected_source), + GRN_TEXT_VALUE(&inspected_source), + domain_name_length, + domain_name); + + grn_obj_unlink(ctx, &inspected_source); + grn_obj_unlink(ctx, domain_object); + } + + return rc; +} + +static grn_rc +between_parse_args(grn_ctx *ctx, int nargs, grn_obj **args, between_data *data) +{ + grn_rc rc = GRN_SUCCESS; + grn_obj *min_border; + grn_obj *max_border; + + if (nargs != 5) { + ERR(GRN_INVALID_ARGUMENT, + "between(): wrong number of arguments (%d for 5)", nargs); + rc = ctx->rc; + goto exit; + } + + data->value = args[0]; + data->min = args[1]; + min_border = args[2]; + data->max = args[3]; + max_border = args[4]; + + data->min_border_type = + between_parse_border(ctx, min_border, "the 3rd argument (min_border)"); + if (data->min_border_type == BETWEEN_BORDER_INVALID) { + rc = ctx->rc; + goto exit; + } + + data->max_border_type = + between_parse_border(ctx, max_border, "the 5th argument (max_border)"); + if (data->max_border_type == BETWEEN_BORDER_INVALID) { + rc = ctx->rc; + goto exit; + } + + { + grn_id value_type; + switch (data->value->header.type) { + case GRN_BULK : + value_type = data->value->header.domain; + break; + case GRN_COLUMN_INDEX : + { + grn_obj *domain_object; + domain_object = grn_ctx_at(ctx, data->value->header.domain); + value_type = domain_object->header.domain; + } + break; + default : + value_type = grn_obj_get_range(ctx, data->value); + break; + } + if (value_type != data->min->header.domain) { + rc = between_cast(ctx, data->min, &data->casted_min, value_type, "min"); + if (rc != GRN_SUCCESS) { + goto exit; + } + data->min = &(data->casted_min); + } + + if (value_type != data->max->header.domain) { + rc = between_cast(ctx, data->max, &data->casted_max, value_type, "max"); + if (rc != GRN_SUCCESS) { + goto exit; + } + data->max = &(data->casted_max); + } + } + +exit : + return rc; +} + +static grn_bool +between_create_expr(grn_ctx *ctx, grn_obj *table, between_data *data, + grn_obj **expr, grn_obj **variable) +{ + GRN_EXPR_CREATE_FOR_QUERY(ctx, table, *expr, *variable); + if (!*expr) { + return GRN_FALSE; + } + + if (data->value->header.type == GRN_BULK) { + grn_expr_append_obj(ctx, *expr, data->value, GRN_OP_PUSH, 1); + } else { + grn_expr_append_obj(ctx, *expr, data->value, GRN_OP_GET_VALUE, 1); + } + grn_expr_append_obj(ctx, *expr, data->min, GRN_OP_PUSH, 1); + if (data->min_border_type == BETWEEN_BORDER_INCLUDE) { + grn_expr_append_op(ctx, *expr, GRN_OP_GREATER_EQUAL, 2); + } else { + grn_expr_append_op(ctx, *expr, GRN_OP_GREATER, 2); + } + + if (data->value->header.type == GRN_BULK) { + grn_expr_append_obj(ctx, *expr, data->value, GRN_OP_PUSH, 1); + } else { + grn_expr_append_obj(ctx, *expr, data->value, GRN_OP_GET_VALUE, 1); + } + grn_expr_append_obj(ctx, *expr, data->max, GRN_OP_PUSH, 1); + if (data->max_border_type == BETWEEN_BORDER_INCLUDE) { + grn_expr_append_op(ctx, *expr, GRN_OP_LESS_EQUAL, 2); + } else { + grn_expr_append_op(ctx, *expr, GRN_OP_LESS, 2); + } + + grn_expr_append_op(ctx, *expr, GRN_OP_AND, 2); + + return GRN_TRUE; +} + +static grn_obj * +func_between(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_rc rc = GRN_SUCCESS; + grn_obj *found; + between_data data; + grn_obj *condition = NULL; + grn_obj *variable; + grn_obj *table = NULL; + grn_obj *between_expr; + grn_obj *between_variable; + grn_obj *result; + + found = GRN_PROC_ALLOC(GRN_DB_BOOL, 0); + if (!found) { + return NULL; + } + GRN_BOOL_SET(ctx, found, GRN_FALSE); + + grn_proc_get_info(ctx, user_data, NULL, NULL, &condition); + if (!condition) { + return found; + } + + variable = grn_expr_get_var_by_offset(ctx, condition, 0); + if (!variable) { + return found; + } + + between_data_init(ctx, &data); + rc = between_parse_args(ctx, nargs, args, &data); + if (rc != GRN_SUCCESS) { + goto exit; + } + + table = grn_ctx_at(ctx, variable->header.domain); + if (!table) { + goto exit; + } + if (!between_create_expr(ctx, table, &data, &between_expr, &between_variable)) { + goto exit; + } + + GRN_RECORD_SET(ctx, between_variable, GRN_RECORD_VALUE(variable)); + result = grn_expr_exec(ctx, between_expr, 0); + if (grn_obj_is_true(ctx, result)) { + GRN_BOOL_SET(ctx, found, GRN_TRUE); + } + + grn_obj_unlink(ctx, between_expr); + grn_obj_unlink(ctx, table); + +exit : + between_data_fin(ctx, &data); + if (table) { + grn_obj_unlink(ctx, table); + } + + return found; +} + +static grn_bool +selector_between_sequential_search_should_use(grn_ctx *ctx, + grn_obj *table, + grn_obj *index, + grn_obj *index_table, + between_data *data, + grn_obj *res, + grn_operator op, + double too_many_index_match_ratio) +{ + int n_index_keys; + + if (too_many_index_match_ratio < 0.0) { + return GRN_FALSE; + } + + if (op != GRN_OP_AND) { + return GRN_FALSE; + } + + if (!index) { + return GRN_FALSE; + } + + if (index->header.flags & GRN_OBJ_WITH_WEIGHT) { + return GRN_FALSE; + } + + if (data->value->header.type == GRN_COLUMN_INDEX) { + return GRN_FALSE; + } + + n_index_keys = grn_table_size(ctx, index_table); + if (n_index_keys == 0) { + return GRN_FALSE; + } + + switch (index_table->header.domain) { + /* TODO: */ + /* case GRN_DB_INT8 : */ + /* case GRN_DB_UINT8 : */ + /* case GRN_DB_INT16 : */ + /* case GRN_DB_UINT16 : */ + /* case GRN_DB_INT32 : */ + /* case GRN_DB_UINT32 : */ + /* case GRN_DB_INT64 : */ + /* case GRN_DB_UINT64 : */ + /* case GRN_DB_FLOAT : */ + case GRN_DB_TIME : + break; + default : + return GRN_FALSE; + } + + { + grn_table_cursor *cursor; + long long int all_min; + long long int all_max; + cursor = grn_table_cursor_open(ctx, index_table, + NULL, -1, + NULL, -1, + 0, 1, + GRN_CURSOR_BY_KEY | GRN_CURSOR_ASCENDING); + if (!cursor) { + return GRN_FALSE; + } + if (grn_table_cursor_next(ctx, cursor) == GRN_ID_NIL) { + grn_table_cursor_close(ctx, cursor); + return GRN_FALSE; + } + { + long long int *key; + grn_table_cursor_get_key(ctx, cursor, (void **)&key); + all_min = *key; + } + grn_table_cursor_close(ctx, cursor); + + cursor = grn_table_cursor_open(ctx, index_table, + NULL, 0, NULL, 0, + 0, 1, + GRN_CURSOR_BY_KEY | GRN_CURSOR_DESCENDING); + if (!cursor) { + return GRN_FALSE; + } + if (grn_table_cursor_next(ctx, cursor) == GRN_ID_NIL) { + grn_table_cursor_close(ctx, cursor); + return GRN_FALSE; + } + { + long long int *key; + grn_table_cursor_get_key(ctx, cursor, (void **)&key); + all_max = *key; + } + grn_table_cursor_close(ctx, cursor); + + /* + * We assume the following: + * * homogeneous index key distribution. + * * each index key matches only 1 record. + * TODO: Improve me. + */ + { + int n_existing_records; + int n_indexed_records; + long long int all_difference; + long long int argument_difference; + + n_existing_records = grn_table_size(ctx, res); + + all_difference = all_max - all_min; + if (all_difference <= 0) { + return GRN_FALSE; + } + argument_difference = + GRN_TIME_VALUE(data->max) - GRN_TIME_VALUE(data->min); + if (argument_difference <= 0) { + return GRN_FALSE; + } + n_indexed_records = + n_index_keys * ((double)argument_difference / (double)all_difference); + + /* + * Same as: + * ((n_existing_record / n_indexed_records) > too_many_index_match_ratio) + */ + if (n_existing_records > (n_indexed_records * too_many_index_match_ratio)) { + return GRN_FALSE; + } + } + } + + return GRN_TRUE; +} + +static grn_rc +selector_between_sequential_search(grn_ctx *ctx, + grn_obj *table, + between_data *data, + grn_obj *res, + grn_operator op) +{ + { + int offset = 0; + int limit = -1; + int flags = 0; + grn_obj *target_table; + grn_obj *target_column; + grn_operator_exec_func *greater; + grn_operator_exec_func *less; + grn_table_cursor *cursor; + grn_id id; + grn_obj value; + + if (op == GRN_OP_AND) { + target_table = res; + } else { + target_table = table; + } + cursor = grn_table_cursor_open(ctx, target_table, + NULL, 0, + NULL, 0, + offset, limit, flags); + if (!cursor) { + return ctx->rc; + } + + if (data->value->header.type == GRN_BULK) { + target_column = grn_obj_column(ctx, + table, + GRN_TEXT_VALUE(data->value), + GRN_TEXT_LEN(data->value)); + } else { + target_column = data->value; + } + if (data->min_border_type == BETWEEN_BORDER_INCLUDE) { + greater = grn_operator_exec_greater_equal; + } else { + greater = grn_operator_exec_greater; + } + if (data->max_border_type == BETWEEN_BORDER_INCLUDE) { + less = grn_operator_exec_less_equal; + } else { + less = grn_operator_exec_less; + } + + GRN_VOID_INIT(&value); + while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) { + grn_id record_id; + + if (target_table == res) { + grn_id *key; + grn_table_cursor_get_key(ctx, cursor, (void **)&key); + record_id = *key; + } else { + record_id = id; + } + + GRN_BULK_REWIND(&value); + grn_obj_get_value(ctx, target_column, record_id, &value); + if (greater(ctx, &value, data->min) && less(ctx, &value, data->max)) { + grn_posting posting; + posting.rid = record_id; + posting.sid = 1; + posting.pos = 0; + posting.weight = 0; + grn_ii_posting_add(ctx, &posting, (grn_hash *)res, op); + } + } + + GRN_OBJ_FIN(ctx, &value); + + if (target_column != data->value && + target_column->header.type == GRN_ACCESSOR) { + grn_obj_unlink(ctx, target_column); + } + + grn_table_cursor_close(ctx, cursor); + + grn_ii_resolve_sel_and(ctx, (grn_hash *)res, op); + } + + return GRN_SUCCESS; +} + +static grn_rc +selector_between(grn_ctx *ctx, + grn_obj *table, + grn_obj *index, + int nargs, + grn_obj **args, + grn_obj *res, + grn_operator op) +{ + grn_rc rc = GRN_SUCCESS; + int offset = 0; + int limit = -1; + int flags = GRN_CURSOR_ASCENDING | GRN_CURSOR_BY_KEY; + between_data data; + grn_bool use_sequential_search; + grn_obj *index_table = NULL; + grn_table_cursor *cursor; + grn_id id; + + between_data_init(ctx, &data); + rc = between_parse_args(ctx, nargs - 1, args + 1, &data); + if (rc != GRN_SUCCESS) { + goto exit; + } + + if (data.min_border_type == BETWEEN_BORDER_EXCLUDE) { + flags |= GRN_CURSOR_GT; + } + if (data.max_border_type == BETWEEN_BORDER_EXCLUDE) { + flags |= GRN_CURSOR_LT; + } + + if (data.value->header.type == GRN_COLUMN_INDEX) { + index = data.value; + } + + if (index) { + switch (index->header.type) { + case GRN_TABLE_NO_KEY : + case GRN_TABLE_HASH_KEY : + break; + case GRN_TABLE_PAT_KEY : + case GRN_TABLE_DAT_KEY : + index_table = index; + index = NULL; + break; + default : + index_table = grn_ctx_at(ctx, index->header.domain); + break; + } + } + + if (index_table) { + double ratio = grn_between_too_many_index_match_ratio; + use_sequential_search = + selector_between_sequential_search_should_use(ctx, + table, + index, + index_table, + &data, + res, + op, + ratio); + } else { + use_sequential_search = GRN_TRUE; + } + if (use_sequential_search) { + rc = selector_between_sequential_search(ctx, table, &data, res, op); + goto exit; + } + + cursor = grn_table_cursor_open(ctx, index_table, + GRN_BULK_HEAD(data.min), + GRN_BULK_VSIZE(data.min), + GRN_BULK_HEAD(data.max), + GRN_BULK_VSIZE(data.max), + offset, limit, flags); + if (!cursor) { + rc = ctx->rc; + goto exit; + } + + if (index) { + while ((id = grn_table_cursor_next(ctx, cursor))) { + grn_ii_at(ctx, (grn_ii *)index, id, (grn_hash *)res, op); + } + } else { + grn_posting posting; + memset(&posting, 0, sizeof(grn_posting)); + posting.sid = 1; + posting.pos = 0; + while ((id = grn_table_cursor_next(ctx, cursor))) { + posting.rid = id; + grn_ii_posting_add(ctx, &posting, (grn_hash *)res, op); + } + } + grn_ii_resolve_sel_and(ctx, (grn_hash *)res, op); + grn_table_cursor_close(ctx, cursor); + +exit : + between_data_fin(ctx, &data); + + return rc; +} + +static grn_obj * +func_in_values(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *found; + grn_obj *target_value; + int i; + + found = GRN_PROC_ALLOC(GRN_DB_BOOL, 0); + if (!found) { + return NULL; + } + GRN_BOOL_SET(ctx, found, GRN_FALSE); + + if (nargs < 1) { + ERR(GRN_INVALID_ARGUMENT, + "in_values(): wrong number of arguments (%d for 1..)", nargs); + return found; + } + + target_value = args[0]; + for (i = 1; i < nargs; i++) { + grn_obj *value = args[i]; + grn_bool result; + + result = grn_operator_exec_equal(ctx, target_value, value); + if (ctx->rc) { + break; + } + + if (result) { + GRN_BOOL_SET(ctx, found, GRN_TRUE); + break; + } + } + + return found; +} + +static grn_bool +is_reference_type_column(grn_ctx *ctx, grn_obj *column) +{ + grn_bool is_reference_type; + grn_obj *range; + + range = grn_ctx_at(ctx, grn_obj_get_range(ctx, column)); + switch (range->header.type) { + case GRN_TABLE_HASH_KEY : + case GRN_TABLE_PAT_KEY : + case GRN_TABLE_DAT_KEY : + is_reference_type = GRN_TRUE; + break; + default : + is_reference_type = GRN_FALSE; + break; + } + grn_obj_unlink(ctx, range); + + return is_reference_type; +} + +static grn_obj * +selector_in_values_find_source(grn_ctx *ctx, grn_obj *index, grn_obj *res) +{ + grn_id source_id = GRN_ID_NIL; + grn_obj source_ids; + unsigned int n_source_ids; + + GRN_UINT32_INIT(&source_ids, GRN_OBJ_VECTOR); + grn_obj_get_info(ctx, index, GRN_INFO_SOURCE, &source_ids); + n_source_ids = GRN_BULK_VSIZE(&source_ids) / sizeof(grn_id); + if (n_source_ids == 1) { + source_id = GRN_UINT32_VALUE_AT(&source_ids, 0); + } + GRN_OBJ_FIN(ctx, &source_ids); + + if (source_id == GRN_ID_NIL) { + return NULL; + } else { + return grn_ctx_at(ctx, source_id); + } +} + +static grn_bool +selector_in_values_sequential_search(grn_ctx *ctx, + grn_obj *table, + grn_obj *index, + int n_values, + grn_obj **values, + grn_obj *res, + grn_operator op) +{ + grn_obj *source; + int n_existing_records; + + if (grn_in_values_too_many_index_match_ratio < 0.0) { + return GRN_FALSE; + } + + if (op != GRN_OP_AND) { + return GRN_FALSE; + } + + if (index->header.flags & GRN_OBJ_WITH_WEIGHT) { + return GRN_FALSE; + } + + n_existing_records = grn_table_size(ctx, res); + if (n_existing_records == 0) { + return GRN_TRUE; + } + + source = selector_in_values_find_source(ctx, index, res); + if (!source) { + return GRN_FALSE; + } + + if (!is_reference_type_column(ctx, source)) { + grn_obj_unlink(ctx, source); + return GRN_FALSE; + } + + { + grn_obj value_ids; + int i, n_value_ids; + int n_indexed_records = 0; + + { + grn_id range_id; + grn_obj *range; + + range_id = grn_obj_get_range(ctx, source); + range = grn_ctx_at(ctx, range_id); + if (!range) { + grn_obj_unlink(ctx, source); + return GRN_FALSE; + } + + GRN_RECORD_INIT(&value_ids, GRN_OBJ_VECTOR, range_id); + for (i = 0; i < n_values; i++) { + grn_obj *value = values[i]; + grn_id value_id; + + value_id = grn_table_get(ctx, range, + GRN_TEXT_VALUE(value), GRN_TEXT_LEN(value)); + if (value_id == GRN_ID_NIL) { + continue; + } + GRN_RECORD_PUT(ctx, &value_ids, value_id); + } + grn_obj_unlink(ctx, range); + } + + n_value_ids = GRN_BULK_VSIZE(&value_ids) / sizeof(grn_id); + for (i = 0; i < n_value_ids; i++) { + grn_id value_id = GRN_RECORD_VALUE_AT(&value_ids, i); + n_indexed_records += grn_ii_estimate_size(ctx, (grn_ii *)index, value_id); + } + + /* + * Same as: + * ((n_existing_record / n_indexed_records) > + * grn_in_values_too_many_index_match_ratio) + */ + if (n_existing_records > + (n_indexed_records * grn_in_values_too_many_index_match_ratio)) { + grn_obj_unlink(ctx, &value_ids); + grn_obj_unlink(ctx, source); + return GRN_FALSE; + } + + { + grn_obj *accessor; + char local_source_name[GRN_TABLE_MAX_KEY_SIZE]; + int local_source_name_length; + + local_source_name_length = grn_column_name(ctx, source, + local_source_name, + GRN_TABLE_MAX_KEY_SIZE); + grn_obj_unlink(ctx, source); + accessor = grn_obj_column(ctx, res, + local_source_name, + local_source_name_length); + { + grn_table_cursor *cursor; + grn_id id; + grn_obj record_value; + + GRN_VOID_INIT(&record_value); + cursor = grn_table_cursor_open(ctx, res, + NULL, 0, NULL, 0, + 0, -1, GRN_CURSOR_ASCENDING); + while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) { + grn_id *record_id; + grn_table_cursor_get_key(ctx, cursor, (void **)&record_id); + GRN_BULK_REWIND(&record_value); + grn_obj_get_value(ctx, accessor, id, &record_value); + for (i = 0; i < n_value_ids; i++) { + grn_id value_id = GRN_RECORD_VALUE_AT(&value_ids, i); + switch (record_value.header.type) { + case GRN_BULK : + if (value_id == GRN_RECORD_VALUE(&record_value)) { + grn_posting posting; + posting.rid = *record_id; + posting.sid = 1; + posting.pos = 0; + posting.weight = 0; + grn_ii_posting_add(ctx, &posting, (grn_hash *)res, op); + } + break; + case GRN_UVECTOR : + { + int j, n_elements; + n_elements = GRN_BULK_VSIZE(&record_value) / sizeof(grn_id); + for (j = 0; j < n_elements; j++) { + if (value_id == GRN_RECORD_VALUE_AT(&record_value, j)) { + grn_posting posting; + posting.rid = *record_id; + posting.sid = 1; + posting.pos = 0; + posting.weight = 0; + grn_ii_posting_add(ctx, &posting, (grn_hash *)res, op); + } + } + } + break; + default : + break; + } + } + } + grn_table_cursor_close(ctx, cursor); + grn_ii_resolve_sel_and(ctx, (grn_hash *)res, op); + GRN_OBJ_FIN(ctx, &record_value); + } + grn_obj_unlink(ctx, accessor); + } + grn_obj_unlink(ctx, &value_ids); + } + grn_obj_unlink(ctx, source); + + return GRN_TRUE; +} + +static grn_rc +selector_in_values(grn_ctx *ctx, grn_obj *table, grn_obj *index, + int nargs, grn_obj **args, + grn_obj *res, grn_operator op) +{ + grn_rc rc = GRN_SUCCESS; + int i, n_values; + grn_obj **values; + + if (!index) { + return GRN_INVALID_ARGUMENT; + } + + if (nargs < 2) { + ERR(GRN_INVALID_ARGUMENT, + "in_values(): wrong number of arguments (%d for 1..)", nargs); + return ctx->rc; + } + + n_values = nargs - 2; + values = args + 2; + + if (n_values == 0) { + return rc; + } + + if (selector_in_values_sequential_search(ctx, table, index, + n_values, values, + res, op)) { + return ctx->rc; + } + + ctx->flags |= GRN_CTX_TEMPORARY_DISABLE_II_RESOLVE_SEL_AND; + for (i = 0; i < n_values; i++) { + grn_obj *value = values[i]; + grn_search_optarg search_options; + memset(&search_options, 0, sizeof(grn_search_optarg)); + search_options.mode = GRN_OP_EXACT; + search_options.similarity_threshold = 0; + search_options.max_interval = 0; + search_options.weight_vector = NULL; + search_options.vector_size = 0; + search_options.proc = NULL; + search_options.max_size = 0; + search_options.scorer = NULL; + if (i == n_values - 1) { + ctx->flags &= ~GRN_CTX_TEMPORARY_DISABLE_II_RESOLVE_SEL_AND; + } + rc = grn_obj_search(ctx, index, value, res, op, &search_options); + if (rc != GRN_SUCCESS) { + break; + } + } + + return rc; +} + +static grn_obj * +proc_range_filter(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *table_name = VAR(0); + grn_obj *column_name = VAR(1); + grn_obj *min = VAR(2); + grn_obj *min_border = VAR(3); + grn_obj *max = VAR(4); + grn_obj *max_border = VAR(5); + grn_obj *offset = VAR(6); + grn_obj *limit = VAR(7); + grn_obj *filter = VAR(8); + grn_obj *output_columns = VAR(9); + grn_obj *table; + grn_obj *res = NULL; + grn_obj *filter_expr = NULL; + grn_obj *filter_variable = NULL; + int real_offset; + int real_limit; + + table = grn_ctx_get(ctx, GRN_TEXT_VALUE(table_name), GRN_TEXT_LEN(table_name)); + if (!table) { + ERR(GRN_INVALID_ARGUMENT, + "[range_filter] nonexistent table <%.*s>", + (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name)); + return NULL; + } + + if (GRN_TEXT_LEN(filter) > 0) { + GRN_EXPR_CREATE_FOR_QUERY(ctx, table, filter_expr, filter_variable); + if (!filter_expr) { + ERR(GRN_INVALID_ARGUMENT, + "[range_filter] failed to create expression"); + goto exit; + } + + grn_expr_parse(ctx, filter_expr, + GRN_TEXT_VALUE(filter), GRN_TEXT_LEN(filter), + NULL, GRN_OP_MATCH, GRN_OP_AND, GRN_EXPR_SYNTAX_SCRIPT); + if (ctx->rc != GRN_SUCCESS) { + goto exit; + } + } + + res = grn_table_create(ctx, NULL, 0, NULL, + GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, + table, NULL); + if (!res) { + ERR(GRN_INVALID_ARGUMENT, + "[range_filter] failed to result table"); + goto exit; + } + + { + grn_obj int32_value; + + GRN_INT32_INIT(&int32_value, 0); + + if (GRN_TEXT_LEN(offset) > 0) { + if (grn_obj_cast(ctx, offset, &int32_value, GRN_FALSE) != GRN_SUCCESS) { + ERR(GRN_INVALID_ARGUMENT, + "[range_filter] invalid offset format: <%.*s>", + (int)GRN_TEXT_LEN(offset), GRN_TEXT_VALUE(offset)); + GRN_OBJ_FIN(ctx, &int32_value); + goto exit; + } + real_offset = GRN_INT32_VALUE(&int32_value); + } else { + real_offset = 0; + } + + GRN_BULK_REWIND(&int32_value); + + if (GRN_TEXT_LEN(limit) > 0) { + if (grn_obj_cast(ctx, limit, &int32_value, GRN_FALSE) != GRN_SUCCESS) { + ERR(GRN_INVALID_ARGUMENT, + "[range_filter] invalid limit format: <%.*s>", + (int)GRN_TEXT_LEN(limit), GRN_TEXT_VALUE(limit)); + GRN_OBJ_FIN(ctx, &int32_value); + goto exit; + } + real_limit = GRN_INT32_VALUE(&int32_value); + } else { + real_limit = GRN_SELECT_DEFAULT_LIMIT; + } + + GRN_OBJ_FIN(ctx, &int32_value); + } + { + grn_rc rc; + int original_offset = real_offset; + int original_limit = real_limit; + rc = grn_normalize_offset_and_limit(ctx, grn_table_size(ctx, table), + &real_offset, &real_limit); + switch (rc) { + case GRN_TOO_SMALL_OFFSET : + ERR(GRN_INVALID_ARGUMENT, + "[range_filter] too small offset: <%d>", original_offset); + goto exit; + case GRN_TOO_LARGE_OFFSET : + ERR(GRN_INVALID_ARGUMENT, + "[range_filter] too large offset: <%d>", original_offset); + goto exit; + case GRN_TOO_SMALL_LIMIT : + ERR(GRN_INVALID_ARGUMENT, + "[range_filter] too small limit: <%d>", original_limit); + goto exit; + default : + break; + } + } + + if (real_limit != 0) { + grn_table_sort_key *sort_keys; + unsigned int n_sort_keys; + sort_keys = grn_table_sort_key_from_str(ctx, + GRN_TEXT_VALUE(column_name), + GRN_TEXT_LEN(column_name), + table, + &n_sort_keys); + if (n_sort_keys == 1) { + grn_table_sort_key *sort_key; + grn_obj *index; + int n_indexes; + grn_operator op = GRN_OP_OR; + + sort_key = &(sort_keys[0]); + n_indexes = grn_column_index(ctx, sort_key->key, GRN_OP_LESS, + &index, 1, NULL); + if (n_indexes > 0) { + grn_obj *lexicon; + grn_table_cursor *table_cursor; + int table_cursor_flags = 0; + between_border_type min_border_type; + between_border_type max_border_type; + grn_obj real_min; + grn_obj real_max; + int n_records = 0; + grn_obj *index_cursor; + int index_cursor_flags = 0; + grn_posting *posting; + + lexicon = grn_ctx_at(ctx, index->header.domain); + if (sort_key->flags & GRN_TABLE_SORT_DESC) { + table_cursor_flags |= GRN_CURSOR_DESCENDING; + } else { + table_cursor_flags |= GRN_CURSOR_ASCENDING; + } + if (GRN_TEXT_LEN(min_border) > 0) { + min_border_type = between_parse_border(ctx, min_border, "min_border"); + } else { + min_border_type = BETWEEN_BORDER_INCLUDE; + } + if (GRN_TEXT_LEN(max_border) > 0) { + max_border_type = between_parse_border(ctx, max_border, "max_border"); + } else { + max_border_type = BETWEEN_BORDER_INCLUDE; + } + if (min_border_type == BETWEEN_BORDER_EXCLUDE) { + table_cursor_flags |= GRN_CURSOR_GT; + } + if (max_border_type == BETWEEN_BORDER_EXCLUDE) { + table_cursor_flags |= GRN_CURSOR_LT; + } + GRN_OBJ_INIT(&real_min, GRN_BULK, 0, lexicon->header.domain); + GRN_OBJ_INIT(&real_max, GRN_BULK, 0, lexicon->header.domain); + if (GRN_TEXT_LEN(min) > 0) { + grn_obj_cast(ctx, min, &real_min, GRN_FALSE); + } + if (GRN_TEXT_LEN(max) > 0) { + grn_obj_cast(ctx, max, &real_max, GRN_FALSE); + } + table_cursor = grn_table_cursor_open(ctx, lexicon, + GRN_BULK_HEAD(&real_min), + GRN_BULK_VSIZE(&real_min), + GRN_BULK_HEAD(&real_max), + GRN_BULK_VSIZE(&real_max), + 0, -1, table_cursor_flags); + index_cursor = grn_index_cursor_open(ctx, table_cursor, + index, GRN_ID_NIL, GRN_ID_NIL, + index_cursor_flags); + while ((posting = grn_index_cursor_next(ctx, index_cursor, NULL))) { + grn_bool result_boolean = GRN_FALSE; + + if (filter_expr) { + grn_obj *result; + GRN_RECORD_SET(ctx, filter_variable, posting->rid); + result = grn_expr_exec(ctx, filter_expr, 0); + if (ctx->rc) { + break; + } + result_boolean = grn_obj_is_true(ctx, result); + } else { + result_boolean = GRN_TRUE; + } + + if (result_boolean) { + if (n_records >= real_offset) { + grn_ii_posting_add(ctx, posting, (grn_hash *)res, op); + } + n_records++; + if (n_records == real_limit) { + break; + } + } + } + grn_obj_unlink(ctx, index_cursor); + grn_table_cursor_close(ctx, table_cursor); + + GRN_OBJ_FIN(ctx, &real_min); + GRN_OBJ_FIN(ctx, &real_max); + } + grn_ii_resolve_sel_and(ctx, (grn_hash *)res, op); + } + grn_table_sort_key_close(ctx, sort_keys, n_sort_keys); + } + + if (ctx->rc == GRN_SUCCESS) { + const char *raw_output_columns; + int raw_output_columns_len; + + raw_output_columns = GRN_TEXT_VALUE(output_columns); + raw_output_columns_len = GRN_TEXT_LEN(output_columns); + if (raw_output_columns_len == 0) { + raw_output_columns = GRN_SELECT_DEFAULT_OUTPUT_COLUMNS; + raw_output_columns_len = strlen(raw_output_columns); + } + grn_proc_select_output_columns(ctx, res, -1, real_offset, real_limit, + raw_output_columns, + raw_output_columns_len, + filter_expr); + } + +exit : + if (filter_expr) { + grn_obj_unlink(ctx, filter_expr); + } + if (res) { + grn_obj_unlink(ctx, res); + } + + return NULL; +} + +static grn_obj * +proc_request_cancel(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *id = VAR(0); + grn_bool canceled; + + if (GRN_TEXT_LEN(id) == 0) { + ERR(GRN_INVALID_ARGUMENT, "[request_cancel] ID is missing"); + return NULL; + } + + canceled = grn_request_canceler_cancel(GRN_TEXT_VALUE(id), GRN_TEXT_LEN(id)); + + GRN_OUTPUT_MAP_OPEN("result", 2); + GRN_OUTPUT_CSTR("id"); + GRN_OUTPUT_STR(GRN_TEXT_VALUE(id), GRN_TEXT_LEN(id)); + GRN_OUTPUT_CSTR("canceled"); + GRN_OUTPUT_BOOL(canceled); + GRN_OUTPUT_MAP_CLOSE(); + + return NULL; +} + +static grn_obj * +proc_plugin_register(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + if (GRN_TEXT_LEN(VAR(0))) { + const char *name; + GRN_TEXT_PUTC(ctx, VAR(0), '\0'); + name = GRN_TEXT_VALUE(VAR(0)); + grn_plugin_register(ctx, name); + } else { + ERR(GRN_INVALID_ARGUMENT, "[plugin_register] name is missing"); + } + GRN_OUTPUT_BOOL(!ctx->rc); + return NULL; +} + +static grn_obj * +proc_plugin_unregister(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + if (GRN_TEXT_LEN(VAR(0))) { + const char *name; + GRN_TEXT_PUTC(ctx, VAR(0), '\0'); + name = GRN_TEXT_VALUE(VAR(0)); + grn_plugin_unregister(ctx, name); + } else { + ERR(GRN_INVALID_ARGUMENT, "[plugin_unregister] name is missing"); + } + GRN_OUTPUT_BOOL(!ctx->rc); + return NULL; +} + +static grn_obj * +proc_io_flush(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *target_name; + grn_obj *recursive; + grn_obj *only_opened; + grn_obj *target; + grn_bool is_recursive; + grn_bool is_only_opened; + + target_name = VAR(0); + recursive = VAR(1); + only_opened = VAR(2); + + if (GRN_TEXT_LEN(target_name) > 0) { + target = grn_ctx_get(ctx, + GRN_TEXT_VALUE(target_name), + GRN_TEXT_LEN(target_name)); + if (!target) { + ERR(GRN_INVALID_ARGUMENT, "[io_flush] unknown target: <%.*s>", + (int)GRN_TEXT_LEN(target_name), + GRN_TEXT_VALUE(target_name)); + GRN_OUTPUT_BOOL(GRN_FALSE); + return NULL; + } + } else { + target = grn_ctx_db(ctx); + } + + is_recursive = grn_proc_option_value_bool(ctx, recursive, GRN_TRUE); + is_only_opened = grn_proc_option_value_bool(ctx, only_opened, GRN_FALSE); + { + grn_rc rc; + if (target->header.type == GRN_DB && is_only_opened) { + rc = grn_obj_flush(ctx, target); + if (rc == GRN_SUCCESS) { + GRN_TABLE_EACH_BEGIN_FLAGS(ctx, target, cursor, id, GRN_CURSOR_BY_ID) { + grn_obj *sub_target; + + if (id < GRN_N_RESERVED_TYPES) { + continue; + } + + if (!grn_ctx_is_opened(ctx, id)) { + continue; + } + + sub_target = grn_ctx_at(ctx, id); + rc = grn_obj_flush(ctx, sub_target); + if (rc != GRN_SUCCESS) { + break; + } + } GRN_TABLE_EACH_END(ctx, cursor); + } + } else { + if (is_recursive) { + rc = grn_obj_flush_recursive(ctx, target); + } else { + rc = grn_obj_flush(ctx, target); + } + } + GRN_OUTPUT_BOOL(rc == GRN_SUCCESS); + } + + + return NULL; +} + +static grn_obj * +proc_thread_limit(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *max_bulk; + uint32_t current_limit; + + current_limit = grn_thread_get_limit(); + GRN_OUTPUT_INT64(current_limit); + + max_bulk = VAR(0); + if (GRN_TEXT_LEN(max_bulk) > 0) { + uint32_t max; + const char *max_text = GRN_TEXT_VALUE(max_bulk); + const char *max_text_end; + const char *max_text_rest; + + max_text_end = max_text + GRN_TEXT_LEN(max_bulk); + max = grn_atoui(max_text, max_text_end, &max_text_rest); + if (max_text_rest != max_text_end) { + ERR(GRN_INVALID_ARGUMENT, + "[thread_limit] max must be unsigned integer value: <%.*s>", + (int)GRN_TEXT_LEN(max_bulk), + max_text); + return NULL; + } + if (max == 0) { + ERR(GRN_INVALID_ARGUMENT, + "[thread_limit] max must be 1 or larger: <%.*s>", + (int)GRN_TEXT_LEN(max_bulk), + max_text); + return NULL; + } + grn_thread_set_limit(max); + } + + return NULL; +} + +static grn_obj * +proc_database_unmap(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_rc rc; + uint32_t current_limit; + + current_limit = grn_thread_get_limit(); + if (current_limit != 1) { + ERR(GRN_OPERATION_NOT_PERMITTED, + "[database_unmap] the max number of threads must be 1: <%u>", + current_limit); + GRN_OUTPUT_BOOL(GRN_FALSE); + return NULL; + } + + rc = grn_db_unmap(ctx, grn_ctx_db(ctx)); + GRN_OUTPUT_BOOL(rc == GRN_SUCCESS); + + return NULL; +} + +static grn_obj * +proc_reindex(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *target_name; + grn_obj *target; + + target_name = VAR(0); + if (GRN_TEXT_LEN(target_name) == 0) { + target = grn_ctx_db(ctx); + } else { + target = grn_ctx_get(ctx, + GRN_TEXT_VALUE(target_name), + GRN_TEXT_LEN(target_name)); + if (!target) { + ERR(GRN_INVALID_ARGUMENT, + "[reindex] nonexistent target: <%.*s>", + (int)GRN_TEXT_LEN(target_name), + GRN_TEXT_VALUE(target_name)); + GRN_OUTPUT_BOOL(GRN_FALSE); + return NULL; + } + } + + grn_obj_reindex(ctx, target); + + GRN_OUTPUT_BOOL(ctx->rc == GRN_SUCCESS); + + return NULL; +} + +static grn_rc +selector_prefix_rk_search_key(grn_ctx *ctx, + grn_obj *table, + grn_obj *column, + grn_obj *query, + grn_obj *res, + grn_operator op) +{ + grn_rc rc = GRN_SUCCESS; + + if (!grn_obj_is_key_accessor(ctx, column)) { + grn_obj inspected_column; + GRN_TEXT_INIT(&inspected_column, 0); + grn_inspect(ctx, &inspected_column, column); + ERR(GRN_INVALID_ARGUMENT, + "prefix_rk_serach(): column must be _key: %.*s", + (int)GRN_TEXT_LEN(&inspected_column), + GRN_TEXT_VALUE(&inspected_column)); + rc = ctx->rc; + GRN_OBJ_FIN(ctx, &inspected_column); + goto exit; + } + + if (table->header.type != GRN_TABLE_PAT_KEY) { + grn_obj inspected_table; + GRN_TEXT_INIT(&inspected_table, 0); + grn_inspect(ctx, &inspected_table, table); + ERR(GRN_INVALID_ARGUMENT, + "prefix_rk_serach(): table of _key must TABLE_PAT_KEY: %.*s", + (int)GRN_TEXT_LEN(&inspected_table), + GRN_TEXT_VALUE(&inspected_table)); + rc = ctx->rc; + GRN_OBJ_FIN(ctx, &inspected_table); + goto exit; + } + + GRN_TABLE_EACH_BEGIN_MIN(ctx, + table, + cursor, + id, + GRN_TEXT_VALUE(query), + GRN_TEXT_LEN(query), + GRN_CURSOR_PREFIX | GRN_CURSOR_RK) { + grn_posting posting; + posting.rid = id; + posting.sid = 1; + posting.pos = 0; + posting.weight = 0; + grn_ii_posting_add(ctx, &posting, (grn_hash *)res, op); + } GRN_TABLE_EACH_END(ctx, cursor); + grn_ii_resolve_sel_and(ctx, (grn_hash *)res, op); + +exit : + return rc; +} + +static grn_rc +selector_prefix_rk_search_index(grn_ctx *ctx, + grn_obj *index, + grn_obj *query, + grn_obj *res, + grn_operator op) +{ + grn_rc rc = GRN_SUCCESS; + grn_obj *table; + + table = grn_column_table(ctx, index); + + GRN_TABLE_EACH_BEGIN_MIN(ctx, + table, + cursor, + id, + GRN_TEXT_VALUE(query), + GRN_TEXT_LEN(query), + GRN_CURSOR_PREFIX | GRN_CURSOR_RK) { + grn_ii_at(ctx, (grn_ii *)index, id, (grn_hash *)res, op); + } GRN_TABLE_EACH_END(ctx, cursor); + grn_ii_resolve_sel_and(ctx, (grn_hash *)res, op); + + return rc; +} + +static grn_rc +selector_prefix_rk_search(grn_ctx *ctx, + grn_obj *table, + grn_obj *index, + int nargs, + grn_obj **args, + grn_obj *res, + grn_operator op) +{ + grn_rc rc = GRN_SUCCESS; + grn_obj *column; + grn_obj *query; + + if ((nargs - 1) != 2) { + ERR(GRN_INVALID_ARGUMENT, + "prefix_rk_serach(): wrong number of arguments (%d for 2)", nargs - 1); + return ctx->rc; + } + + column = args[1]; + query = args[2]; + + if (index) { + rc = selector_prefix_rk_search_index(ctx, index, query, res, op); + } else if (grn_obj_is_accessor(ctx, column) && + ((grn_accessor *)column)->next) { + grn_obj *accessor = column; + unsigned int accessor_deep = 0; + grn_obj *base_table = NULL; + grn_obj *base_column = NULL; + grn_obj *base_index = NULL; + grn_obj *base_res = NULL; + grn_accessor *a; + + for (a = (grn_accessor *)accessor; a; a = a->next) { + if (a->next) { + accessor_deep++; + } else { + if (grn_obj_is_data_column(ctx, a->obj)) { + grn_operator selector_op; + grn_index_datum index_data; + unsigned int n_index_datum; + + selector_op = grn_proc_get_selector_operator(ctx, args[0]); + base_column = a->obj; + base_table = grn_column_table(ctx, a->obj); + n_index_datum = grn_column_find_index_data(ctx, + base_column, + selector_op, + &index_data, + 1); + if (n_index_datum > 0) { + base_index = index_data.index; + } + } else { + base_column = (grn_obj *)a; + base_table = a->obj; + } + base_res = grn_table_create(ctx, NULL, 0, NULL, + GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, + base_table, NULL); + } + } + if (base_index) { + rc = selector_prefix_rk_search_index(ctx, + base_index, + query, + base_res, + GRN_OP_OR); + } else { + rc = selector_prefix_rk_search_key(ctx, + base_table, + base_column, + query, + base_res, + GRN_OP_OR); + } + if (rc == GRN_SUCCESS) { + grn_accessor_resolve(ctx, + accessor, + accessor_deep, + base_res, + res, + op); + } + grn_obj_close(ctx, base_res); + } else { + rc = selector_prefix_rk_search_key(ctx, + table, + column, + query, + res, + op); + } + return rc; +} + +#define DEF_VAR(v,name_str) do {\ + (v).name = (name_str);\ + (v).name_size = GRN_STRLEN(name_str);\ + GRN_TEXT_INIT(&(v).value, 0);\ +} while (0) + +#define DEF_COMMAND(name, func, nvars, vars)\ + (grn_proc_create(ctx, (name), (sizeof(name) - 1),\ + GRN_PROC_COMMAND, (func), NULL, NULL, (nvars), (vars))) + +void +grn_db_init_builtin_commands(grn_ctx *ctx) +{ + grn_expr_var vars[10]; + + grn_proc_init_define_selector(ctx); + grn_proc_init_select(ctx); + + DEF_VAR(vars[0], "values"); + DEF_VAR(vars[1], "table"); + DEF_VAR(vars[2], "columns"); + DEF_VAR(vars[3], "ifexists"); + DEF_VAR(vars[4], "input_type"); + DEF_VAR(vars[5], "each"); + DEF_VAR(vars[6], "output_ids"); + DEF_VAR(vars[7], "output_errors"); + DEF_COMMAND("load", proc_load, 8, vars); + + DEF_COMMAND("status", proc_status, 0, vars); + + grn_proc_init_table_list(ctx); + + grn_proc_init_column_list(ctx); + + grn_proc_init_table_create(ctx); + + grn_proc_init_table_remove(ctx); + + grn_proc_init_table_rename(ctx); + + grn_proc_init_column_create(ctx); + + grn_proc_init_column_remove(ctx); + + grn_proc_init_column_rename(ctx); + + DEF_VAR(vars[0], "path"); + DEF_COMMAND(GRN_EXPR_MISSING_NAME, proc_missing, 1, vars); + + DEF_COMMAND("quit", proc_quit, 0, vars); + + DEF_VAR(vars[0], "mode"); + DEF_COMMAND("shutdown", proc_shutdown, 1, vars); + + grn_proc_init_clearlock(ctx); + grn_proc_init_lock_clear(ctx); + + DEF_VAR(vars[0], "target_name"); + DEF_VAR(vars[1], "threshold"); + DEF_COMMAND("defrag", proc_defrag, 2, vars); + + DEF_VAR(vars[0], "level"); + DEF_COMMAND("log_level", proc_log_level, 1, vars); + + DEF_VAR(vars[0], "level"); + DEF_VAR(vars[1], "message"); + DEF_COMMAND("log_put", proc_log_put, 2, vars); + + DEF_COMMAND("log_reopen", proc_log_reopen, 0, vars); + + DEF_VAR(vars[0], "table"); + DEF_VAR(vars[1], "key"); + DEF_VAR(vars[2], "id"); + DEF_VAR(vars[3], "filter"); + DEF_COMMAND("delete", proc_delete, 4, vars); + + DEF_VAR(vars[0], "max"); + DEF_COMMAND("cache_limit", proc_cache_limit, 1, vars); + + grn_proc_init_dump(ctx); + + /* Deprecated. Use "plugin_register" instead. */ + DEF_VAR(vars[0], "path"); + DEF_COMMAND("register", proc_register, 1, vars); + + DEF_VAR(vars[0], "obj"); + DEF_COMMAND("check", proc_check, 1, vars); + + DEF_VAR(vars[0], "target_name"); + DEF_VAR(vars[1], "table"); + DEF_COMMAND("truncate", proc_truncate, 2, vars); + + DEF_VAR(vars[0], "normalizer"); + DEF_VAR(vars[1], "string"); + DEF_VAR(vars[2], "flags"); + DEF_COMMAND("normalize", proc_normalize, 3, vars); + + grn_proc_init_tokenize(ctx); + grn_proc_init_table_tokenize(ctx); + + DEF_COMMAND("tokenizer_list", proc_tokenizer_list, 0, vars); + + DEF_COMMAND("normalizer_list", proc_normalizer_list, 0, vars); + + { + grn_obj *proc; + proc = grn_proc_create(ctx, "rand", -1, GRN_PROC_FUNCTION, func_rand, + NULL, NULL, 0, NULL); + grn_proc_set_is_stable(ctx, proc, GRN_FALSE); + } + + { + grn_obj *proc; + proc = grn_proc_create(ctx, "now", -1, GRN_PROC_FUNCTION, func_now, + NULL, NULL, 0, NULL); + grn_proc_set_is_stable(ctx, proc, GRN_FALSE); + } + + grn_proc_create(ctx, "max", -1, GRN_PROC_FUNCTION, func_max, + NULL, NULL, 0, NULL); + grn_proc_create(ctx, "min", -1, GRN_PROC_FUNCTION, func_min, + NULL, NULL, 0, NULL); + + { + grn_obj *selector_proc; + + selector_proc = grn_proc_create(ctx, "geo_in_circle", -1, GRN_PROC_FUNCTION, + func_geo_in_circle, NULL, NULL, 0, NULL); + grn_proc_set_selector(ctx, selector_proc, grn_selector_geo_in_circle); + /* We may need GRN_OP_GEO_IN_CIRCLE. */ + grn_proc_set_selector_operator(ctx, selector_proc, GRN_OP_MATCH); + + selector_proc = grn_proc_create(ctx, "geo_in_rectangle", -1, + GRN_PROC_FUNCTION, + func_geo_in_rectangle, NULL, NULL, 0, NULL); + grn_proc_set_selector(ctx, selector_proc, grn_selector_geo_in_rectangle); + /* We may need GRN_OP_GEO_IN_RECTANGLE. */ + grn_proc_set_selector_operator(ctx, selector_proc, GRN_OP_MATCH); + } + + grn_proc_create(ctx, "geo_distance", -1, GRN_PROC_FUNCTION, + func_geo_distance, NULL, NULL, 0, NULL); + + /* deprecated. */ + grn_proc_create(ctx, "geo_distance2", -1, GRN_PROC_FUNCTION, + func_geo_distance2, NULL, NULL, 0, NULL); + + /* deprecated. */ + grn_proc_create(ctx, "geo_distance3", -1, GRN_PROC_FUNCTION, + func_geo_distance3, NULL, NULL, 0, NULL); + + grn_proc_init_edit_distance(ctx); + + { + grn_obj *selector_proc; + + selector_proc = grn_proc_create(ctx, "all_records", -1, GRN_PROC_FUNCTION, + func_all_records, NULL, NULL, 0, NULL); + grn_proc_set_selector(ctx, selector_proc, selector_all_records); + grn_proc_set_selector_operator(ctx, selector_proc, GRN_OP_NOP); + } + + /* experimental */ + grn_proc_init_snippet_html(ctx); + + { + grn_obj *selector_proc; + + selector_proc = grn_proc_create(ctx, "query", -1, GRN_PROC_FUNCTION, + func_query, NULL, NULL, 0, NULL); + grn_proc_set_selector(ctx, selector_proc, selector_query); + grn_proc_set_selector_operator(ctx, selector_proc, GRN_OP_NOP); + } + + { + grn_obj *selector_proc; + + selector_proc = grn_proc_create(ctx, "sub_filter", -1, GRN_PROC_FUNCTION, + NULL, NULL, NULL, 0, NULL); + grn_proc_set_selector(ctx, selector_proc, selector_sub_filter); + grn_proc_set_selector_operator(ctx, selector_proc, GRN_OP_NOP); + } + + grn_proc_create(ctx, "html_untag", -1, GRN_PROC_FUNCTION, + func_html_untag, NULL, NULL, 0, NULL); + + { + grn_obj *selector_proc; + + selector_proc = grn_proc_create(ctx, "between", -1, GRN_PROC_FUNCTION, + func_between, NULL, NULL, 0, NULL); + grn_proc_set_selector(ctx, selector_proc, selector_between); + grn_proc_set_selector_operator(ctx, selector_proc, GRN_OP_LESS); + } + + grn_proc_init_highlight_html(ctx); + grn_proc_init_highlight_full(ctx); + + { + grn_obj *selector_proc; + + selector_proc = grn_proc_create(ctx, "in_values", -1, GRN_PROC_FUNCTION, + func_in_values, NULL, NULL, 0, NULL); + grn_proc_set_selector(ctx, selector_proc, selector_in_values); + grn_proc_set_selector_operator(ctx, selector_proc, GRN_OP_EQUAL); + } + + DEF_VAR(vars[0], "table"); + DEF_VAR(vars[1], "column"); + DEF_VAR(vars[2], "min"); + DEF_VAR(vars[3], "min_border"); + DEF_VAR(vars[4], "max"); + DEF_VAR(vars[5], "max_border"); + DEF_VAR(vars[6], "offset"); + DEF_VAR(vars[7], "limit"); + DEF_VAR(vars[8], "filter"); + DEF_VAR(vars[9], "output_columns"); + DEF_COMMAND("range_filter", proc_range_filter, 10, vars); + + DEF_VAR(vars[0], "id"); + DEF_COMMAND("request_cancel", proc_request_cancel, 1, vars); + + DEF_VAR(vars[0], "name"); + DEF_COMMAND("plugin_register", proc_plugin_register, 1, vars); + + DEF_VAR(vars[0], "name"); + DEF_COMMAND("plugin_unregister", proc_plugin_unregister, 1, vars); + + DEF_VAR(vars[0], "target_name"); + DEF_VAR(vars[1], "recursive"); + DEF_VAR(vars[2], "only_opened"); + DEF_COMMAND("io_flush", proc_io_flush, 3, vars); + + grn_proc_init_object_exist(ctx); + + DEF_VAR(vars[0], "max"); + DEF_COMMAND("thread_limit", proc_thread_limit, 1, vars); + + DEF_COMMAND("database_unmap", proc_database_unmap, 0, vars); + + grn_proc_init_column_copy(ctx); + + grn_proc_init_schema(ctx); + + DEF_VAR(vars[0], "target_name"); + DEF_COMMAND("reindex", proc_reindex, 1, vars); + + { + grn_obj *selector_proc; + + selector_proc = grn_proc_create(ctx, "prefix_rk_search", -1, + GRN_PROC_FUNCTION, + NULL, NULL, NULL, 0, NULL); + grn_proc_set_selector(ctx, selector_proc, selector_prefix_rk_search); + grn_proc_set_selector_operator(ctx, selector_proc, GRN_OP_PREFIX); + } + + grn_proc_init_config_get(ctx); + grn_proc_init_config_set(ctx); + grn_proc_init_config_delete(ctx); + + grn_proc_init_lock_acquire(ctx); + grn_proc_init_lock_release(ctx); + + grn_proc_init_object_inspect(ctx); + + grn_proc_init_fuzzy_search(ctx); + + grn_proc_init_object_remove(ctx); + + grn_proc_init_snippet(ctx); + grn_proc_init_highlight(ctx); + + grn_proc_init_query_expand(ctx); + + grn_proc_init_object_list(ctx); + + grn_proc_init_table_copy(ctx); + + grn_proc_init_in_records(ctx); + + grn_proc_init_query_log_flags_get(ctx); + grn_proc_init_query_log_flags_set(ctx); + grn_proc_init_query_log_flags_add(ctx); + grn_proc_init_query_log_flags_remove(ctx); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/Makefile.am b/storage/mroonga/vendor/groonga/lib/proc/Makefile.am new file mode 100644 index 00000000..e4284dc2 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/Makefile.am @@ -0,0 +1,17 @@ +AM_CPPFLAGS = \ + -I$(top_builddir) \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib + +AM_CFLAGS = \ + $(NO_STRICT_ALIASING_CFLAGS) \ + $(COVERAGE_CFLAGS) \ + $(GRN_CFLAGS) \ + $(MESSAGE_PACK_CFLAGS) \ + $(MRUBY_CFLAGS) + +noinst_LTLIBRARIES = libgrnproc.la + +include sources.am + +CLEANFILES = *.gcno *.gcda diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_column.c b/storage/mroonga/vendor/groonga/lib/proc/proc_column.c new file mode 100644 index 00000000..74d0d7a9 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_column.c @@ -0,0 +1,1019 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2009-2016 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_proc.h" + +#include "../grn_ctx.h" +#include "../grn_db.h" +#include "../grn_str.h" + +#include <groonga/plugin.h> + +grn_column_flags +grn_proc_column_parse_flags(grn_ctx *ctx, + const char *error_message_tag, + const char *text, + const char *end) +{ + grn_column_flags flags = 0; + while (text < end) { + size_t name_size; + + if (*text == '|' || *text == ' ') { + text += 1; + continue; + } + +#define CHECK_FLAG(name) \ + name_size = strlen(#name); \ + if ((unsigned long) (end - text) >= (unsigned long) name_size && \ + memcmp(text, #name, name_size) == 0) { \ + flags |= GRN_OBJ_ ## name; \ + text += name_size; \ + continue; \ + } + + CHECK_FLAG(COLUMN_SCALAR); + CHECK_FLAG(COLUMN_VECTOR); + CHECK_FLAG(COLUMN_INDEX); + CHECK_FLAG(COMPRESS_ZLIB); + CHECK_FLAG(COMPRESS_LZ4); + CHECK_FLAG(COMPRESS_ZSTD); + CHECK_FLAG(WITH_SECTION); + CHECK_FLAG(WITH_WEIGHT); + CHECK_FLAG(WITH_POSITION); + CHECK_FLAG(RING_BUFFER); + CHECK_FLAG(INDEX_SMALL); + CHECK_FLAG(INDEX_MEDIUM); + +#undef CHECK_FLAG + + ERR(GRN_INVALID_ARGUMENT, + "%s unknown flag: <%.*s>", + error_message_tag, + (int)(end - text), text); + return 0; + } + return flags; +} + +static grn_rc +command_column_create_resolve_source_name(grn_ctx *ctx, + grn_obj *table, + const char *source_name, + int source_name_length, + grn_obj *source_ids) +{ + grn_obj *column; + + column = grn_obj_column(ctx, table, source_name, source_name_length); + if (!column) { + ERR(GRN_INVALID_ARGUMENT, + "[column][create] nonexistent source: <%.*s>", + source_name_length, source_name); + return ctx->rc; + } + + if (column->header.type == GRN_ACCESSOR) { + if (strncmp(source_name, "_key", source_name_length) == 0) { + grn_id source_id = grn_obj_id(ctx, table); + GRN_UINT32_PUT(ctx, source_ids, source_id); + } else { + ERR(GRN_INVALID_ARGUMENT, + "[column][create] pseudo column except <_key> is invalid: <%.*s>", + source_name_length, source_name); + } + } else { + grn_id source_id = grn_obj_id(ctx, column); + GRN_UINT32_PUT(ctx, source_ids, source_id); + } + grn_obj_unlink(ctx, column); + + return ctx->rc; +} + +static grn_rc +command_column_create_resolve_source_names(grn_ctx *ctx, + grn_obj *table, + grn_obj *source_names, + grn_obj *source_ids) +{ + int i, names_length; + int start, source_name_length; + const char *names; + + names = GRN_TEXT_VALUE(source_names); + start = 0; + source_name_length = 0; + names_length = GRN_TEXT_LEN(source_names); + for (i = 0; i < names_length; i++) { + switch (names[i]) { + case ' ' : + if (source_name_length == 0) { + start++; + } + break; + case ',' : + { + grn_rc rc; + const char *source_name = names + start; + rc = command_column_create_resolve_source_name(ctx, + table, + source_name, + source_name_length, + source_ids); + if (rc) { + return rc; + } + start = i + 1; + source_name_length = 0; + } + break; + default : + source_name_length++; + break; + } + } + + if (source_name_length > 0) { + grn_rc rc; + const char *source_name = names + start; + rc = command_column_create_resolve_source_name(ctx, + table, + source_name, + source_name_length, + source_ids); + if (rc) { + return rc; + } + } + + return GRN_SUCCESS; +} + +static grn_obj * +command_column_create(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_bool succeeded = GRN_TRUE; + grn_obj *table; + grn_obj *column; + grn_obj *table_raw; + grn_obj *name; + grn_obj *flags_raw; + grn_obj *type_raw; + grn_obj *source_raw; + grn_column_flags flags; + grn_obj *type = NULL; + + table_raw = grn_plugin_proc_get_var(ctx, user_data, "table", -1); + name = grn_plugin_proc_get_var(ctx, user_data, "name", -1); + flags_raw = grn_plugin_proc_get_var(ctx, user_data, "flags", -1); + type_raw = grn_plugin_proc_get_var(ctx, user_data, "type", -1); + source_raw = grn_plugin_proc_get_var(ctx, user_data, "source", -1); + + table = grn_ctx_get(ctx, GRN_TEXT_VALUE(table_raw), GRN_TEXT_LEN(table_raw)); + if (!table) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[column][create] table doesn't exist: <%.*s>", + (int)GRN_TEXT_LEN(table_raw), + GRN_TEXT_VALUE(table_raw)); + succeeded = GRN_FALSE; + goto exit; + } + + { + const char *rest; + flags = grn_atoi(GRN_TEXT_VALUE(flags_raw), + GRN_BULK_CURR(flags_raw), + &rest); + if (GRN_TEXT_VALUE(flags_raw) == rest) { + flags = grn_proc_column_parse_flags(ctx, + "[column][create][flags]", + GRN_TEXT_VALUE(flags_raw), + GRN_BULK_CURR(flags_raw)); + if (ctx->rc) { + succeeded = GRN_FALSE; + goto exit; + } + } + } + + type = grn_ctx_get(ctx, + GRN_TEXT_VALUE(type_raw), + GRN_TEXT_LEN(type_raw)); + if (!type) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[column][create] type doesn't exist: <%.*s>", + (int)GRN_TEXT_LEN(type_raw), + GRN_TEXT_VALUE(type_raw)); + succeeded = GRN_FALSE; + goto exit; + } + + if (GRN_TEXT_LEN(name) == 0) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[column][create] name is missing"); + succeeded = GRN_FALSE; + goto exit; + } + flags |= GRN_OBJ_PERSISTENT; + + column = grn_column_create(ctx, table, + GRN_TEXT_VALUE(name), + GRN_TEXT_LEN(name), + NULL, flags, type); + if (!column) { + succeeded = GRN_FALSE; + goto exit; + } + + if (GRN_TEXT_LEN(source_raw) > 0) { + grn_rc rc; + grn_obj source_ids; + GRN_UINT32_INIT(&source_ids, GRN_OBJ_VECTOR); + rc = command_column_create_resolve_source_names(ctx, + type, + source_raw, + &source_ids); + if (rc == GRN_SUCCESS && GRN_BULK_VSIZE(&source_ids) > 0) { + grn_obj_set_info(ctx, column, GRN_INFO_SOURCE, &source_ids); + rc = ctx->rc; + } + GRN_OBJ_FIN(ctx, &source_ids); + if (rc != GRN_SUCCESS) { + grn_obj_remove(ctx, column); + succeeded = GRN_FALSE; + goto exit; + } + } + + grn_obj_unlink(ctx, column); + +exit : + grn_ctx_output_bool(ctx, succeeded); + if (table) { grn_obj_unlink(ctx, table); } + if (type) { grn_obj_unlink(ctx, type); } + + return NULL; +} + +void +grn_proc_init_column_create(grn_ctx *ctx) +{ + grn_expr_var vars[5]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "table", -1); + grn_plugin_expr_var_init(ctx, &(vars[1]), "name", -1); + grn_plugin_expr_var_init(ctx, &(vars[2]), "flags", -1); + grn_plugin_expr_var_init(ctx, &(vars[3]), "type", -1); + grn_plugin_expr_var_init(ctx, &(vars[4]), "source", -1); + grn_plugin_command_create(ctx, + "column_create", -1, + command_column_create, + 5, + vars); +} + +static grn_obj * +command_column_remove(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *table_raw; + grn_obj *name; + grn_obj *table; + grn_obj *column; + char fullname[GRN_TABLE_MAX_KEY_SIZE]; + unsigned int fullname_len; + + table_raw = grn_plugin_proc_get_var(ctx, user_data, "table", -1); + name = grn_plugin_proc_get_var(ctx, user_data, "name", -1); + + table = grn_ctx_get(ctx, + GRN_TEXT_VALUE(table_raw), + GRN_TEXT_LEN(table_raw)); + + fullname_len = grn_obj_name(ctx, table, fullname, GRN_TABLE_MAX_KEY_SIZE); + if (fullname_len == 0) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[column][remove] table isn't found: <%.*s>", + (int)GRN_TEXT_LEN(table_raw), + GRN_TEXT_VALUE(table_raw)); + grn_ctx_output_bool(ctx, GRN_FALSE); + return NULL; + } + + fullname[fullname_len] = GRN_DB_DELIMITER; + fullname_len++; + if (fullname_len + GRN_TEXT_LEN(name) > GRN_TABLE_MAX_KEY_SIZE) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[column][remove] column name is too long: <%d> > <%u>: " + "<%.*s>", + (int)GRN_TEXT_LEN(name), + GRN_TABLE_MAX_KEY_SIZE - fullname_len, + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name)); + grn_ctx_output_bool(ctx, GRN_FALSE); + return NULL; + } + grn_memcpy(fullname + fullname_len, + GRN_TEXT_VALUE(name), + GRN_TEXT_LEN(name)); + fullname_len += GRN_TEXT_LEN(name); + column = grn_ctx_get(ctx, fullname, fullname_len); + if (!column) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[column][remove] column isn't found: <%.*s%c%.*s>", + (int)GRN_TEXT_LEN(table_raw), + GRN_TEXT_VALUE(table_raw), + GRN_DB_DELIMITER, + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name)); + grn_ctx_output_bool(ctx, GRN_FALSE); + return NULL; + } + + grn_obj_remove(ctx, column); + grn_ctx_output_bool(ctx, ctx->rc == GRN_SUCCESS); + return NULL; +} + +void +grn_proc_init_column_remove(grn_ctx *ctx) +{ + grn_expr_var vars[2]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "table", -1); + grn_plugin_expr_var_init(ctx, &(vars[1]), "name", -1); + grn_plugin_command_create(ctx, + "column_remove", -1, + command_column_remove, + 2, + vars); +} + +static grn_obj * +command_column_rename(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_rc rc = GRN_SUCCESS; + grn_obj *table_raw; + grn_obj *name; + grn_obj *new_name; + grn_obj *table = NULL; + grn_obj *column = NULL; + + table_raw = grn_plugin_proc_get_var(ctx, user_data, "table", -1); + name = grn_plugin_proc_get_var(ctx, user_data, "name", -1); + new_name = grn_plugin_proc_get_var(ctx, user_data, "new_name", -1); + + if (GRN_TEXT_LEN(table_raw) == 0) { + rc = GRN_INVALID_ARGUMENT; + GRN_PLUGIN_ERROR(ctx, + rc, + "[column][rename] table name isn't specified"); + goto exit; + } + + table = grn_ctx_get(ctx, GRN_TEXT_VALUE(table_raw), GRN_TEXT_LEN(table_raw)); + if (!table) { + rc = GRN_INVALID_ARGUMENT; + GRN_PLUGIN_ERROR(ctx, + rc, + "[column][rename] table isn't found: <%.*s>", + (int)GRN_TEXT_LEN(table_raw), + GRN_TEXT_VALUE(table_raw)); + goto exit; + } + + if (GRN_TEXT_LEN(name) == 0) { + rc = GRN_INVALID_ARGUMENT; + GRN_PLUGIN_ERROR(ctx, + rc, + "[column][rename] column name isn't specified: <%.*s>", + (int)GRN_TEXT_LEN(table_raw), + GRN_TEXT_VALUE(table_raw)); + goto exit; + } + + column = grn_obj_column(ctx, table, + GRN_TEXT_VALUE(name), + GRN_TEXT_LEN(name)); + if (!column) { + rc = GRN_INVALID_ARGUMENT; + GRN_PLUGIN_ERROR(ctx, + rc, + "[column][rename] column isn't found: <%.*s%c%.*s>", + (int)GRN_TEXT_LEN(table_raw), + GRN_TEXT_VALUE(table_raw), + GRN_DB_DELIMITER, + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name)); + goto exit; + } + + if (GRN_TEXT_LEN(new_name) == 0) { + rc = GRN_INVALID_ARGUMENT; + GRN_PLUGIN_ERROR(ctx, + rc, + "[column][rename] new column name isn't specified: " + "<%.*s%c%.*s>", + (int)GRN_TEXT_LEN(table_raw), + GRN_TEXT_VALUE(table_raw), + GRN_DB_DELIMITER, + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name)); + goto exit; + } + + rc = grn_column_rename(ctx, column, + GRN_TEXT_VALUE(new_name), + GRN_TEXT_LEN(new_name)); + if (rc != GRN_SUCCESS && ctx->rc == GRN_SUCCESS) { + GRN_PLUGIN_ERROR(ctx, + rc, + "[column][rename] failed to rename: " + "<%.*s%c%.*s> -> <%.*s%c%.*s>", + (int)GRN_TEXT_LEN(table_raw), + GRN_TEXT_VALUE(table_raw), + GRN_DB_DELIMITER, + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name), + (int)GRN_TEXT_LEN(table_raw), + GRN_TEXT_VALUE(table_raw), + GRN_DB_DELIMITER, + (int)GRN_TEXT_LEN(new_name), + GRN_TEXT_VALUE(new_name)); + goto exit; + } + +exit : + grn_ctx_output_bool(ctx, rc == GRN_SUCCESS); + if (column) { grn_obj_unlink(ctx, column); } + if (table) { grn_obj_unlink(ctx, table); } + return NULL; +} + +void +grn_proc_init_column_rename(grn_ctx *ctx) +{ + grn_expr_var vars[3]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "table", -1); + grn_plugin_expr_var_init(ctx, &(vars[1]), "name", -1); + grn_plugin_expr_var_init(ctx, &(vars[2]), "new_name", -1); + grn_plugin_command_create(ctx, + "column_rename", -1, + command_column_rename, + 3, + vars); +} + +static void +output_column_name(grn_ctx *ctx, grn_obj *column) +{ + grn_obj bulk; + int name_len; + char name[GRN_TABLE_MAX_KEY_SIZE]; + + GRN_TEXT_INIT(&bulk, GRN_OBJ_DO_SHALLOW_COPY); + name_len = grn_column_name(ctx, column, name, GRN_TABLE_MAX_KEY_SIZE); + GRN_TEXT_SET(ctx, &bulk, name, name_len); + + grn_ctx_output_obj(ctx, &bulk, NULL); + GRN_OBJ_FIN(ctx, &bulk); +} + +static int +output_column_info(grn_ctx *ctx, grn_obj *column) +{ + grn_obj o; + grn_id id; + const char *type; + const char *path; + + switch (column->header.type) { + case GRN_COLUMN_FIX_SIZE: + type = "fix"; + break; + case GRN_COLUMN_VAR_SIZE: + type = "var"; + break; + case GRN_COLUMN_INDEX: + type = "index"; + break; + default: + GRN_LOG(ctx, GRN_LOG_NOTICE, "invalid header type %d\n", column->header.type); + return 0; + } + id = grn_obj_id(ctx, column); + path = grn_obj_path(ctx, column); + GRN_TEXT_INIT(&o, 0); + grn_ctx_output_array_open(ctx, "COLUMN", 8); + grn_ctx_output_int64(ctx, id); + output_column_name(ctx, column); + grn_ctx_output_cstr(ctx, path); + grn_ctx_output_cstr(ctx, type); + grn_dump_column_create_flags(ctx, grn_column_get_flags(ctx, column), &o); + grn_ctx_output_obj(ctx, &o, NULL); + grn_proc_output_object_id_name(ctx, column->header.domain); + grn_proc_output_object_id_name(ctx, grn_obj_get_range(ctx, column)); + { + grn_db_obj *obj = (grn_db_obj *)column; + grn_id *s = obj->source; + int i = 0, n = obj->source_size / sizeof(grn_id); + grn_ctx_output_array_open(ctx, "SOURCES", n); + for (i = 0; i < n; i++, s++) { + grn_proc_output_object_id_name(ctx, *s); + } + grn_ctx_output_array_close(ctx); + + } + /* output_obj_source(ctx, (grn_db_obj *)column); */ + grn_ctx_output_array_close(ctx); + GRN_OBJ_FIN(ctx, &o); + return 1; +} + +static grn_obj * +command_column_list(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *table_raw; + grn_obj *table; + grn_hash *cols; + grn_obj *col; + int column_list_size = -1; + + table_raw = grn_plugin_proc_get_var(ctx, user_data, "table", -1); + + table = grn_ctx_get(ctx, + GRN_TEXT_VALUE(table_raw), + GRN_TEXT_LEN(table_raw)); + if (!table) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[column][list] table doesn't exist: <%.*s>", + (int)GRN_TEXT_LEN(table_raw), + GRN_TEXT_VALUE(table_raw)); + return NULL; + } + + if (!grn_obj_is_table(ctx, table)) { + const char *type_name; + type_name = grn_obj_type_to_string(table->header.type); + grn_obj_unlink(ctx, table); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[column][list] not table: <%.*s>: <%s>", + (int)GRN_TEXT_LEN(table_raw), + GRN_TEXT_VALUE(table_raw), + type_name); + return NULL; + } + + column_list_size = 1; /* [header, (key), (COLUMNS)] */ + if (table->header.type != GRN_TABLE_NO_KEY) { + column_list_size++; + } + cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, + GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY); + if (!cols) { + grn_obj_unlink(ctx, table); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[column][list] " + "failed to create temporary table to list columns: <%.*s>", + (int)GRN_TEXT_LEN(table_raw), + GRN_TEXT_VALUE(table_raw)); + return NULL; + } + + column_list_size += grn_table_columns(ctx, table, NULL, 0, (grn_obj *)cols); + + grn_ctx_output_array_open(ctx, "COLUMN_LIST", column_list_size); + grn_ctx_output_array_open(ctx, "HEADER", 8); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "id"); + grn_ctx_output_cstr(ctx, "UInt32"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "name"); + grn_ctx_output_cstr(ctx, "ShortText"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "path"); + grn_ctx_output_cstr(ctx, "ShortText"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "type"); + grn_ctx_output_cstr(ctx, "ShortText"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "flags"); + grn_ctx_output_cstr(ctx, "ShortText"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "domain"); + grn_ctx_output_cstr(ctx, "ShortText"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "range"); + grn_ctx_output_cstr(ctx, "ShortText"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "source"); + grn_ctx_output_cstr(ctx, "ShortText"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_close(ctx); + + if ((col = grn_obj_column(ctx, table, + GRN_COLUMN_NAME_KEY, + GRN_COLUMN_NAME_KEY_LEN))) { + int name_len; + char name_buf[GRN_TABLE_MAX_KEY_SIZE]; + grn_id id; + grn_obj buf; + GRN_TEXT_INIT(&buf, 0); + grn_ctx_output_array_open(ctx, "COLUMN", 8); + id = grn_obj_id(ctx, table); + grn_ctx_output_int64(ctx, id); + grn_ctx_output_cstr(ctx, GRN_COLUMN_NAME_KEY); + grn_ctx_output_cstr(ctx, ""); + grn_ctx_output_cstr(ctx, ""); + grn_dump_column_create_flags(ctx, 0, &buf); + grn_ctx_output_obj(ctx, &buf, NULL); + name_len = grn_obj_name(ctx, table, name_buf, GRN_TABLE_MAX_KEY_SIZE); + grn_ctx_output_str(ctx, name_buf, name_len); + grn_proc_output_object_id_name(ctx, table->header.domain); + grn_ctx_output_array_open(ctx, "SOURCES", 0); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_close(ctx); + GRN_OBJ_FIN(ctx, &buf); + grn_obj_unlink(ctx, col); + } + { + grn_id *key; + GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, { + if ((col = grn_ctx_at(ctx, *key))) { + output_column_info(ctx, col); + grn_obj_unlink(ctx, col); + } + }); + } + grn_ctx_output_array_close(ctx); + grn_hash_close(ctx, cols); + grn_obj_unlink(ctx, table); + + return NULL; +} + +void +grn_proc_init_column_list(grn_ctx *ctx) +{ + grn_expr_var vars[1]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "table", -1); + grn_plugin_command_create(ctx, + "column_list", -1, + command_column_list, + 1, + vars); +} + +static grn_rc +command_column_copy_resolve_target(grn_ctx *ctx, + const char *label, + grn_obj *table_name, + grn_obj *column_name, + grn_obj **table, + grn_obj **column) +{ + if (GRN_TEXT_LEN(table_name) == 0) { + ERR(GRN_INVALID_ARGUMENT, + "[column][copy] %s table name isn't specified", + label); + return ctx->rc; + } + *table = grn_ctx_get(ctx, + GRN_TEXT_VALUE(table_name), + GRN_TEXT_LEN(table_name)); + if (!*table) { + ERR(GRN_INVALID_ARGUMENT, + "[column][copy] %s table isn't found: <%.*s>", + label, + (int)GRN_TEXT_LEN(table_name), + GRN_TEXT_VALUE(table_name)); + return ctx->rc; + } + + if (GRN_TEXT_LEN(column_name) == 0) { + ERR(GRN_INVALID_ARGUMENT, + "[column][copy] %s column name isn't specified: <%.*s>", + label, + (int)GRN_TEXT_LEN(table_name), + GRN_TEXT_VALUE(table_name)); + return ctx->rc; + } + *column = grn_obj_column(ctx, *table, + GRN_TEXT_VALUE(column_name), + GRN_TEXT_LEN(column_name)); + if (!*column) { + ERR(GRN_INVALID_ARGUMENT, + "[column][copy] %s column isn't found: <%.*s.%.*s>", + label, + (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name), + (int)GRN_TEXT_LEN(column_name), GRN_TEXT_VALUE(column_name)); + return ctx->rc; + } + + return ctx->rc; +} + +static void +command_column_copy_same_table(grn_ctx *ctx, grn_obj *table, + grn_obj *from_column, grn_obj *to_column) +{ + grn_table_cursor *cursor; + grn_id id; + grn_obj value; + + cursor = grn_table_cursor_open(ctx, table, + NULL, 0, + NULL, 0, + 0, -1, 0); + if (!cursor) { + return; + } + + GRN_VOID_INIT(&value); + while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) { + GRN_BULK_REWIND(&value); + grn_obj_get_value(ctx, from_column, id, &value); + grn_obj_set_value(ctx, to_column, id, &value, GRN_OBJ_SET); + } + GRN_OBJ_FIN(ctx, &value); + grn_table_cursor_close(ctx, cursor); +} + +static void +command_column_copy_same_key_type(grn_ctx *ctx, + grn_obj *from_table, + grn_obj *from_column, + grn_obj *to_table, + grn_obj *to_column) +{ + grn_table_cursor *cursor; + grn_id from_id; + grn_obj value; + + cursor = grn_table_cursor_open(ctx, from_table, + NULL, 0, + NULL, 0, + 0, -1, 0); + if (!cursor) { + return; + } + + GRN_VOID_INIT(&value); + while ((from_id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) { + void *key; + int key_size; + grn_id to_id; + + key_size = grn_table_cursor_get_key(ctx, cursor, &key); + to_id = grn_table_add(ctx, to_table, key, key_size, NULL); + if (to_id == GRN_ID_NIL) { + continue; + } + + GRN_BULK_REWIND(&value); + grn_obj_get_value(ctx, from_column, from_id, &value); + grn_obj_set_value(ctx, to_column, to_id, &value, GRN_OBJ_SET); + } + GRN_OBJ_FIN(ctx, &value); + grn_table_cursor_close(ctx, cursor); +} + +static void +command_column_copy_different(grn_ctx *ctx, + grn_obj *from_table, + grn_obj *from_column, + grn_obj *to_table, + grn_obj *to_column, + grn_obj *from_table_name, + grn_obj *from_column_name, + grn_obj *to_table_name, + grn_obj *to_column_name) +{ + grn_table_cursor *cursor; + grn_id from_id; + grn_obj from_key_buffer; + grn_obj to_key_buffer; + grn_obj value; + + cursor = grn_table_cursor_open(ctx, from_table, + NULL, 0, + NULL, 0, + 0, -1, 0); + if (!cursor) { + return; + } + + if (from_table->header.domain == GRN_DB_SHORT_TEXT) { + GRN_SHORT_TEXT_INIT(&from_key_buffer, 0); + } else { + GRN_VALUE_FIX_SIZE_INIT(&from_key_buffer, 0, from_table->header.domain); + } + if (to_table->header.domain == GRN_DB_SHORT_TEXT) { + GRN_SHORT_TEXT_INIT(&to_key_buffer, 0); + } else { + GRN_VALUE_FIX_SIZE_INIT(&to_key_buffer, 0, to_table->header.domain); + } + GRN_VOID_INIT(&value); + while ((from_id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) { + void *key; + int key_size; + grn_rc cast_rc; + grn_id to_id; + + GRN_BULK_REWIND(&from_key_buffer); + GRN_BULK_REWIND(&to_key_buffer); + + key_size = grn_table_cursor_get_key(ctx, cursor, &key); + grn_bulk_write(ctx, &from_key_buffer, key, key_size); + cast_rc = grn_obj_cast(ctx, &from_key_buffer, &to_key_buffer, GRN_FALSE); + if (cast_rc != GRN_SUCCESS) { + grn_obj *to_key_type; + grn_obj inspected_key; + grn_obj inspected_to_key_type; + + to_key_type = grn_ctx_at(ctx, to_table->header.domain); + GRN_TEXT_INIT(&inspected_key, 0); + GRN_TEXT_INIT(&inspected_to_key_type, 0); + grn_inspect(ctx, &inspected_key, &from_key_buffer); + grn_inspect(ctx, &inspected_to_key_type, to_key_type); + ERR(cast_rc, + "[column][copy] failed to cast key: <%.*s> -> %.*s: " + "<%.*s.%.*s> -> <%.*s.%.*s>", + (int)GRN_TEXT_LEN(&inspected_key), + GRN_TEXT_VALUE(&inspected_key), + (int)GRN_TEXT_LEN(&inspected_to_key_type), + GRN_TEXT_VALUE(&inspected_to_key_type), + (int)GRN_TEXT_LEN(from_table_name), + GRN_TEXT_VALUE(from_table_name), + (int)GRN_TEXT_LEN(from_column_name), + GRN_TEXT_VALUE(from_column_name), + (int)GRN_TEXT_LEN(to_table_name), + GRN_TEXT_VALUE(to_table_name), + (int)GRN_TEXT_LEN(to_column_name), + GRN_TEXT_VALUE(to_column_name)); + GRN_OBJ_FIN(ctx, &inspected_key); + GRN_OBJ_FIN(ctx, &inspected_to_key_type); + break; + } + to_id = grn_table_add(ctx, to_table, + GRN_BULK_HEAD(&to_key_buffer), + GRN_BULK_VSIZE(&to_key_buffer), + NULL); + if (to_id == GRN_ID_NIL) { + continue; + } + + GRN_BULK_REWIND(&value); + grn_obj_get_value(ctx, from_column, from_id, &value); + grn_obj_set_value(ctx, to_column, to_id, &value, GRN_OBJ_SET); + } + GRN_OBJ_FIN(ctx, &from_key_buffer); + GRN_OBJ_FIN(ctx, &to_key_buffer); + GRN_OBJ_FIN(ctx, &value); + + grn_table_cursor_close(ctx, cursor); +} + +static grn_obj * +command_column_copy(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_rc rc = GRN_SUCCESS; + grn_obj *from_table = NULL; + grn_obj *from_column = NULL; + grn_obj *to_table = NULL; + grn_obj *to_column = NULL; + grn_obj *from_table_name; + grn_obj *from_column_name; + grn_obj *to_table_name; + grn_obj *to_column_name; + + from_table_name = grn_plugin_proc_get_var(ctx, user_data, "from_table", -1); + from_column_name = grn_plugin_proc_get_var(ctx, user_data, "from_name", -1); + to_table_name = grn_plugin_proc_get_var(ctx, user_data, "to_table", -1); + to_column_name = grn_plugin_proc_get_var(ctx, user_data, "to_name", -1); + + rc = command_column_copy_resolve_target(ctx, "from", + from_table_name, from_column_name, + &from_table, &from_column); + if (rc != GRN_SUCCESS) { + goto exit; + } + rc = command_column_copy_resolve_target(ctx, "to", + to_table_name, to_column_name, + &to_table, &to_column); + if (rc != GRN_SUCCESS) { + goto exit; + } + + if ((from_table->header.type == GRN_TABLE_NO_KEY || + to_table->header.type == GRN_TABLE_NO_KEY) && + from_table != to_table) { + rc = GRN_OPERATION_NOT_SUPPORTED; + GRN_PLUGIN_ERROR(ctx, + rc, + "[column][copy] copy from/to TABLE_NO_KEY isn't supported: " + "<%.*s%c%.*s> -> <%.*s%c%.*s>", + (int)GRN_TEXT_LEN(from_table_name), + GRN_TEXT_VALUE(from_table_name), + GRN_DB_DELIMITER, + (int)GRN_TEXT_LEN(from_column_name), + GRN_TEXT_VALUE(from_column_name), + (int)GRN_TEXT_LEN(to_table_name), + GRN_TEXT_VALUE(to_table_name), + GRN_DB_DELIMITER, + (int)GRN_TEXT_LEN(to_column_name), + GRN_TEXT_VALUE(to_column_name)); + goto exit; + } + + if (from_table == to_table) { + command_column_copy_same_table(ctx, from_table, from_column, to_column); + } else if (from_table->header.domain == to_table->header.domain) { + command_column_copy_same_key_type(ctx, + from_table, from_column, + to_table, to_column); + } else { + command_column_copy_different(ctx, + from_table, + from_column, + to_table, + to_column, + from_table_name, + from_column_name, + to_table_name, + to_column_name); + } + +exit : + grn_ctx_output_bool(ctx, rc == GRN_SUCCESS); + + if (to_column) { + grn_obj_unlink(ctx, to_column); + } + if (to_table) { + grn_obj_unlink(ctx, to_table); + } + if (from_column) { + grn_obj_unlink(ctx, from_column); + } + if (from_table) { + grn_obj_unlink(ctx, from_table); + } + + return NULL; +} + +void +grn_proc_init_column_copy(grn_ctx *ctx) +{ + grn_expr_var vars[4]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "from_table", -1); + grn_plugin_expr_var_init(ctx, &(vars[1]), "from_name", -1); + grn_plugin_expr_var_init(ctx, &(vars[2]), "to_table", -1); + grn_plugin_expr_var_init(ctx, &(vars[3]), "to_name", -1); + grn_plugin_command_create(ctx, + "column_copy", -1, + command_column_copy, + 4, + vars); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_config.c b/storage/mroonga/vendor/groonga/lib/proc/proc_config.c new file mode 100644 index 00000000..61a1c5a8 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_config.c @@ -0,0 +1,139 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2016 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_proc.h" + +#include <groonga/plugin.h> + +static grn_obj * +command_config_get(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *key; + const char *value; + uint32_t value_size; + + key = grn_plugin_proc_get_var(ctx, user_data, "key", -1); + if (GRN_TEXT_LEN(key) == 0) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[config][get] key is missing"); + return NULL; + } + + grn_config_get(ctx, + GRN_TEXT_VALUE(key), GRN_TEXT_LEN(key), + &value, &value_size); + if (ctx->rc) { + return NULL; + } + + grn_ctx_output_str(ctx, value, value_size); + + return NULL; +} + +static grn_obj * +command_config_set(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *key; + grn_obj *value; + + key = grn_plugin_proc_get_var(ctx, user_data, "key", -1); + if (GRN_TEXT_LEN(key) == 0) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[config][set] key is missing"); + return NULL; + } + + value = grn_plugin_proc_get_var(ctx, user_data, "value", -1); + grn_config_set(ctx, + GRN_TEXT_VALUE(key), GRN_TEXT_LEN(key), + GRN_TEXT_VALUE(value), GRN_TEXT_LEN(value)); + + grn_ctx_output_bool(ctx, ctx->rc == GRN_SUCCESS); + + return NULL; +} + +static grn_obj * +command_config_delete(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *key; + + key = grn_plugin_proc_get_var(ctx, user_data, "key", -1); + if (GRN_TEXT_LEN(key) == 0) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[config][delete] key is missing"); + return NULL; + } + + grn_config_delete(ctx, + GRN_TEXT_VALUE(key), GRN_TEXT_LEN(key)); + + grn_ctx_output_bool(ctx, ctx->rc == GRN_SUCCESS); + + return NULL; +} + +void +grn_proc_init_config_get(grn_ctx *ctx) +{ + grn_expr_var vars[1]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "key", -1); + grn_plugin_command_create(ctx, + "config_get", -1, + command_config_get, + 1, + vars); +} + +void +grn_proc_init_config_set(grn_ctx *ctx) +{ + grn_expr_var vars[2]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "key", -1); + grn_plugin_expr_var_init(ctx, &(vars[1]), "value", -1); + grn_plugin_command_create(ctx, + "config_set", -1, + command_config_set, + 2, + vars); +} + +void +grn_proc_init_config_delete(grn_ctx *ctx) +{ + grn_expr_var vars[1]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "key", -1); + grn_plugin_command_create(ctx, + "config_delete", -1, + command_config_delete, + 1, + vars); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_dump.c b/storage/mroonga/vendor/groonga/lib/proc/proc_dump.c new file mode 100644 index 00000000..391925d8 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_dump.c @@ -0,0 +1,1138 @@ +/* -*- 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_proc.h" +#include "../grn_ctx_impl.h" +#include "../grn_db.h" +#include "../grn_str.h" + +#include <groonga/plugin.h> + +static const size_t DUMP_FLUSH_THRESHOLD_SIZE = 256 * 1024; + +typedef struct { + grn_obj *output; + grn_bool is_close_opened_object_mode; + grn_bool have_reference_column; + grn_bool have_index_column; + grn_bool is_sort_hash_table; + grn_obj column_name_buffer; +} grn_dumper; + +static void +dumper_collect_statistics_table(grn_ctx *ctx, + grn_dumper *dumper, + grn_obj *table) +{ + grn_hash *columns; + + columns = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, + GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY); + if (!columns) { + return; + } + + grn_table_columns(ctx, table, NULL, 0, (grn_obj *)columns); + GRN_HASH_EACH_BEGIN(ctx, columns, cursor, id) { + void *key; + grn_id column_id; + grn_obj *column; + + grn_hash_cursor_get_key(ctx, cursor, &key); + column_id = *((grn_id *)key); + + if (dumper->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + column = grn_ctx_at(ctx, column_id); + if (!column) { + GRN_PLUGIN_CLEAR_ERROR(ctx); + goto next_loop; + } + + if (grn_obj_is_index_column(ctx, column)) { + dumper->have_index_column = GRN_TRUE; + } else if (grn_obj_is_reference_column(ctx, column)) { + dumper->have_reference_column = GRN_TRUE; + } + + next_loop : + if (dumper->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + } GRN_HASH_EACH_END(ctx, cursor); + grn_hash_close(ctx, columns); +} + +static void +dumper_collect_statistics(grn_ctx *ctx, grn_dumper *dumper) +{ + GRN_DB_EACH_BEGIN_BY_ID(ctx, cursor, id) { + void *name; + int name_size; + grn_obj *object; + + if (grn_id_is_builtin(ctx, id)) { + continue; + } + + name_size = grn_table_cursor_get_key(ctx, cursor, &name); + if (grn_obj_name_is_column(ctx, name, name_size)) { + continue; + } + + if (dumper->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + object = grn_ctx_at(ctx, id); + if (!object) { + /* XXX: this clause is executed when MeCab tokenizer is enabled in + database but the groonga isn't supported MeCab. + We should return error mesage about it and error exit status + but it's too difficult for this architecture. :< */ + GRN_PLUGIN_CLEAR_ERROR(ctx); + goto next_loop; + } + + if (!grn_obj_is_table(ctx, object)) { + goto next_loop; + } + + dumper_collect_statistics_table(ctx, dumper, object); + +next_loop : + if (dumper->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + } GRN_DB_EACH_END(ctx, cursor); +} + +static void +dump_value_raw(grn_ctx *ctx, grn_obj *output, const char *value, int value_len) +{ + grn_obj escaped_value; + GRN_TEXT_INIT(&escaped_value, 0); + grn_text_esc(ctx, &escaped_value, value, value_len); + /* is no character escaped? */ + /* TODO false positive with spaces inside values */ + if (GRN_TEXT_LEN(&escaped_value) == value_len + 2) { + GRN_TEXT_PUT(ctx, output, value, value_len); + } else { + GRN_TEXT_PUT(ctx, output, + GRN_TEXT_VALUE(&escaped_value), GRN_TEXT_LEN(&escaped_value)); + } + grn_obj_close(ctx, &escaped_value); +} + +static void +dump_value(grn_ctx *ctx, grn_dumper *dumper, const char *value, int value_len) +{ + dump_value_raw(ctx, dumper->output, value, value_len); +} + +static void +dump_configs(grn_ctx *ctx, grn_dumper *dumper) +{ + grn_obj *config_cursor; + + config_cursor = grn_config_cursor_open(ctx); + if (!config_cursor) + return; + + while (grn_config_cursor_next(ctx, config_cursor)) { + const char *key; + uint32_t key_size; + const char *value; + uint32_t value_size; + + key_size = grn_config_cursor_get_key(ctx, config_cursor, &key); + value_size = grn_config_cursor_get_value(ctx, config_cursor, &value); + + GRN_TEXT_PUTS(ctx, dumper->output, "config_set "); + dump_value(ctx, dumper, key, key_size); + GRN_TEXT_PUTS(ctx, dumper->output, " "); + dump_value(ctx, dumper, value, value_size); + GRN_TEXT_PUTC(ctx, dumper->output, '\n'); + } + grn_obj_close(ctx, config_cursor); +} + +static void +dump_plugins(grn_ctx *ctx, grn_dumper *dumper) +{ + grn_obj plugin_names; + unsigned int i, n; + + GRN_TEXT_INIT(&plugin_names, GRN_OBJ_VECTOR); + + grn_plugin_get_names(ctx, &plugin_names); + + n = grn_vector_size(ctx, &plugin_names); + if (n == 0) { + GRN_OBJ_FIN(ctx, &plugin_names); + return; + } + + if (GRN_TEXT_LEN(dumper->output) > 0) { + GRN_TEXT_PUTC(ctx, dumper->output, '\n'); + grn_ctx_output_flush(ctx, 0); + } + for (i = 0; i < n; i++) { + const char *name; + unsigned int name_size; + + name_size = grn_vector_get_element(ctx, &plugin_names, i, &name, NULL, NULL); + grn_text_printf(ctx, dumper->output, "plugin_register %.*s\n", + (int)name_size, name); + } + + GRN_OBJ_FIN(ctx, &plugin_names); +} + +static void +dump_obj_name_raw(grn_ctx *ctx, grn_obj *output, grn_obj *obj) +{ + char name[GRN_TABLE_MAX_KEY_SIZE]; + int name_len; + name_len = grn_obj_name(ctx, obj, name, GRN_TABLE_MAX_KEY_SIZE); + dump_value_raw(ctx, output, name, name_len); +} + +static void +dump_obj_name(grn_ctx *ctx, grn_dumper *dumper, grn_obj *obj) +{ + dump_obj_name_raw(ctx, dumper->output, obj); +} + +static void +dump_column_name(grn_ctx *ctx, grn_dumper *dumper, grn_obj *column) +{ + char name[GRN_TABLE_MAX_KEY_SIZE]; + int name_len; + name_len = grn_column_name(ctx, column, name, GRN_TABLE_MAX_KEY_SIZE); + dump_value(ctx, dumper, name, name_len); +} + +static void +dump_index_column_sources(grn_ctx *ctx, grn_dumper *dumper, grn_obj *column) +{ + grn_obj sources; + grn_id *source_ids; + int i, n; + + GRN_OBJ_INIT(&sources, GRN_BULK, 0, GRN_ID_NIL); + grn_obj_get_info(ctx, column, GRN_INFO_SOURCE, &sources); + + n = GRN_BULK_VSIZE(&sources) / sizeof(grn_id); + source_ids = (grn_id *)GRN_BULK_HEAD(&sources); + if (n > 0) { + GRN_TEXT_PUTC(ctx, dumper->output, ' '); + } + for (i = 0; i < n; i++) { + grn_id source_id; + grn_obj *source; + + source_id = *source_ids; + source_ids++; + + if (dumper->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + source = grn_ctx_at(ctx, source_id); + if (!source) { + goto next_loop; + } + + if (i) { GRN_TEXT_PUTC(ctx, dumper->output, ','); } + switch (source->header.type) { + case GRN_TABLE_PAT_KEY: + case GRN_TABLE_DAT_KEY: + case GRN_TABLE_HASH_KEY: + GRN_TEXT_PUT(ctx, + dumper->output, + GRN_COLUMN_NAME_KEY, + GRN_COLUMN_NAME_KEY_LEN); + break; + default: + dump_column_name(ctx, dumper, source); + break; + } + + next_loop : + if (dumper->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + } + grn_obj_close(ctx, &sources); +} + +static void +dump_column(grn_ctx *ctx, grn_dumper *dumper, grn_obj *table, grn_obj *column) +{ + grn_id type_id; + grn_obj *type; + grn_column_flags flags; + grn_column_flags default_flags = GRN_OBJ_PERSISTENT; + + type_id = grn_obj_get_range(ctx, column); + if (dumper->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + type = grn_ctx_at(ctx, type_id); + if (!type) { + /* ERR(GRN_RANGE_ERROR, "couldn't get column's type object"); */ + goto exit; + } + + GRN_TEXT_PUTS(ctx, dumper->output, "column_create "); + dump_obj_name(ctx, dumper, table); + GRN_TEXT_PUTC(ctx, dumper->output, ' '); + dump_column_name(ctx, dumper, column); + GRN_TEXT_PUTC(ctx, dumper->output, ' '); + if (type->header.type == GRN_TYPE) { + default_flags |= type->header.flags; + } + flags = grn_column_get_flags(ctx, column); + grn_dump_column_create_flags(ctx, + flags & ~default_flags, + dumper->output); + GRN_TEXT_PUTC(ctx, dumper->output, ' '); + dump_obj_name(ctx, dumper, type); + if (column->header.flags & GRN_OBJ_COLUMN_INDEX) { + dump_index_column_sources(ctx, dumper, column); + } + GRN_TEXT_PUTC(ctx, dumper->output, '\n'); + +exit : + if (dumper->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } +} + +static void +dump_columns(grn_ctx *ctx, grn_dumper *dumper, grn_obj *table, + grn_bool dump_data_column, + grn_bool dump_reference_column, + grn_bool dump_index_column) +{ + grn_hash *columns; + columns = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, + GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY); + if (!columns) { + GRN_PLUGIN_ERROR(ctx, + GRN_NO_MEMORY_AVAILABLE, + "couldn't create a hash to hold columns"); + return; + } + + if (grn_table_columns(ctx, table, NULL, 0, (grn_obj *)columns) >= 0) { + GRN_HASH_EACH_BEGIN(ctx, columns, cursor, id) { + void *key; + grn_id column_id; + grn_obj *column; + + grn_hash_cursor_get_key(ctx, cursor, &key); + column_id = *((grn_id *)key); + + if (dumper->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + column = grn_ctx_at(ctx, column_id); + if (!column) { + GRN_PLUGIN_CLEAR_ERROR(ctx); + goto next_loop; + } + + if (grn_obj_is_index_column(ctx, column)) { + if (dump_index_column) { + dump_column(ctx, dumper, table, column); + GRN_PLUGIN_CLEAR_ERROR(ctx); + } + } else if (grn_obj_is_reference_column(ctx, column)) { + if (dump_reference_column) { + dump_column(ctx, dumper, table, column); + GRN_PLUGIN_CLEAR_ERROR(ctx); + } + } else { + if (dump_data_column) { + dump_column(ctx, dumper, table, column); + GRN_PLUGIN_CLEAR_ERROR(ctx); + } + } + + next_loop : + if (dumper->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + } GRN_HASH_EACH_END(ctx, cursor); + } + grn_hash_close(ctx, columns); +} + +static void +dump_record_column_vector(grn_ctx *ctx, grn_dumper *dumper, grn_id id, + grn_obj *column, grn_id range_id, grn_obj *buf) +{ + grn_obj *range; + grn_obj_format *format_argument = NULL; + grn_obj_format format; + + range = grn_ctx_at(ctx, range_id); + if (column->header.flags & GRN_OBJ_WITH_WEIGHT) { + format.flags = GRN_OBJ_FORMAT_WITH_WEIGHT; + format_argument = &format; + } + + if (grn_obj_is_table(ctx, range) || + (range->header.flags & GRN_OBJ_KEY_VAR_SIZE) == 0) { + GRN_OBJ_INIT(buf, GRN_UVECTOR, 0, range_id); + grn_obj_get_value(ctx, column, id, buf); + grn_text_otoj(ctx, dumper->output, buf, format_argument); + } else { + GRN_OBJ_INIT(buf, GRN_VECTOR, 0, range_id); + grn_obj_get_value(ctx, column, id, buf); + grn_text_otoj(ctx, dumper->output, buf, format_argument); + } + + grn_obj_unlink(ctx, range); + grn_obj_unlink(ctx, buf); +} + +static void +dump_record(grn_ctx *ctx, grn_dumper *dumper, + grn_obj *table, + grn_id id, + grn_obj *columns, int n_columns) +{ + int j; + grn_obj buf; + grn_obj *column_name = &(dumper->column_name_buffer); + + GRN_TEXT_PUTC(ctx, dumper->output, '['); + for (j = 0; j < n_columns; j++) { + grn_bool is_value_column; + grn_id range; + grn_obj *column; + column = GRN_PTR_VALUE_AT(columns, j); + /* TODO: use grn_obj_is_value_accessor() */ + GRN_BULK_REWIND(column_name); + grn_column_name_(ctx, column, column_name); + if (GRN_TEXT_LEN(column_name) == GRN_COLUMN_NAME_VALUE_LEN && + !memcmp(GRN_TEXT_VALUE(column_name), + GRN_COLUMN_NAME_VALUE, + GRN_COLUMN_NAME_VALUE_LEN)) { + is_value_column = GRN_TRUE; + } else { + is_value_column = GRN_FALSE; + } + range = grn_obj_get_range(ctx, column); + + if (j) { GRN_TEXT_PUTC(ctx, dumper->output, ','); } + switch (column->header.type) { + case GRN_COLUMN_VAR_SIZE: + case GRN_COLUMN_FIX_SIZE: + switch (column->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) { + case GRN_OBJ_COLUMN_VECTOR: + dump_record_column_vector(ctx, dumper, id, column, range, &buf); + break; + case GRN_OBJ_COLUMN_SCALAR: + { + GRN_OBJ_INIT(&buf, GRN_BULK, 0, range); + grn_obj_get_value(ctx, column, id, &buf); + grn_text_otoj(ctx, dumper->output, &buf, NULL); + grn_obj_unlink(ctx, &buf); + } + break; + default: + GRN_PLUGIN_ERROR(ctx, + GRN_OPERATION_NOT_SUPPORTED, + "unsupported column type: %#x", + column->header.type); + break; + } + break; + case GRN_COLUMN_INDEX: + break; + case GRN_ACCESSOR: + { + GRN_OBJ_INIT(&buf, GRN_BULK, 0, range); + grn_obj_get_value(ctx, column, id, &buf); + /* XXX maybe, grn_obj_get_range() should not unconditionally return + GRN_DB_INT32 when column is GRN_ACCESSOR and + GRN_ACCESSOR_GET_VALUE */ + if (is_value_column) { + buf.header.domain = grn_obj_get_range(ctx, table); + } + grn_text_otoj(ctx, dumper->output, &buf, NULL); + grn_obj_unlink(ctx, &buf); + } + break; + default: + GRN_PLUGIN_ERROR(ctx, + GRN_OPERATION_NOT_SUPPORTED, + "unsupported header type %#x", + column->header.type); + break; + } + } + GRN_TEXT_PUTC(ctx, dumper->output, ']'); + if ((size_t) GRN_TEXT_LEN(dumper->output) >= DUMP_FLUSH_THRESHOLD_SIZE) { + grn_ctx_output_flush(ctx, 0); + } +} + +static void +dump_records(grn_ctx *ctx, grn_dumper *dumper, grn_obj *table) +{ + grn_table_cursor *cursor; + int i, n_columns; + grn_obj columns; + grn_bool have_index_column = GRN_FALSE; + grn_bool have_data_column = GRN_FALSE; + + if (grn_table_size(ctx, table) == 0) { + return; + } + + if (dumper->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + GRN_PTR_INIT(&columns, GRN_OBJ_VECTOR, GRN_ID_NIL); + + if (table->header.type == GRN_TABLE_NO_KEY) { + grn_obj *id_accessor; + id_accessor = grn_obj_column(ctx, + table, + GRN_COLUMN_NAME_ID, + GRN_COLUMN_NAME_ID_LEN); + GRN_PTR_PUT(ctx, &columns, id_accessor); + } else if (table->header.domain != GRN_ID_NIL) { + grn_obj *key_accessor; + key_accessor = grn_obj_column(ctx, + table, + GRN_COLUMN_NAME_KEY, + GRN_COLUMN_NAME_KEY_LEN); + GRN_PTR_PUT(ctx, &columns, key_accessor); + } + + if (grn_obj_get_range(ctx, table) != GRN_ID_NIL) { + grn_obj *value_accessor; + value_accessor = grn_obj_column(ctx, + table, + GRN_COLUMN_NAME_VALUE, + GRN_COLUMN_NAME_VALUE_LEN); + GRN_PTR_PUT(ctx, &columns, value_accessor); + } + + { + grn_hash *real_columns; + + real_columns = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, + GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY); + grn_table_columns(ctx, table, NULL, 0, (grn_obj *)real_columns); + GRN_HASH_EACH_BEGIN(ctx, real_columns, cursor, id) { + void *key; + grn_id column_id; + grn_obj *column; + + if (dumper->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + grn_hash_cursor_get_key(ctx, cursor, &key); + column_id = *((grn_id *)key); + + column = grn_ctx_at(ctx, column_id); + if (column) { + if (grn_obj_is_index_column(ctx, column)) { + have_index_column = GRN_TRUE; + if (dumper->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + } else { + have_data_column = GRN_TRUE; + GRN_PTR_PUT(ctx, &columns, column); + if (dumper->is_close_opened_object_mode) { + grn_ctx_merge_temporary_open_space(ctx); + } + } + } else { + GRN_PLUGIN_CLEAR_ERROR(ctx); + if (dumper->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + } + } GRN_HASH_EACH_END(ctx, cursor); + grn_hash_close(ctx, real_columns); + } + + n_columns = GRN_BULK_VSIZE(&columns) / sizeof(grn_obj *); + + if (have_index_column && !have_data_column) { + goto exit; + } + + if (GRN_TEXT_LEN(dumper->output) > 0) { + GRN_TEXT_PUTC(ctx, dumper->output, '\n'); + } + + GRN_TEXT_PUTS(ctx, dumper->output, "load --table "); + dump_obj_name(ctx, dumper, table); + GRN_TEXT_PUTS(ctx, dumper->output, "\n[\n"); + + GRN_TEXT_PUTC(ctx, dumper->output, '['); + for (i = 0; i < n_columns; i++) { + grn_obj *column; + grn_obj *column_name = &(dumper->column_name_buffer); + + column = GRN_PTR_VALUE_AT(&columns, i); + if (i) { GRN_TEXT_PUTC(ctx, dumper->output, ','); } + GRN_BULK_REWIND(column_name); + grn_column_name_(ctx, column, column_name); + grn_text_otoj(ctx, dumper->output, column_name, NULL); + } + GRN_TEXT_PUTS(ctx, dumper->output, "],\n"); + + if (table->header.type == GRN_TABLE_HASH_KEY && dumper->is_sort_hash_table) { + grn_obj *sorted; + grn_table_sort_key sort_keys[1]; + uint32_t n_sort_keys = 1; + grn_bool is_first_record = GRN_TRUE; + + sort_keys[0].key = grn_obj_column(ctx, table, + GRN_COLUMN_NAME_KEY, + GRN_COLUMN_NAME_KEY_LEN); + sort_keys[0].flags = GRN_TABLE_SORT_ASC; + sort_keys[0].offset = 0; + sorted = grn_table_create(ctx, + NULL, 0, NULL, + GRN_TABLE_NO_KEY, + NULL, + table); + grn_table_sort(ctx, + table, 0, -1, + sorted, + sort_keys, n_sort_keys); + cursor = grn_table_cursor_open(ctx, + sorted, + NULL, 0, NULL, 0, + 0, -1, + 0); + while (grn_table_cursor_next(ctx, cursor) != GRN_ID_NIL) { + void *value_raw; + grn_id id; + + grn_table_cursor_get_value(ctx, cursor, &value_raw); + id = *((grn_id *)value_raw); + + if (is_first_record) { + is_first_record = GRN_FALSE; + } else { + GRN_TEXT_PUTS(ctx, dumper->output, ",\n"); + } + dump_record(ctx, dumper, table, id, &columns, n_columns); + } + GRN_TEXT_PUTS(ctx, dumper->output, "\n]\n"); + grn_obj_close(ctx, sorted); + grn_obj_unlink(ctx, sort_keys[0].key); + } else { + grn_obj delete_commands; + grn_id old_id = GRN_ID_NIL; + grn_id id; + + GRN_TEXT_INIT(&delete_commands, 0); + cursor = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1, + GRN_CURSOR_BY_KEY); + while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) { + if (old_id != GRN_ID_NIL) { GRN_TEXT_PUTS(ctx, dumper->output, ",\n"); } + if (table->header.type == GRN_TABLE_NO_KEY && old_id + 1 < id) { + grn_id current_id; + for (current_id = old_id + 1; current_id < id; current_id++) { + GRN_TEXT_PUTS(ctx, dumper->output, "[],\n"); + GRN_TEXT_PUTS(ctx, &delete_commands, "delete --table "); + dump_obj_name_raw(ctx, &delete_commands, table); + GRN_TEXT_PUTS(ctx, &delete_commands, " --id "); + grn_text_lltoa(ctx, &delete_commands, current_id); + GRN_TEXT_PUTC(ctx, &delete_commands, '\n'); + } + } + dump_record(ctx, dumper, table, id, &columns, n_columns); + + old_id = id; + } + grn_table_cursor_close(ctx, cursor); + GRN_TEXT_PUTS(ctx, dumper->output, "\n]\n"); + GRN_TEXT_PUT(ctx, dumper->output, + GRN_TEXT_VALUE(&delete_commands), + GRN_TEXT_LEN(&delete_commands)); + GRN_OBJ_FIN(ctx, &delete_commands); + } +exit : + for (i = 0; i < n_columns; i++) { + grn_obj *column; + + column = GRN_PTR_VALUE_AT(&columns, i); + if (column->header.type == GRN_ACCESSOR) { + grn_obj_close(ctx, column); + } + } + GRN_OBJ_FIN(ctx, &columns); + + if (dumper->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } +} + +static void +dump_table(grn_ctx *ctx, grn_dumper *dumper, grn_obj *table) +{ + grn_obj *domain = NULL; + grn_id range_id; + grn_obj *range = NULL; + grn_table_flags flags; + grn_table_flags default_flags = GRN_OBJ_PERSISTENT; + grn_obj *default_tokenizer; + grn_obj *normalizer; + grn_obj *token_filters; + + switch (table->header.type) { + case GRN_TABLE_HASH_KEY: + case GRN_TABLE_PAT_KEY: + case GRN_TABLE_DAT_KEY: + domain = grn_ctx_at(ctx, table->header.domain); + break; + default: + break; + } + + if (GRN_TEXT_LEN(dumper->output) > 0) { + GRN_TEXT_PUTC(ctx, dumper->output, '\n'); + grn_ctx_output_flush(ctx, 0); + } + + grn_table_get_info(ctx, table, + &flags, + NULL, + &default_tokenizer, + &normalizer, + &token_filters); + + GRN_TEXT_PUTS(ctx, dumper->output, "table_create "); + dump_obj_name(ctx, dumper, table); + GRN_TEXT_PUTC(ctx, dumper->output, ' '); + grn_dump_table_create_flags(ctx, + flags & ~default_flags, + dumper->output); + if (domain) { + GRN_TEXT_PUTC(ctx, dumper->output, ' '); + dump_obj_name(ctx, dumper, domain); + } + range_id = grn_obj_get_range(ctx, table); + if (range_id != GRN_ID_NIL) { + range = grn_ctx_at(ctx, range_id); + if (!range) { + // ERR(GRN_RANGE_ERROR, "couldn't get table's value_type object"); + return; + } + if (table->header.type != GRN_TABLE_NO_KEY) { + GRN_TEXT_PUTC(ctx, dumper->output, ' '); + } else { + GRN_TEXT_PUTS(ctx, dumper->output, " --value_type "); + } + dump_obj_name(ctx, dumper, range); + grn_obj_unlink(ctx, range); + } + if (default_tokenizer) { + GRN_TEXT_PUTS(ctx, dumper->output, " --default_tokenizer "); + dump_obj_name(ctx, dumper, default_tokenizer); + } + if (normalizer) { + GRN_TEXT_PUTS(ctx, dumper->output, " --normalizer "); + dump_obj_name(ctx, dumper, normalizer); + } + if (table->header.type != GRN_TABLE_NO_KEY) { + int n_token_filters; + + n_token_filters = GRN_BULK_VSIZE(token_filters) / sizeof(grn_obj *); + if (n_token_filters > 0) { + int i; + GRN_TEXT_PUTS(ctx, dumper->output, " --token_filters "); + for (i = 0; i < n_token_filters; i++) { + grn_obj *token_filter = GRN_PTR_VALUE_AT(token_filters, i); + if (i > 0) { + GRN_TEXT_PUTC(ctx, dumper->output, ','); + } + dump_obj_name(ctx, dumper, token_filter); + } + } + } + + GRN_TEXT_PUTC(ctx, dumper->output, '\n'); + + dump_columns(ctx, dumper, table, GRN_TRUE, GRN_FALSE, GRN_FALSE); +} + +static void +dump_schema(grn_ctx *ctx, grn_dumper *dumper) +{ + GRN_DB_EACH_BEGIN_BY_KEY(ctx, cursor, id) { + void *name; + int name_size; + grn_obj *object; + + if (grn_id_is_builtin(ctx, id)) { + continue; + } + + name_size = grn_table_cursor_get_key(ctx, cursor, &name); + if (grn_obj_name_is_column(ctx, name, name_size)) { + continue; + } + + if (dumper->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + if ((object = grn_ctx_at(ctx, id))) { + switch (object->header.type) { + case GRN_TABLE_HASH_KEY: + case GRN_TABLE_PAT_KEY: + case GRN_TABLE_DAT_KEY: + case GRN_TABLE_NO_KEY: + dump_table(ctx, dumper, object); + break; + default: + break; + } + } else { + /* XXX: this clause is executed when MeCab tokenizer is enabled in + database but the groonga isn't supported MeCab. + We should return error mesage about it and error exit status + but it's too difficult for this architecture. :< */ + GRN_PLUGIN_CLEAR_ERROR(ctx); + } + + if (dumper->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + } GRN_DB_EACH_END(ctx, cursor); + + if (!dumper->have_reference_column) { + return; + } + + GRN_TEXT_PUTC(ctx, dumper->output, '\n'); + grn_ctx_output_flush(ctx, 0); + + GRN_DB_EACH_BEGIN_BY_KEY(ctx, cursor, id) { + void *name; + int name_size; + grn_obj *object; + + if (grn_id_is_builtin(ctx, id)) { + continue; + } + + name_size = grn_table_cursor_get_key(ctx, cursor, &name); + if (grn_obj_name_is_column(ctx, name, name_size)) { + continue; + } + + if (dumper->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + if ((object = grn_ctx_at(ctx, id))) { + switch (object->header.type) { + case GRN_TABLE_HASH_KEY: + case GRN_TABLE_PAT_KEY: + case GRN_TABLE_DAT_KEY: + case GRN_TABLE_NO_KEY: + dump_columns(ctx, dumper, object, GRN_FALSE, GRN_TRUE, GRN_FALSE); + break; + default: + break; + } + } else { + /* XXX: this clause is executed when MeCab tokenizer is enabled in + database but the groonga isn't supported MeCab. + We should return error mesage about it and error exit status + but it's too difficult for this architecture. :< */ + GRN_PLUGIN_CLEAR_ERROR(ctx); + } + + if (dumper->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + } GRN_DB_EACH_END(ctx, cursor); +} + +static void +dump_selected_tables_records(grn_ctx *ctx, grn_dumper *dumper, grn_obj *tables) +{ + const char *p, *e; + + p = GRN_TEXT_VALUE(tables); + e = p + GRN_TEXT_LEN(tables); + while (p < e) { + int len; + grn_obj *table; + const char *token, *token_e; + + if ((len = grn_isspace(p, ctx->encoding))) { + p += len; + continue; + } + + token = p; + if (!(('a' <= *p && *p <= 'z') || + ('A' <= *p && *p <= 'Z') || + (*p == '_'))) { + while (p < e && !grn_isspace(p, ctx->encoding)) { + p++; + } + GRN_LOG(ctx, GRN_LOG_WARNING, "invalid table name is ignored: <%.*s>\n", + (int)(p - token), token); + continue; + } + while (p < e && + (('a' <= *p && *p <= 'z') || + ('A' <= *p && *p <= 'Z') || + ('0' <= *p && *p <= '9') || + (*p == '_'))) { + p++; + } + token_e = p; + while (p < e && (len = grn_isspace(p, ctx->encoding))) { + p += len; + continue; + } + if (p < e && *p == ',') { + p++; + } + + table = grn_ctx_get(ctx, token, token_e - token); + if (!table) { + GRN_LOG(ctx, GRN_LOG_WARNING, + "nonexistent table name is ignored: <%.*s>\n", + (int)(token_e - token), token); + continue; + } + + if (grn_obj_is_table(ctx, table)) { + dump_records(ctx, dumper, table); + } + grn_obj_unlink(ctx, table); + } +} + +static void +dump_all_records(grn_ctx *ctx, grn_dumper *dumper) +{ + GRN_DB_EACH_BEGIN_BY_KEY(ctx, cursor, id) { + void *name; + int name_size; + grn_obj *table; + + if (grn_id_is_builtin(ctx, id)) { + continue; + } + + name_size = grn_table_cursor_get_key(ctx, cursor, &name); + if (grn_obj_name_is_column(ctx, name, name_size)) { + continue; + } + + if (dumper->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + table = grn_ctx_at(ctx, id); + if (!table) { + /* XXX: this clause is executed when MeCab tokenizer is enabled in + database but the groonga isn't supported MeCab. + We should return error mesage about it and error exit status + but it's too difficult for this architecture. :< */ + GRN_PLUGIN_CLEAR_ERROR(ctx); + goto next_loop; + } + + if (grn_obj_is_table(ctx, table)) { + dump_records(ctx, dumper, table); + } + + next_loop : + if (dumper->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + } GRN_DB_EACH_END(ctx, cursor); +} + +static void +dump_indexes(grn_ctx *ctx, grn_dumper *dumper) +{ + if (!dumper->have_index_column) { + return; + } + + if (GRN_TEXT_LEN(dumper->output) > 0) { + GRN_TEXT_PUTC(ctx, dumper->output, '\n'); + } + + GRN_DB_EACH_BEGIN_BY_KEY(ctx, cursor, id) { + void *name; + int name_size; + grn_obj *object; + + if (grn_id_is_builtin(ctx, id)) { + continue; + } + + name_size = grn_table_cursor_get_key(ctx, cursor, &name); + if (grn_obj_name_is_column(ctx, name, name_size)) { + continue; + } + + if (dumper->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + object = grn_ctx_at(ctx, id); + if (!object) { + /* XXX: this clause is executed when MeCab tokenizer is enabled in + database but the groonga isn't supported MeCab. + We should return error mesage about it and error exit status + but it's too difficult for this architecture. :< */ + GRN_PLUGIN_CLEAR_ERROR(ctx); + goto next_loop; + } + + if (grn_obj_is_table(ctx, object)) { + dump_columns(ctx, dumper, object, GRN_FALSE, GRN_FALSE, GRN_TRUE); + } + + next_loop : + if (dumper->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + } GRN_DB_EACH_END(ctx, cursor); +} + +static grn_obj * +command_dump(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_dumper dumper; + grn_obj *tables; + grn_bool is_dump_plugins; + grn_bool is_dump_schema; + grn_bool is_dump_records; + grn_bool is_dump_indexes; + grn_bool is_dump_configs; + + dumper.output = ctx->impl->output.buf; + if (grn_thread_get_limit() == 1) { + dumper.is_close_opened_object_mode = GRN_TRUE; + } else { + dumper.is_close_opened_object_mode = GRN_FALSE; + } + dumper.have_reference_column = GRN_FALSE; + dumper.have_index_column = GRN_FALSE; + + tables = grn_plugin_proc_get_var(ctx, user_data, "tables", -1); + is_dump_plugins = grn_plugin_proc_get_var_bool(ctx, user_data, + "dump_plugins", -1, + GRN_TRUE); + is_dump_schema = grn_plugin_proc_get_var_bool(ctx, user_data, + "dump_schema", -1, + GRN_TRUE); + is_dump_records = grn_plugin_proc_get_var_bool(ctx, user_data, + "dump_records", -1, + GRN_TRUE); + is_dump_indexes = grn_plugin_proc_get_var_bool(ctx, user_data, + "dump_indexes", -1, + GRN_TRUE); + is_dump_configs = grn_plugin_proc_get_var_bool(ctx, user_data, + "dump_configs", -1, + GRN_TRUE); + dumper.is_sort_hash_table = + grn_plugin_proc_get_var_bool(ctx, user_data, + "sort_hash_table", -1, + GRN_FALSE); + GRN_TEXT_INIT(&(dumper.column_name_buffer), 0); + + grn_ctx_set_output_type(ctx, GRN_CONTENT_GROONGA_COMMAND_LIST); + + dumper_collect_statistics(ctx, &dumper); + + if (is_dump_configs) { + dump_configs(ctx, &dumper); + } + if (is_dump_plugins) { + dump_plugins(ctx, &dumper); + } + if (is_dump_schema) { + dump_schema(ctx, &dumper); + } + if (is_dump_records) { + /* To update index columns correctly, we first create the whole schema, then + load non-derivative records, while skipping records of index columns. That + way, Groonga will silently do the job of updating index columns for us. */ + if (GRN_TEXT_LEN(tables) > 0) { + dump_selected_tables_records(ctx, &dumper, tables); + } else { + dump_all_records(ctx, &dumper); + } + } + if (is_dump_indexes) { + dump_indexes(ctx, &dumper); + } + /* remove the last newline because another one will be added by the caller. + maybe, the caller of proc functions currently doesn't consider the + possibility of multiple-line output from proc functions. */ + if (GRN_BULK_VSIZE(dumper.output) > 0) { + grn_bulk_truncate(ctx, dumper.output, GRN_BULK_VSIZE(dumper.output) - 1); + } + + GRN_OBJ_FIN(ctx, &(dumper.column_name_buffer)); + + return NULL; +} + +void +grn_proc_init_dump(grn_ctx *ctx) +{ + grn_expr_var vars[7]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "tables", -1); + grn_plugin_expr_var_init(ctx, &(vars[1]), "dump_plugins", -1); + grn_plugin_expr_var_init(ctx, &(vars[2]), "dump_schema", -1); + grn_plugin_expr_var_init(ctx, &(vars[3]), "dump_records", -1); + grn_plugin_expr_var_init(ctx, &(vars[4]), "dump_indexes", -1); + grn_plugin_expr_var_init(ctx, &(vars[5]), "dump_configs", -1); + grn_plugin_expr_var_init(ctx, &(vars[6]), "sort_hash_table", -1); + grn_plugin_command_create(ctx, + "dump", -1, + command_dump, + sizeof(vars) / sizeof(vars[0]), + vars); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_fuzzy_search.c b/storage/mroonga/vendor/groonga/lib/proc/proc_fuzzy_search.c new file mode 100644 index 00000000..952fdbb1 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_fuzzy_search.c @@ -0,0 +1,467 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2009-2016 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_proc.h" +#include "../grn_rset.h" +#include "../grn_ii.h" + +#include <groonga/plugin.h> + +#include <string.h> + +#define DIST(ox,oy) (dists[((lx + 1) * (oy)) + (ox)]) + +static uint32_t +calc_edit_distance(grn_ctx *ctx, char *sx, char *ex, char *sy, char *ey, int flags) +{ + int d = 0; + uint32_t cx, lx, cy, ly, *dists; + char *px, *py; + for (px = sx, lx = 0; px < ex && (cx = grn_charlen(ctx, px, ex)); px += cx, lx++); + for (py = sy, ly = 0; py < ey && (cy = grn_charlen(ctx, py, ey)); py += cy, ly++); + if ((dists = GRN_PLUGIN_MALLOC(ctx, (lx + 1) * (ly + 1) * sizeof(uint32_t)))) { + uint32_t x, y; + for (x = 0; x <= lx; x++) { DIST(x, 0) = x; } + for (y = 0; y <= ly; y++) { DIST(0, y) = y; } + for (x = 1, px = sx; x <= lx; x++, px += cx) { + cx = grn_charlen(ctx, px, ex); + for (y = 1, py = sy; y <= ly; y++, py += cy) { + cy = grn_charlen(ctx, py, ey); + if (cx == cy && !memcmp(px, py, cx)) { + DIST(x, y) = DIST(x - 1, y - 1); + } else { + uint32_t a = DIST(x - 1, y) + 1; + uint32_t b = DIST(x, y - 1) + 1; + uint32_t c = DIST(x - 1, y - 1) + 1; + DIST(x, y) = ((a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c)); + if (flags & GRN_TABLE_FUZZY_SEARCH_WITH_TRANSPOSITION && + x > 1 && y > 1 && cx == cy && + memcmp(px, py - cy, cx) == 0 && + memcmp(px - cx, py, cx) == 0) { + uint32_t t = DIST(x - 2, y - 2) + 1; + DIST(x, y) = ((DIST(x, y) < t) ? DIST(x, y) : t); + } + } + } + } + d = DIST(lx, ly); + GRN_PLUGIN_FREE(ctx, dists); + } + return d; +} + +static grn_obj * +func_edit_distance(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ +#define N_REQUIRED_ARGS 2 +#define MAX_ARGS 3 + int d = 0; + int flags = 0; + grn_obj *obj; + if (nargs >= N_REQUIRED_ARGS && nargs <= MAX_ARGS) { + if (nargs == MAX_ARGS && GRN_BOOL_VALUE(args[2])) { + flags |= GRN_TABLE_FUZZY_SEARCH_WITH_TRANSPOSITION; + } + d = calc_edit_distance(ctx, GRN_TEXT_VALUE(args[0]), GRN_BULK_CURR(args[0]), + GRN_TEXT_VALUE(args[1]), GRN_BULK_CURR(args[1]), flags); + } + if ((obj = grn_plugin_proc_alloc(ctx, user_data, GRN_DB_UINT32, 0))) { + GRN_UINT32_SET(ctx, obj, d); + } + return obj; +#undef N_REQUIRED_ARGS +#undef MAX_ARGS +} + +void +grn_proc_init_edit_distance(grn_ctx *ctx) +{ + grn_proc_create(ctx, "edit_distance", -1, GRN_PROC_FUNCTION, + func_edit_distance, NULL, NULL, 0, NULL); +} + +#define SCORE_HEAP_SIZE 256 + +typedef struct { + grn_id id; + uint32_t score; +} score_heap_node; + +typedef struct { + int n_entries; + int limit; + score_heap_node *nodes; +} score_heap; + +static inline score_heap * +score_heap_open(grn_ctx *ctx, int max) +{ + score_heap *h = GRN_PLUGIN_MALLOC(ctx, sizeof(score_heap)); + if (!h) { return NULL; } + h->nodes = GRN_PLUGIN_MALLOC(ctx, sizeof(score_heap_node) * max); + if (!h->nodes) { + GRN_PLUGIN_FREE(ctx, h); + return NULL; + } + h->n_entries = 0; + h->limit = max; + return h; +} + +static inline grn_bool +score_heap_push(grn_ctx *ctx, score_heap *h, grn_id id, uint32_t score) +{ + int n, n2; + score_heap_node node = {id, score}; + score_heap_node node2; + if (h->n_entries >= h->limit) { + int max = h->limit * 2; + score_heap_node *nodes; + nodes = GRN_PLUGIN_REALLOC(ctx, h->nodes, sizeof(score_heap) * max); + if (!nodes) { + return GRN_FALSE; + } + h->limit = max; + h->nodes = nodes; + } + h->nodes[h->n_entries] = node; + n = h->n_entries++; + while (n) { + n2 = (n - 1) >> 1; + if (h->nodes[n2].score <= h->nodes[n].score) { break; } + node2 = h->nodes[n]; + h->nodes[n] = h->nodes[n2]; + h->nodes[n2] = node2; + n = n2; + } + return GRN_TRUE; +} + +static inline void +score_heap_close(grn_ctx *ctx, score_heap *h) +{ + GRN_PLUGIN_FREE(ctx, h->nodes); + GRN_PLUGIN_FREE(ctx, h); +} + +static grn_rc +sequential_fuzzy_search(grn_ctx *ctx, grn_obj *table, grn_obj *column, grn_obj *query, + uint32_t max_distance, uint32_t prefix_match_size, + uint32_t max_expansion, int flags, grn_obj *res, grn_operator op) +{ + grn_table_cursor *tc; + char *sx = GRN_TEXT_VALUE(query); + char *ex = GRN_BULK_CURR(query); + + if (op == GRN_OP_AND) { + tc = grn_table_cursor_open(ctx, res, NULL, 0, NULL, 0, 0, -1, GRN_CURSOR_BY_ID); + } else { + tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1, GRN_CURSOR_BY_ID); + } + if (tc) { + grn_id id; + grn_obj value; + score_heap *heap; + int i, n; + GRN_TEXT_INIT(&value, 0); + + heap = score_heap_open(ctx, SCORE_HEAP_SIZE); + if (!heap) { + grn_table_cursor_close(ctx, tc); + grn_obj_unlink(ctx, &value); + return GRN_NO_MEMORY_AVAILABLE; + } + + while ((id = grn_table_cursor_next(ctx, tc))) { + unsigned int distance = 0; + grn_obj *domain; + grn_id record_id; + + if (op == GRN_OP_AND) { + grn_id *key; + grn_table_cursor_get_key(ctx, tc, (void **)&key); + record_id = *key; + } else { + record_id = id; + } + GRN_BULK_REWIND(&value); + grn_obj_get_value(ctx, column, record_id, &value); + domain = grn_ctx_at(ctx, ((&value))->header.domain); + if ((&(value))->header.type == GRN_VECTOR) { + n = grn_vector_size(ctx, &value); + for (i = 0; i < n; i++) { + unsigned int length; + const char *vector_value = NULL; + length = grn_vector_get_element(ctx, &value, i, &vector_value, NULL, NULL); + + if (!prefix_match_size || + (prefix_match_size > 0 && length >= prefix_match_size && + !memcmp(sx, vector_value, prefix_match_size))) { + distance = calc_edit_distance(ctx, sx, ex, + (char *)vector_value, + (char *)vector_value + length, flags); + if (distance <= max_distance) { + score_heap_push(ctx, heap, record_id, distance); + break; + } + } + } + } else if ((&(value))->header.type == GRN_UVECTOR && + grn_obj_is_table(ctx, domain)) { + n = grn_vector_size(ctx, &value); + for (i = 0; i < n; i++) { + grn_id rid; + char key_name[GRN_TABLE_MAX_KEY_SIZE]; + int key_length; + rid = grn_uvector_get_element(ctx, &value, i, NULL); + key_length = grn_table_get_key(ctx, domain, rid, key_name, GRN_TABLE_MAX_KEY_SIZE); + + if (!prefix_match_size || + (prefix_match_size > 0 && key_length >= (int) prefix_match_size && + !memcmp(sx, key_name, prefix_match_size))) { + distance = calc_edit_distance(ctx, sx, ex, + key_name, key_name + key_length, flags); + if (distance <= max_distance) { + score_heap_push(ctx, heap, record_id, distance); + break; + } + } + } + } else { + if (grn_obj_is_reference_column(ctx, column)) { + grn_id rid; + char key_name[GRN_TABLE_MAX_KEY_SIZE]; + int key_length; + rid = GRN_RECORD_VALUE(&value); + key_length = grn_table_get_key(ctx, domain, rid, key_name, GRN_TABLE_MAX_KEY_SIZE); + if (!prefix_match_size || + (prefix_match_size > 0 && key_length >= (int) prefix_match_size && + !memcmp(sx, key_name, prefix_match_size))) { + distance = calc_edit_distance(ctx, sx, ex, + key_name, key_name + key_length, flags); + if (distance <= max_distance) { + score_heap_push(ctx, heap, record_id, distance); + } + } + } else { + if (!prefix_match_size || + (prefix_match_size > 0 && GRN_TEXT_LEN(&value) >= prefix_match_size && + !memcmp(sx, GRN_TEXT_VALUE(&value), prefix_match_size))) { + distance = calc_edit_distance(ctx, sx, ex, + GRN_TEXT_VALUE(&value), + GRN_BULK_CURR(&value), flags); + if (distance <= max_distance) { + score_heap_push(ctx, heap, record_id, distance); + } + } + } + } + grn_obj_unlink(ctx, domain); + } + grn_table_cursor_close(ctx, tc); + grn_obj_unlink(ctx, &value); + + for (i = 0; i < heap->n_entries; i++) { + if (max_expansion > 0 && (uint32_t) i >= max_expansion) { + break; + } + { + grn_posting posting; + posting.rid = heap->nodes[i].id; + posting.sid = 1; + posting.pos = 0; + posting.weight = max_distance - heap->nodes[i].score; + grn_ii_posting_add(ctx, &posting, (grn_hash *)res, op); + } + } + grn_ii_resolve_sel_and(ctx, (grn_hash *)res, op); + score_heap_close(ctx, heap); + } + + return GRN_SUCCESS; +} + +static grn_rc +selector_fuzzy_search(grn_ctx *ctx, grn_obj *table, grn_obj *index, + int nargs, grn_obj **args, + grn_obj *res, grn_operator op) +{ + grn_rc rc = GRN_SUCCESS; + grn_obj *target = NULL; + grn_obj *obj; + grn_obj *query; + uint32_t max_distance = 1; + uint32_t prefix_length = 0; + uint32_t prefix_match_size = 0; + uint32_t max_expansion = 0; + int flags = 0; + grn_bool use_sequential_search = GRN_FALSE; + + if ((nargs - 1) < 2) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "fuzzy_search(): wrong number of arguments (%d ...)", + nargs - 1); + rc = ctx->rc; + goto exit; + } + obj = args[1]; + query = args[2]; + + if (nargs == 4) { + grn_obj *options = args[3]; + + switch (options->header.type) { + case GRN_BULK : + max_distance = GRN_UINT32_VALUE(options); + break; + case GRN_TABLE_HASH_KEY : + { + grn_hash_cursor *cursor; + void *key; + grn_obj *value; + int key_size; + cursor = grn_hash_cursor_open(ctx, (grn_hash *)options, + NULL, 0, NULL, 0, + 0, -1, 0); + if (!cursor) { + GRN_PLUGIN_ERROR(ctx, GRN_NO_MEMORY_AVAILABLE, + "fuzzy_search(): couldn't open cursor"); + goto exit; + } + while (grn_hash_cursor_next(ctx, cursor) != GRN_ID_NIL) { + grn_hash_cursor_get_key_value(ctx, cursor, &key, &key_size, + (void **)&value); + + if (key_size == 12 && !memcmp(key, "max_distance", 12)) { + max_distance = GRN_UINT32_VALUE(value); + } else if (key_size == 13 && !memcmp(key, "prefix_length", 13)) { + prefix_length = GRN_UINT32_VALUE(value); + } else if (key_size == 13 && !memcmp(key, "max_expansion", 13)) { + max_expansion = GRN_UINT32_VALUE(value); + } else if (key_size == 18 && !memcmp(key, "with_transposition", 18)) { + if (GRN_BOOL_VALUE(value)) { + flags |= GRN_TABLE_FUZZY_SEARCH_WITH_TRANSPOSITION; + } + } else { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "invalid option name: <%.*s>", + key_size, (char *)key); + grn_hash_cursor_close(ctx, cursor); + goto exit; + } + } + grn_hash_cursor_close(ctx, cursor); + } + break; + default : + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "fuzzy_search(): " + "3rd argument must be integer or object literal: <%.*s>", + (int)GRN_TEXT_LEN(options), + GRN_TEXT_VALUE(options)); + goto exit; + } + } + + if (index) { + target = index; + } else { + if (obj->header.type == GRN_COLUMN_INDEX) { + target = obj; + } else { + grn_column_index(ctx, obj, GRN_OP_FUZZY, &target, 1, NULL); + } + } + + if (target) { + grn_obj *lexicon; + use_sequential_search = GRN_TRUE; + lexicon = grn_ctx_at(ctx, target->header.domain); + if (lexicon) { + if (lexicon->header.type == GRN_TABLE_PAT_KEY) { + use_sequential_search = GRN_FALSE; + } + grn_obj_unlink(ctx, lexicon); + } + } else { + if (grn_obj_is_key_accessor(ctx, obj) && + table->header.type == GRN_TABLE_PAT_KEY) { + target = table; + } else { + use_sequential_search = GRN_TRUE; + } + } + + if (prefix_length) { + const char *s = GRN_TEXT_VALUE(query); + const char *e = GRN_BULK_CURR(query); + const char *p; + unsigned int cl = 0; + unsigned int length = 0; + for (p = s; p < e && (cl = grn_charlen(ctx, p, e)); p += cl) { + length++; + if (length > prefix_length) { + break; + } + } + prefix_match_size = p - s; + } + + if (use_sequential_search) { + rc = sequential_fuzzy_search(ctx, table, obj, query, + max_distance, prefix_match_size, + max_expansion, flags, res, op); + goto exit; + } + + if (!target) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, target); + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "fuzzy_search(): " + "column must be COLUMN_INDEX or TABLE_PAT_KEY: <%.*s>", + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + rc = ctx->rc; + GRN_OBJ_FIN(ctx, &inspected); + } else { + grn_search_optarg options = {0}; + options.mode = GRN_OP_FUZZY; + options.fuzzy.prefix_match_size = prefix_match_size; + options.fuzzy.max_distance = max_distance; + options.fuzzy.max_expansion = max_expansion; + options.fuzzy.flags = flags; + grn_obj_search(ctx, target, query, res, op, &options); + } + +exit : + return rc; +} + +void +grn_proc_init_fuzzy_search(grn_ctx *ctx) +{ + grn_obj *selector_proc; + + selector_proc = grn_proc_create(ctx, "fuzzy_search", -1, + GRN_PROC_FUNCTION, + NULL, NULL, NULL, 0, NULL); + grn_proc_set_selector(ctx, selector_proc, selector_fuzzy_search); + grn_proc_set_selector_operator(ctx, selector_proc, GRN_OP_FUZZY); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_highlight.c b/storage/mroonga/vendor/groonga/lib/proc/proc_highlight.c new file mode 100644 index 00000000..80551a10 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_highlight.c @@ -0,0 +1,503 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2009-2016 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_proc.h" +#include "../grn_expr.h" + +#include <groonga/plugin.h> +#include <string.h> + +#define GRN_FUNC_HIGHLIGHT_HTML_CACHE_NAME "$highlight_html" + +static void +grn_pat_tag_keys_put_original_text(grn_ctx *ctx, grn_obj *output, + const char *text, unsigned int length, + grn_bool use_html_escape) +{ + if (use_html_escape) { + grn_text_escape_xml(ctx, output, text, length); + } else { + GRN_TEXT_PUT(ctx, output, text, length); + } +} + +static grn_rc +grn_pat_tag_keys(grn_ctx *ctx, grn_obj *keywords, + const char *string, unsigned int string_length, + const char **open_tags, unsigned int *open_tag_lengths, + const char **close_tags, unsigned int *close_tag_lengths, + unsigned int n_tags, + grn_obj *highlighted, + grn_bool use_html_escape) +{ + while (string_length > 0) { +#define MAX_N_HITS 16 + grn_pat_scan_hit hits[MAX_N_HITS]; + const char *rest; + unsigned int i, n_hits; + unsigned int previous = 0; + size_t chunk_length; + + n_hits = grn_pat_scan(ctx, (grn_pat *)keywords, + string, string_length, + hits, MAX_N_HITS, &rest); + for (i = 0; i < n_hits; i++) { + unsigned int nth_tag; + if (hits[i].offset - previous > 0) { + grn_pat_tag_keys_put_original_text(ctx, + highlighted, + string + previous, + hits[i].offset - previous, + use_html_escape); + } + nth_tag = ((hits[i].id - 1) % n_tags); + GRN_TEXT_PUT(ctx, highlighted, + open_tags[nth_tag], open_tag_lengths[nth_tag]); + grn_pat_tag_keys_put_original_text(ctx, + highlighted, + string + hits[i].offset, + hits[i].length, + use_html_escape); + GRN_TEXT_PUT(ctx, highlighted, + close_tags[nth_tag], close_tag_lengths[nth_tag]); + previous = hits[i].offset + hits[i].length; + } + + chunk_length = rest - string; + if (chunk_length - previous > 0) { + grn_pat_tag_keys_put_original_text(ctx, + highlighted, + string + previous, + string_length - previous, + use_html_escape); + } + string_length -= chunk_length; + string = rest; +#undef MAX_N_HITS + } + + return GRN_SUCCESS; +} + +static grn_obj * +func_highlight_create_keywords_table(grn_ctx *ctx, + grn_user_data *user_data, + const char *normalizer_name, + unsigned int normalizer_name_length) +{ + grn_obj *keywords; + + keywords = grn_table_create(ctx, NULL, 0, NULL, + GRN_OBJ_TABLE_PAT_KEY, + grn_ctx_at(ctx, GRN_DB_SHORT_TEXT), + NULL); + + if (normalizer_name_length > 0) { + grn_obj *normalizer; + normalizer = grn_ctx_get(ctx, + normalizer_name, + normalizer_name_length); + if (!grn_obj_is_normalizer_proc(ctx, normalizer)) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, normalizer); + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "highlight_full() not normalizer: <%.*s>", + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + grn_obj_unlink(ctx, normalizer); + grn_obj_unlink(ctx, keywords); + return NULL; + } + grn_obj_set_info(ctx, keywords, GRN_INFO_NORMALIZER, normalizer); + grn_obj_unlink(ctx, normalizer); + } + + return keywords; +} + +static grn_obj * +highlight_keyword_sets(grn_ctx *ctx, grn_user_data *user_data, + grn_obj **keyword_set_args, unsigned int n_keyword_args, + grn_obj *string, grn_obj *keywords, + grn_bool use_html_escape) +{ + grn_obj *highlighted = NULL; +#define KEYWORD_SET_SIZE 3 + { + unsigned int i; + unsigned int n_keyword_sets; + grn_obj open_tags; + grn_obj open_tag_lengths; + grn_obj close_tags; + grn_obj close_tag_lengths; + + n_keyword_sets = n_keyword_args / KEYWORD_SET_SIZE; + + GRN_OBJ_INIT(&open_tags, GRN_BULK, 0, GRN_DB_VOID); + GRN_OBJ_INIT(&open_tag_lengths, GRN_BULK, 0, GRN_DB_VOID); + GRN_OBJ_INIT(&close_tags, GRN_BULK, 0, GRN_DB_VOID); + GRN_OBJ_INIT(&close_tag_lengths, GRN_BULK, 0, GRN_DB_VOID); + + for (i = 0; i < n_keyword_sets; i++) { + grn_obj *keyword = keyword_set_args[i * KEYWORD_SET_SIZE + 0]; + grn_obj *open_tag = keyword_set_args[i * KEYWORD_SET_SIZE + 1]; + grn_obj *close_tag = keyword_set_args[i * KEYWORD_SET_SIZE + 2]; + + grn_table_add(ctx, keywords, + GRN_TEXT_VALUE(keyword), + GRN_TEXT_LEN(keyword), + NULL); + { + const char *open_tag_content = GRN_TEXT_VALUE(open_tag); + grn_bulk_write(ctx, &open_tags, + (const char *)(&open_tag_content), + sizeof(char *)); + } + { + unsigned int open_tag_length = GRN_TEXT_LEN(open_tag); + grn_bulk_write(ctx, &open_tag_lengths, + (const char *)(&open_tag_length), + sizeof(unsigned int)); + } + { + const char *close_tag_content = GRN_TEXT_VALUE(close_tag); + grn_bulk_write(ctx, &close_tags, + (const char *)(&close_tag_content), + sizeof(char *)); + } + { + unsigned int close_tag_length = GRN_TEXT_LEN(close_tag); + grn_bulk_write(ctx, &close_tag_lengths, + (const char *)(&close_tag_length), + sizeof(unsigned int)); + } + } + + highlighted = grn_plugin_proc_alloc(ctx, user_data, GRN_DB_TEXT, 0); + grn_pat_tag_keys(ctx, keywords, + GRN_TEXT_VALUE(string), GRN_TEXT_LEN(string), + (const char **)GRN_BULK_HEAD(&open_tags), + (unsigned int *)GRN_BULK_HEAD(&open_tag_lengths), + (const char **)GRN_BULK_HEAD(&close_tags), + (unsigned int *)GRN_BULK_HEAD(&close_tag_lengths), + n_keyword_sets, + highlighted, + use_html_escape); + grn_obj_unlink(ctx, &open_tags); + grn_obj_unlink(ctx, &open_tag_lengths); + grn_obj_unlink(ctx, &close_tags); + grn_obj_unlink(ctx, &close_tag_lengths); + } +#undef KEYWORD_SET_SIZE + return highlighted; +} + +static grn_obj * +highlight_keywords(grn_ctx *ctx, grn_user_data *user_data, + grn_obj *string, grn_obj *keywords, grn_bool use_html_escape, + const char *default_open_tag, unsigned int default_open_tag_length, + const char *default_close_tag, unsigned int default_close_tag_length) +{ + grn_obj *highlighted = NULL; + const char *open_tags[1]; + unsigned int open_tag_lengths[1]; + const char *close_tags[1]; + unsigned int close_tag_lengths[1]; + unsigned int n_keyword_sets = 1; + + open_tags[0] = default_open_tag; + open_tag_lengths[0] = default_open_tag_length; + close_tags[0] = default_close_tag; + close_tag_lengths[0] = default_close_tag_length; + + highlighted = grn_plugin_proc_alloc(ctx, user_data, GRN_DB_TEXT, 0); + grn_pat_tag_keys(ctx, keywords, + GRN_TEXT_VALUE(string), GRN_TEXT_LEN(string), + open_tags, + open_tag_lengths, + close_tags, + close_tag_lengths, + n_keyword_sets, + highlighted, + use_html_escape); + + return highlighted; +} + +static grn_obj * +func_highlight(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *highlighted = NULL; + +#define N_REQUIRED_ARGS 1 + if (nargs > N_REQUIRED_ARGS) { + grn_obj *string = args[0]; + grn_bool use_html_escape = GRN_FALSE; + grn_obj *keywords; + const char *normalizer_name = "NormalizerAuto"; + unsigned int normalizer_name_length = 14; + const char *default_open_tag = NULL; + unsigned int default_open_tag_length = 0; + const char *default_close_tag = NULL; + unsigned int default_close_tag_length = 0; + grn_obj *end_arg = args[nargs - 1]; + int n_args_without_option = nargs; + + if (end_arg->header.type == GRN_TABLE_HASH_KEY) { + grn_obj *options = end_arg; + grn_hash_cursor *cursor; + void *key; + grn_obj *value; + int key_size; + + n_args_without_option--; + cursor = grn_hash_cursor_open(ctx, (grn_hash *)options, + NULL, 0, NULL, 0, + 0, -1, 0); + if (!cursor) { + GRN_PLUGIN_ERROR(ctx, GRN_NO_MEMORY_AVAILABLE, + "highlight(): couldn't open cursor"); + goto exit; + } + while (grn_hash_cursor_next(ctx, cursor) != GRN_ID_NIL) { + grn_hash_cursor_get_key_value(ctx, cursor, &key, &key_size, + (void **)&value); + if (key_size == 10 && !memcmp(key, "normalizer", 10)) { + normalizer_name = GRN_TEXT_VALUE(value); + normalizer_name_length = GRN_TEXT_LEN(value); + } else if (key_size == 11 && !memcmp(key, "html_escape", 11)) { + if (GRN_BOOL_VALUE(value)) { + use_html_escape = GRN_TRUE; + } + } else if (key_size == 16 && !memcmp(key, "default_open_tag", 16)) { + default_open_tag = GRN_TEXT_VALUE(value); + default_open_tag_length = GRN_TEXT_LEN(value); + } else if (key_size == 17 && !memcmp(key, "default_close_tag", 17)) { + default_close_tag = GRN_TEXT_VALUE(value); + default_close_tag_length = GRN_TEXT_LEN(value); + } else { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, "invalid option name: <%.*s>", + key_size, (char *)key); + grn_hash_cursor_close(ctx, cursor); + goto exit; + } + } + grn_hash_cursor_close(ctx, cursor); + } + + keywords = + func_highlight_create_keywords_table(ctx, user_data, + normalizer_name, + normalizer_name_length); + + if (keywords) { + grn_obj **keyword_args = args + N_REQUIRED_ARGS; + unsigned int n_keyword_args = n_args_without_option - N_REQUIRED_ARGS; + if (default_open_tag_length == 0 && default_close_tag_length == 0) { + highlighted = highlight_keyword_sets(ctx, user_data, + keyword_args, n_keyword_args, + string, keywords, use_html_escape); + } else { + unsigned int i; + for (i = 0; i < n_keyword_args; i++) { + grn_table_add(ctx, keywords, + GRN_TEXT_VALUE(keyword_args[i]), + GRN_TEXT_LEN(keyword_args[i]), + NULL); + } + highlighted = highlight_keywords(ctx, user_data, + string, keywords, use_html_escape, + default_open_tag, default_open_tag_length, + default_close_tag, default_close_tag_length); + } + } + } +#undef N_REQUIRED_ARGS + +exit : + if (!highlighted) { + highlighted = grn_plugin_proc_alloc(ctx, user_data, GRN_DB_VOID, 0); + } + + return highlighted; +} + +void +grn_proc_init_highlight(grn_ctx *ctx) +{ + grn_proc_create(ctx, "highlight", -1, GRN_PROC_FUNCTION, + func_highlight, NULL, NULL, 0, NULL); +} + +static grn_obj * +func_highlight_full(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *highlighted = NULL; + +#define N_REQUIRED_ARGS 3 +#define KEYWORD_SET_SIZE 3 + if ((nargs >= (N_REQUIRED_ARGS + KEYWORD_SET_SIZE) && + (nargs - N_REQUIRED_ARGS) % KEYWORD_SET_SIZE == 0)) { + grn_obj *string = args[0]; + grn_obj *keywords; + const char *normalizer_name = GRN_TEXT_VALUE(args[1]); + unsigned int normalizer_name_length = GRN_TEXT_LEN(args[1]); + grn_bool use_html_escape = GRN_BOOL_VALUE(args[2]); + + keywords = + func_highlight_create_keywords_table(ctx, user_data, + normalizer_name, + normalizer_name_length); + if (keywords) { + highlighted = highlight_keyword_sets(ctx, user_data, + args + N_REQUIRED_ARGS, + nargs - N_REQUIRED_ARGS, + string, keywords, + use_html_escape); + } + } + + if (!highlighted) { + highlighted = grn_plugin_proc_alloc(ctx, user_data, GRN_DB_VOID, 0); + } +#undef KEYWORD_SET_SIZE +#undef N_REQUIRED_ARGS + + return highlighted; +} + +void +grn_proc_init_highlight_full(grn_ctx *ctx) +{ + grn_proc_create(ctx, "highlight_full", -1, GRN_PROC_FUNCTION, + func_highlight_full, NULL, NULL, 0, NULL); +} + +static grn_obj * +func_highlight_html_create_keywords_table(grn_ctx *ctx, grn_obj *expression) +{ + grn_obj *keywords; + grn_obj *condition_ptr = NULL; + grn_obj *condition = NULL; + + keywords = grn_table_create(ctx, NULL, 0, NULL, + GRN_OBJ_TABLE_PAT_KEY, + grn_ctx_at(ctx, GRN_DB_SHORT_TEXT), + NULL); + + { + grn_obj *normalizer; + normalizer = grn_ctx_get(ctx, "NormalizerAuto", -1); + grn_obj_set_info(ctx, keywords, GRN_INFO_NORMALIZER, normalizer); + grn_obj_unlink(ctx, normalizer); + } + + condition_ptr = grn_expr_get_var(ctx, expression, + GRN_SELECT_INTERNAL_VAR_CONDITION, + strlen(GRN_SELECT_INTERNAL_VAR_CONDITION)); + if (condition_ptr) { + condition = GRN_PTR_VALUE(condition_ptr); + } + + if (condition) { + size_t i, n_keywords; + grn_obj current_keywords; + GRN_TEXT_INIT(¤t_keywords, GRN_OBJ_VECTOR); + grn_expr_get_keywords(ctx, condition, ¤t_keywords); + + n_keywords = grn_vector_size(ctx, ¤t_keywords); + for (i = 0; i < n_keywords; i++) { + const char *keyword; + unsigned int keyword_size; + keyword_size = grn_vector_get_element(ctx, + ¤t_keywords, + i, + &keyword, + NULL, + NULL); + grn_table_add(ctx, + keywords, + keyword, + keyword_size, + NULL); + } + GRN_OBJ_FIN(ctx, ¤t_keywords); + } + + return keywords; +} + +static grn_obj * +func_highlight_html(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *highlighted = NULL; + +#define N_REQUIRED_ARGS 1 + if (nargs == N_REQUIRED_ARGS) { + grn_obj *string = args[0]; + grn_obj *expression = NULL; + grn_obj *keywords; + grn_obj *keywords_ptr; + grn_bool use_html_escape = GRN_TRUE; + + grn_proc_get_info(ctx, user_data, NULL, NULL, &expression); + + keywords_ptr = grn_expr_get_var(ctx, expression, + GRN_FUNC_HIGHLIGHT_HTML_CACHE_NAME, + strlen(GRN_FUNC_HIGHLIGHT_HTML_CACHE_NAME)); + if (keywords_ptr) { + keywords = GRN_PTR_VALUE(keywords_ptr); + } else { + keywords_ptr = + grn_expr_get_or_add_var(ctx, expression, + GRN_FUNC_HIGHLIGHT_HTML_CACHE_NAME, + strlen(GRN_FUNC_HIGHLIGHT_HTML_CACHE_NAME)); + GRN_OBJ_FIN(ctx, keywords_ptr); + GRN_PTR_INIT(keywords_ptr, GRN_OBJ_OWN, GRN_DB_OBJECT); + + keywords = func_highlight_html_create_keywords_table(ctx, expression); + GRN_PTR_SET(ctx, keywords_ptr, keywords); + } + + highlighted = highlight_keywords(ctx, user_data, + string, keywords, use_html_escape, + "<span class=\"keyword\">", + strlen("<span class=\"keyword\">"), + "</span>", + strlen("</span>")); + } +#undef N_REQUIRED_ARGS + + if (!highlighted) { + highlighted = grn_plugin_proc_alloc(ctx, user_data, GRN_DB_VOID, 0); + } + + return highlighted; +} + +void +grn_proc_init_highlight_html(grn_ctx *ctx) +{ + grn_proc_create(ctx, "highlight_html", -1, GRN_PROC_FUNCTION, + func_highlight_html, NULL, NULL, 0, NULL); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_in_records.c b/storage/mroonga/vendor/groonga/lib/proc/proc_in_records.c new file mode 100644 index 00000000..e3b8a7e3 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_in_records.c @@ -0,0 +1,519 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 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_proc.h" +#include "../grn_db.h" +#include "../grn_store.h" + +#include <groonga/plugin.h> + +typedef struct { + int n_conditions; + grn_obj *condition_table; + grn_obj condition_columns; + grn_operator *condition_modes; + grn_obj *search_result; +} grn_in_records_data; + +static void +grn_in_records_data_free(grn_ctx *ctx, grn_in_records_data *data) +{ + int i; + int n_condition_columns; + + if (!data) { + return; + } + + GRN_PLUGIN_FREE(ctx, data->condition_modes); + + n_condition_columns = + GRN_BULK_VSIZE(&(data->condition_columns)) / sizeof(grn_obj *); + for (i = 0; i < n_condition_columns; i++) { + grn_obj *condition_column; + condition_column = GRN_PTR_VALUE_AT(&(data->condition_columns), i); + if (condition_column && condition_column->header.type == GRN_ACCESSOR) { + grn_obj_unlink(ctx, condition_column); + } + } + GRN_OBJ_FIN(ctx, &(data->condition_columns)); + + if (data->search_result) { + grn_obj_close(ctx, data->search_result); + } + + GRN_PLUGIN_FREE(ctx, data); +} + +static grn_obj * +func_in_records_init(grn_ctx *ctx, + int n_args, + grn_obj **args, + grn_user_data *user_data) +{ + grn_in_records_data *data; + grn_obj *condition_table; + grn_expr_code *codes; + int n_arg_codes; + int n_logical_args; + int n_conditions; + int i; + int nth; + + { + grn_obj *caller; + grn_expr *expr; + grn_expr_code *call_code; + + caller = grn_plugin_proc_get_caller(ctx, user_data); + expr = (grn_expr *)caller; + call_code = expr->codes + expr->codes_curr - 1; + n_logical_args = call_code->nargs - 1; + codes = expr->codes + 1; + n_arg_codes = expr->codes_curr - 2; + } + + if (n_logical_args < 4) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "in_records(): wrong number of arguments (%d for 4..)", + n_logical_args); + return NULL; + } + + if ((n_logical_args % 3) != 1) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "in_records(): the number of arguments must be 1 + 3n (%d)", + n_logical_args); + return NULL; + } + + n_conditions = (n_logical_args - 1) / 3; + + condition_table = codes[0].value; + if (!grn_obj_is_table(ctx, condition_table)) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, condition_table); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "in_records(): the first argument must be a table: <%.*s>", + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + return NULL; + } + + data = GRN_PLUGIN_CALLOC(ctx, sizeof(grn_in_records_data)); + if (!data) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "in_records(): failed to allocate internal data"); + return NULL; + } + user_data->ptr = data; + + data->n_conditions = n_conditions; + data->condition_table = condition_table; + GRN_PTR_INIT(&(data->condition_columns), GRN_OBJ_VECTOR, GRN_ID_NIL); + data->condition_modes = GRN_PLUGIN_MALLOCN(ctx, grn_operator, n_conditions); + if (!data->condition_modes) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "in_records(): " + "failed to allocate internal data for condition modes"); + goto exit; + } + + for (i = 1, nth = 0; i < n_arg_codes; nth++) { + int value_i = i; + int mode_name_i; + grn_obj *mode_name; + int column_name_i; + grn_obj *column_name; + grn_obj *condition_column; + + value_i += codes[value_i].modify; + + mode_name_i = value_i + 1; + mode_name = codes[mode_name_i].value; + data->condition_modes[nth] = grn_proc_option_value_mode(ctx, + mode_name, + GRN_OP_EQUAL, + "in_records()"); + if (ctx->rc != GRN_SUCCESS) { + goto exit; + } + + column_name_i = mode_name_i + 1; + column_name = codes[column_name_i].value; + if (!grn_obj_is_text_family_bulk(ctx, column_name)) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, condition_table); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "in_records(): " + "the %dth argument must be column name as string: " + "<%.*s>", + column_name_i, + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + goto exit; + } + + condition_column = grn_obj_column(ctx, condition_table, + GRN_TEXT_VALUE(column_name), + GRN_TEXT_LEN(column_name)); + if (!condition_column) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, condition_table); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "in_records(): " + "the %dth argument must be existing column name: " + "<%.*s>: <%.*s>", + column_name_i, + (int)GRN_TEXT_LEN(column_name), + GRN_TEXT_VALUE(column_name), + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + goto exit; + } + GRN_PTR_PUT(ctx, &(data->condition_columns), condition_column); + + i = column_name_i + 1; + } + + return NULL; + +exit : + grn_in_records_data_free(ctx, data); + + return NULL; +} + +static grn_obj * +func_in_records_next(grn_ctx *ctx, + int n_args, + grn_obj **args, + grn_user_data *user_data) +{ + grn_in_records_data *data = user_data->ptr; + grn_obj *found; + grn_obj *condition; + grn_obj *variable; + int i; + + found = grn_plugin_proc_alloc(ctx, user_data, GRN_DB_BOOL, 0); + if (!found) { + return NULL; + } + GRN_BOOL_SET(ctx, found, GRN_FALSE); + + if (!data) { + return found; + } + + GRN_EXPR_CREATE_FOR_QUERY(ctx, + data->condition_table, + condition, + variable); + if (!condition) { + grn_rc rc = ctx->rc; + if (rc == GRN_SUCCESS) { + rc = GRN_NO_MEMORY_AVAILABLE; + } + GRN_PLUGIN_ERROR(ctx, + rc, + "in_records(): " + "failed to create internal expression: %s", + ctx->errbuf); + return found; + } + + for (i = 1; i < n_args; i += 3) { + int nth = (i - 1) / 3; + grn_obj *value = args[i]; + grn_obj *condition_column; + grn_operator condition_mode; + + condition_column = GRN_PTR_VALUE_AT(&(data->condition_columns), nth); + condition_mode = data->condition_modes[nth]; + + switch (condition_mode) { + case GRN_OP_EQUAL : + case GRN_OP_NOT_EQUAL : + grn_expr_append_obj(ctx, condition, condition_column, GRN_OP_GET_VALUE, 1); + grn_expr_append_obj(ctx, condition, value, GRN_OP_PUSH, 1); + grn_expr_append_op(ctx, condition, condition_mode, 2); + break; + case GRN_OP_LESS : + grn_expr_append_obj(ctx, condition, condition_column, GRN_OP_GET_VALUE, 1); + grn_expr_append_obj(ctx, condition, value, GRN_OP_PUSH, 1); + grn_expr_append_op(ctx, condition, GRN_OP_GREATER_EQUAL, 2); + break; + case GRN_OP_GREATER : + grn_expr_append_obj(ctx, condition, condition_column, GRN_OP_GET_VALUE, 1); + grn_expr_append_obj(ctx, condition, value, GRN_OP_PUSH, 1); + grn_expr_append_op(ctx, condition, GRN_OP_LESS_EQUAL, 2); + break; + case GRN_OP_LESS_EQUAL : + grn_expr_append_obj(ctx, condition, condition_column, GRN_OP_GET_VALUE, 1); + grn_expr_append_obj(ctx, condition, value, GRN_OP_PUSH, 1); + grn_expr_append_op(ctx, condition, GRN_OP_GREATER, 2); + break; + case GRN_OP_GREATER_EQUAL : + grn_expr_append_obj(ctx, condition, condition_column, GRN_OP_GET_VALUE, 1); + grn_expr_append_obj(ctx, condition, value, GRN_OP_PUSH, 1); + grn_expr_append_op(ctx, condition, GRN_OP_LESS, 2); + break; + default : + grn_expr_append_obj(ctx, condition, value, GRN_OP_PUSH, 1); + grn_expr_append_obj(ctx, condition, condition_column, GRN_OP_GET_VALUE, 1); + grn_expr_append_op(ctx, condition, condition_mode, 2); + break; + } + + if (nth > 0) { + grn_expr_append_op(ctx, condition, GRN_OP_AND, 2); + } + } + + data->search_result = grn_table_select(ctx, + data->condition_table, + condition, + data->search_result, + GRN_OP_OR); + if (grn_table_size(ctx, data->search_result) > 0) { + GRN_BOOL_SET(ctx, found, GRN_TRUE); + + GRN_TABLE_EACH_BEGIN(ctx, data->search_result, cursor, id) { + grn_table_cursor_delete(ctx, cursor); + } GRN_TABLE_EACH_END(ctx, cursor); + } + + grn_obj_close(ctx, condition); + + return found; +} + +static grn_obj * +func_in_records_fin(grn_ctx *ctx, + int n_args, + grn_obj **args, + grn_user_data *user_data) +{ + grn_in_records_data *data = user_data->ptr; + + grn_in_records_data_free(ctx, data); + + return NULL; +} + +static grn_rc +selector_in_records(grn_ctx *ctx, + grn_obj *table, + grn_obj *index, + int n_args, + grn_obj **args, + grn_obj *res, + grn_operator op) +{ + grn_obj *condition_table; + grn_operator *condition_modes = NULL; + grn_obj condition_columns; + int i, nth; + + /* TODO: Enable me when function call is supported. */ + return GRN_FUNCTION_NOT_IMPLEMENTED; + + if (n_args < 5) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "in_records(): wrong number of arguments (%d for 4..)", + n_args - 1); + return ctx->rc; + } + + condition_table = args[1]; + if (!grn_obj_is_table(ctx, condition_table)) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, condition_table); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "in_records(): the first argument must be a table: <%.*s>", + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + return ctx->rc; + } + + condition_modes = GRN_PLUGIN_MALLOCN(ctx, grn_operator, (n_args - 2) / 3); + GRN_PTR_INIT(&condition_columns, GRN_OBJ_VECTOR, GRN_ID_NIL); + for (i = 2, nth = 0; i < n_args; i += 3, nth++) { + int mode_name_i = i + 1; + int column_name_i = i + 2; + grn_obj *mode_name; + grn_operator mode; + grn_obj *column_name; + grn_obj *condition_column; + + mode_name = args[mode_name_i]; + mode = grn_proc_option_value_mode(ctx, + mode_name, + GRN_OP_EQUAL, + "in_records()"); + if (ctx->rc != GRN_SUCCESS) { + goto exit; + } + + condition_modes[nth] = mode; + + column_name = args[column_name_i]; + if (!grn_obj_is_text_family_bulk(ctx, column_name)) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, condition_table); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "in_records(): " + "the %dth argument must be column name as string: " + "<%.*s>", + column_name_i, + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + goto exit; + } + + condition_column = grn_obj_column(ctx, condition_table, + GRN_TEXT_VALUE(column_name), + GRN_TEXT_LEN(column_name)); + if (!condition_column) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, condition_table); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "in_records(): " + "the %dth argument must be existing column name: " + "<%.*s>: <%.*s>", + column_name_i, + (int)GRN_TEXT_LEN(column_name), + GRN_TEXT_VALUE(column_name), + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + goto exit; + } + GRN_PTR_PUT(ctx, &condition_columns, condition_column); + } + + { + grn_obj condition_column_value; + + GRN_VOID_INIT(&condition_column_value); + GRN_TABLE_EACH_BEGIN(ctx, condition_table, cursor, id) { + grn_obj *sub_res = NULL; + + for (i = 2; i < n_args; i += 3) { + int nth = (i - 2) / 3; + grn_operator sub_op; + grn_obj *condition_column; + grn_operator condition_mode; + grn_obj *column = args[i]; + grn_obj *expr; + grn_obj *variable; + + if (nth == 0) { + sub_op = GRN_OP_OR; + } else { + sub_op = GRN_OP_AND; + } + + condition_column = GRN_PTR_VALUE_AT(&condition_columns, nth); + condition_mode = condition_modes[nth]; + + GRN_BULK_REWIND(&condition_column_value); + grn_obj_get_value(ctx, + condition_column, + id, + &condition_column_value); + + GRN_EXPR_CREATE_FOR_QUERY(ctx, table, expr, variable); + if (!expr) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "in_records(): failed to create expression"); + GRN_OBJ_FIN(ctx, &condition_column_value); + if (sub_res) { + grn_obj_close(ctx, sub_res); + } + goto exit; + } + grn_expr_append_obj(ctx, expr, column, GRN_OP_GET_VALUE, 1); + grn_expr_append_obj(ctx, expr, &condition_column_value, GRN_OP_PUSH, 1); + grn_expr_append_op(ctx, expr, condition_mode, 2); + sub_res = grn_table_select(ctx, table, expr, sub_res, sub_op); + grn_obj_close(ctx, expr); + } + + if (sub_res) { + grn_table_setoperation(ctx, res, sub_res, res, op); + grn_obj_close(ctx, sub_res); + } + } GRN_TABLE_EACH_END(ctx, cursor); + GRN_OBJ_FIN(ctx, &condition_column_value); + } + +exit : + GRN_PLUGIN_FREE(ctx, condition_modes); + + for (i = 2; i < n_args; i += 3) { + int nth = (i - 2) / 3; + grn_obj *condition_column; + condition_column = GRN_PTR_VALUE_AT(&condition_columns, nth); + if (condition_column && condition_column->header.type == GRN_ACCESSOR) { + grn_obj_unlink(ctx, condition_column); + } + } + GRN_OBJ_FIN(ctx, &condition_columns); + + return ctx->rc; +} + +void +grn_proc_init_in_records(grn_ctx *ctx) +{ + grn_obj *selector_proc; + + selector_proc = grn_proc_create(ctx, "in_records", -1, GRN_PROC_FUNCTION, + func_in_records_init, + func_in_records_next, + func_in_records_fin, + 0, + NULL); + grn_proc_set_selector(ctx, selector_proc, selector_in_records); + grn_proc_set_selector_operator(ctx, selector_proc, GRN_OP_NOP); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_lock.c b/storage/mroonga/vendor/groonga/lib/proc/proc_lock.c new file mode 100644 index 00000000..9eaf808a --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_lock.c @@ -0,0 +1,172 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2009-2016 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_proc.h" + +#include "../grn_ctx.h" + +#include <groonga/plugin.h> + +static grn_obj * +command_lock_clear(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + int target_name_len; + grn_obj *target_name; + grn_obj *obj; + + target_name = grn_plugin_proc_get_var(ctx, user_data, "target_name", -1); + target_name_len = GRN_TEXT_LEN(target_name); + + if (target_name_len) { + obj = grn_ctx_get(ctx, GRN_TEXT_VALUE(target_name), target_name_len); + } else { + obj = grn_ctx_db(ctx); + } + + if (obj) { + grn_obj_clear_lock(ctx, obj); + } else { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[lock][clear] target object not found: <%.*s>", + target_name_len, GRN_TEXT_VALUE(target_name)); + } + + grn_ctx_output_bool(ctx, ctx->rc == GRN_SUCCESS); + + return NULL; +} + +void +grn_proc_init_clearlock(grn_ctx *ctx) +{ + grn_expr_var vars[1]; + + /* Deprecated. Use "lock_clear" instead. */ + grn_plugin_expr_var_init(ctx, &(vars[0]), "target_name", -1); + grn_plugin_command_create(ctx, + "clearlock", -1, + command_lock_clear, + 1, + vars); +} + +void +grn_proc_init_lock_clear(grn_ctx *ctx) +{ + grn_expr_var vars[1]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "target_name", -1); + grn_plugin_command_create(ctx, + "lock_clear", -1, + command_lock_clear, + 1, + vars); +} + +static grn_obj * +command_lock_acquire(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + int target_name_len; + grn_obj *target_name; + grn_obj *obj; + + target_name = grn_plugin_proc_get_var(ctx, user_data, "target_name", -1); + target_name_len = GRN_TEXT_LEN(target_name); + + if (target_name_len) { + obj = grn_ctx_get(ctx, GRN_TEXT_VALUE(target_name), target_name_len); + } else { + obj = grn_ctx_db(ctx); + } + + if (obj) { + grn_obj_lock(ctx, obj, GRN_ID_NIL, grn_lock_timeout); + } else { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[lock][acquire] target object not found: <%.*s>", + target_name_len, GRN_TEXT_VALUE(target_name)); + } + + grn_ctx_output_bool(ctx, ctx->rc == GRN_SUCCESS); + + return NULL; +} + +void +grn_proc_init_lock_acquire(grn_ctx *ctx) +{ + grn_expr_var vars[1]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "target_name", -1); + grn_plugin_command_create(ctx, + "lock_acquire", -1, + command_lock_acquire, + 1, + vars); +} + +static grn_obj * +command_lock_release(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + int target_name_len; + grn_obj *target_name; + grn_obj *obj; + + target_name = grn_plugin_proc_get_var(ctx, user_data, "target_name", -1); + target_name_len = GRN_TEXT_LEN(target_name); + + if (target_name_len) { + obj = grn_ctx_get(ctx, GRN_TEXT_VALUE(target_name), target_name_len); + } else { + obj = grn_ctx_db(ctx); + } + + if (obj) { + grn_obj_unlock(ctx, obj, GRN_ID_NIL); + } else { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[lock][release] target object not found: <%.*s>", + target_name_len, GRN_TEXT_VALUE(target_name)); + } + + grn_ctx_output_bool(ctx, ctx->rc == GRN_SUCCESS); + + return NULL; +} + +void +grn_proc_init_lock_release(grn_ctx *ctx) +{ + grn_expr_var vars[1]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "target_name", -1); + grn_plugin_command_create(ctx, + "lock_release", -1, + command_lock_release, + 1, + vars); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_object.c b/storage/mroonga/vendor/groonga/lib/proc/proc_object.c new file mode 100644 index 00000000..380e6553 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_object.c @@ -0,0 +1,138 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2009-2016 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_proc.h" +#include "../grn_io.h" + +#include <groonga/plugin.h> + +#include <sys/types.h> +#include <sys/stat.h> + +static grn_obj * +command_object_exist(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *db; + grn_obj *name; + grn_id id; + + db = grn_ctx_db(ctx); + name = grn_plugin_proc_get_var(ctx, user_data, "name", -1); + if (GRN_TEXT_LEN(name) == 0) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[object][exist] name is missing"); + grn_ctx_output_bool(ctx, GRN_FALSE); + return NULL; + } + + id = grn_table_get(ctx, db, + GRN_TEXT_VALUE(name), + GRN_TEXT_LEN(name)); + grn_ctx_output_bool(ctx, id != GRN_ID_NIL); + return NULL; +} + +void +grn_proc_init_object_exist(grn_ctx *ctx) +{ + grn_expr_var vars[1]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "name", -1); + grn_plugin_command_create(ctx, + "object_exist", -1, + command_object_exist, + 1, + vars); +} + +static grn_obj * +command_object_remove(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *db; + grn_obj *name; + grn_bool force; + grn_obj *target; + grn_bool failed_to_open; + + db = grn_ctx_db(ctx); + name = grn_plugin_proc_get_var(ctx, user_data, "name", -1); + force = grn_plugin_proc_get_var_bool(ctx, user_data, "force", -1, GRN_FALSE); + + if (GRN_TEXT_LEN(name) == 0) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[object][remove] name is missing"); + grn_ctx_output_bool(ctx, GRN_FALSE); + return NULL; + } + + target = grn_ctx_get(ctx, + GRN_TEXT_VALUE(name), + GRN_TEXT_LEN(name)); + if (target) { + grn_obj_remove(ctx, target); + if (!force || ctx->rc == GRN_SUCCESS) { + grn_ctx_output_bool(ctx, ctx->rc == GRN_SUCCESS); + return NULL; + } + grn_obj_close(ctx, target); + failed_to_open = GRN_TRUE; + } else { + failed_to_open = (ctx->rc != GRN_SUCCESS); + } + + if (force) { + grn_obj_remove_force(ctx, GRN_TEXT_VALUE(name), GRN_TEXT_LEN(name)); + grn_ctx_output_bool(ctx, ctx->rc == GRN_SUCCESS); + } else { + if (failed_to_open) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[object][remove] " + "failed to open the target object: <%.*s>", + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name)); + } else { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[object][remove] target object doesn't exist: <%.*s>", + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name)); + } + grn_ctx_output_bool(ctx, GRN_FALSE); + } + + return NULL; +} + +void +grn_proc_init_object_remove(grn_ctx *ctx) +{ + grn_expr_var vars[2]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "name", -1); + grn_plugin_expr_var_init(ctx, &(vars[1]), "force", -1); + grn_plugin_command_create(ctx, + "object_remove", -1, + command_object_remove, + 2, + vars); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_object_inspect.c b/storage/mroonga/vendor/groonga/lib/proc/proc_object_inspect.c new file mode 100644 index 00000000..eaa6ec4b --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_object_inspect.c @@ -0,0 +1,614 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2016-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_pat.h" +#include "../grn_dat.h" +#include "../grn_ii.h" + +#include "../grn_proc.h" + +#include <groonga/plugin.h> + +static void command_object_inspect_dispatch(grn_ctx *ctx, grn_obj *obj); + +static void +command_object_inspect_obj_name(grn_ctx *ctx, grn_obj *obj) +{ + char name[GRN_TABLE_MAX_KEY_SIZE]; + int name_size; + + name_size = grn_obj_name(ctx, obj, name, GRN_TABLE_MAX_KEY_SIZE); + grn_ctx_output_str(ctx, name, name_size); +} + +static void +command_object_inspect_obj_type(grn_ctx *ctx, uint8_t type) +{ + grn_ctx_output_map_open(ctx, "type", 2); + { + grn_ctx_output_cstr(ctx, "id"); + grn_ctx_output_uint64(ctx, type); + grn_ctx_output_cstr(ctx, "name"); + grn_ctx_output_cstr(ctx, grn_obj_type_to_string(type)); + } + grn_ctx_output_map_close(ctx); +} + +static void +command_object_inspect_type(grn_ctx *ctx, grn_obj *type) +{ + if (!type) { + grn_ctx_output_null(ctx); + return; + } + + grn_ctx_output_map_open(ctx, "type", 4); + { + grn_ctx_output_cstr(ctx, "id"); + grn_ctx_output_uint64(ctx, grn_obj_id(ctx, type)); + grn_ctx_output_cstr(ctx, "name"); + command_object_inspect_obj_name(ctx, type); + grn_ctx_output_cstr(ctx, "type"); + command_object_inspect_obj_type(ctx, type->header.type); + grn_ctx_output_cstr(ctx, "size"); + if (type->header.type == GRN_TYPE) { + grn_ctx_output_uint64(ctx, grn_type_size(ctx, type)); + } else { + grn_ctx_output_uint64(ctx, sizeof(grn_id)); + } + } + grn_ctx_output_map_close(ctx); +} + +static void +command_object_inspect_disk_usage(grn_ctx *ctx, grn_obj *obj) +{ + grn_ctx_output_uint64(ctx, grn_obj_get_disk_usage(ctx, obj)); +} + +static void +command_object_inspect_table_hash_key_key(grn_ctx *ctx, grn_hash *hash) +{ + grn_ctx_output_map_open(ctx, "key", 3); + { + grn_ctx_output_cstr(ctx, "type"); + command_object_inspect_type(ctx, grn_ctx_at(ctx, hash->obj.header.domain)); + grn_ctx_output_cstr(ctx, "total_size"); + grn_ctx_output_uint64(ctx, grn_hash_total_key_size(ctx, hash)); + grn_ctx_output_cstr(ctx, "max_total_size"); + grn_ctx_output_uint64(ctx, grn_hash_max_total_key_size(ctx, hash)); + } + grn_ctx_output_map_close(ctx); +} + +static void +command_object_inspect_table_pat_key_key(grn_ctx *ctx, grn_pat *pat) +{ + grn_ctx_output_map_open(ctx, "key", 3); + { + grn_ctx_output_cstr(ctx, "type"); + command_object_inspect_type(ctx, grn_ctx_at(ctx, pat->obj.header.domain)); + grn_ctx_output_cstr(ctx, "total_size"); + grn_ctx_output_uint64(ctx, grn_pat_total_key_size(ctx, pat)); + grn_ctx_output_cstr(ctx, "max_total_size"); + grn_ctx_output_uint64(ctx, GRN_PAT_MAX_TOTAL_KEY_SIZE); + } + grn_ctx_output_map_close(ctx); +} + +static void +command_object_inspect_table_dat_key_key(grn_ctx *ctx, grn_dat *dat) +{ + grn_ctx_output_map_open(ctx, "key", 1); + { + grn_ctx_output_cstr(ctx, "type"); + command_object_inspect_type(ctx, grn_ctx_at(ctx, dat->obj.header.domain)); + } + grn_ctx_output_map_close(ctx); +} + +static void +command_object_inspect_table_key(grn_ctx *ctx, grn_obj *table) +{ + switch (table->header.type) { + case GRN_TABLE_HASH_KEY : + command_object_inspect_table_hash_key_key(ctx, (grn_hash *)table); + break; + case GRN_TABLE_PAT_KEY : + command_object_inspect_table_pat_key_key(ctx, (grn_pat *)table); + break; + case GRN_TABLE_DAT_KEY : + command_object_inspect_table_dat_key_key(ctx, (grn_dat *)table); + break; + case GRN_TABLE_NO_KEY : + grn_ctx_output_null(ctx); + break; + default : + break; + } +} + +static void +command_object_inspect_table_value(grn_ctx *ctx, grn_obj *table) +{ + if (table->header.type == GRN_TABLE_DAT_KEY) { + grn_ctx_output_null(ctx); + } else { + grn_ctx_output_map_open(ctx, "value", 1); + { + grn_id range_id = grn_obj_get_range(ctx, table); + grn_ctx_output_cstr(ctx, "type"); + command_object_inspect_type(ctx, grn_ctx_at(ctx, range_id)); + } + grn_ctx_output_map_close(ctx); + } +} + +static void +command_object_inspect_table(grn_ctx *ctx, grn_obj *obj) +{ + grn_ctx_output_map_open(ctx, "table", 7); + { + grn_ctx_output_cstr(ctx, "id"); + grn_ctx_output_uint64(ctx, grn_obj_id(ctx, obj)); + grn_ctx_output_cstr(ctx, "name"); + command_object_inspect_obj_name(ctx, obj); + grn_ctx_output_cstr(ctx, "type"); + command_object_inspect_obj_type(ctx, obj->header.type); + grn_ctx_output_cstr(ctx, "key"); + command_object_inspect_table_key(ctx, obj); + grn_ctx_output_cstr(ctx, "value"); + command_object_inspect_table_value(ctx, obj); + grn_ctx_output_cstr(ctx, "n_records"); + grn_ctx_output_uint64(ctx, grn_table_size(ctx, obj)); + grn_ctx_output_cstr(ctx, "disk_usage"); + command_object_inspect_disk_usage(ctx, obj); + } + grn_ctx_output_map_close(ctx); +} + +static void +command_object_inspect_column_name(grn_ctx *ctx, grn_obj *column) +{ + char name[GRN_TABLE_MAX_KEY_SIZE]; + int name_size; + + name_size = grn_column_name(ctx, column, name, GRN_TABLE_MAX_KEY_SIZE); + name[name_size] = '\0'; + grn_ctx_output_str(ctx, name, name_size); +} + +static void +command_object_inspect_column_type_name(grn_ctx *ctx, grn_obj *column) +{ + switch (column->header.type) { + case GRN_COLUMN_FIX_SIZE : + case GRN_COLUMN_VAR_SIZE : + switch (column->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) { + case GRN_OBJ_COLUMN_SCALAR : + grn_ctx_output_cstr(ctx, "scalar"); + break; + case GRN_OBJ_COLUMN_VECTOR : + grn_ctx_output_cstr(ctx, "vector"); + break; + } + break; + case GRN_COLUMN_INDEX : + grn_ctx_output_cstr(ctx, "index"); + break; + default: + break; + } +} + +static void +command_object_inspect_column_type(grn_ctx *ctx, grn_obj *column) +{ + grn_ctx_output_map_open(ctx, "type", 2); + { + grn_ctx_output_cstr(ctx, "name"); + command_object_inspect_column_type_name(ctx, column); + + grn_ctx_output_cstr(ctx, "raw"); + grn_ctx_output_map_open(ctx, "raw", 2); + { + grn_ctx_output_cstr(ctx, "id"); + grn_ctx_output_uint64(ctx, column->header.type); + grn_ctx_output_cstr(ctx, "name"); + grn_ctx_output_cstr(ctx, grn_obj_type_to_string(column->header.type)); + } + grn_ctx_output_map_close(ctx); + } + grn_ctx_output_map_close(ctx); +} + +static void +command_object_inspect_column_index_value_statistics(grn_ctx *ctx, + grn_ii *ii) +{ + grn_ctx_output_map_open(ctx, "statistics", 11); + { + struct grn_ii_header *h = ii->header; + + grn_ctx_output_cstr(ctx, "max_section_id"); + grn_ctx_output_uint64(ctx, grn_ii_max_section(ii)); + + { + uint32_t max_id = 0; + uint32_t n_garbage_segments = 0; + uint32_t n_array_segments = 0; + uint32_t n_buffer_segments = 0; + + grn_ctx_output_cstr(ctx, "n_garbage_segments"); + { + uint32_t i; + + for (i = h->bgqtail; + i != h->bgqhead; + i = ((i + 1) & (GRN_II_BGQSIZE - 1))) { + uint32_t id = h->bgqbody[i]; + n_garbage_segments++; + if (id > max_id) { max_id = id; } + } + grn_ctx_output_uint64(ctx, n_garbage_segments); + } + + grn_ctx_output_cstr(ctx, "max_array_segment_id"); + grn_ctx_output_uint64(ctx, h->amax); + grn_ctx_output_cstr(ctx, "n_array_segments"); + { + uint32_t i; + + for (i = 0; i < GRN_II_MAX_LSEG; i++) { + uint32_t id = h->ainfo[i]; + if (id != GRN_II_PSEG_NOT_ASSIGNED) { + if (id > max_id) { max_id = id; } + n_array_segments++; + } + } + grn_ctx_output_uint64(ctx, n_array_segments); + } + + grn_ctx_output_cstr(ctx, "max_buffer_segment_id"); + grn_ctx_output_uint64(ctx, h->bmax); + grn_ctx_output_cstr(ctx, "n_buffer_segments"); + { + uint32_t i; + + for (i = 0; i < GRN_II_MAX_LSEG; i++) { + uint32_t id = h->binfo[i]; + if (id != GRN_II_PSEG_NOT_ASSIGNED) { + if (id > max_id) { max_id = id; } + n_buffer_segments++; + } + } + grn_ctx_output_uint64(ctx, n_buffer_segments); + } + + grn_ctx_output_cstr(ctx, "max_in_use_physical_segment_id"); + grn_ctx_output_uint64(ctx, max_id); + + grn_ctx_output_cstr(ctx, "n_unmanaged_segments"); + grn_ctx_output_uint64(ctx, + h->pnext - + n_array_segments - + n_buffer_segments - + n_garbage_segments); + } + + { + grn_ctx_output_cstr(ctx, "total_chunk_size"); + grn_ctx_output_uint64(ctx, h->total_chunk_size); + grn_ctx_output_cstr(ctx, "max_in_use_chunk_id"); + { + uint32_t i; + uint32_t max_id; + + for (max_id = 0, i = 0; i < (GRN_II_MAX_CHUNK >> 3); i++) { + uint8_t sub_chunk_info = h->chunks[i]; + uint8_t bit; + + if (sub_chunk_info == 0) { + continue; + } + for (bit = 0; bit < 8; bit++) { + if (sub_chunk_info & (1 << bit)) { + max_id = (i << 3) + sub_chunk_info; + } + } + } + grn_ctx_output_uint64(ctx, max_id); + } + grn_ctx_output_cstr(ctx, "n_garbage_chunks"); + grn_ctx_output_array_open(ctx, + "n_garbage_chunks", + GRN_II_N_CHUNK_VARIATION); + { + uint32_t i; + for (i = 0; i <= GRN_II_N_CHUNK_VARIATION; i++) { + grn_ctx_output_uint64(ctx, h->ngarbages[i]); + } + } + grn_ctx_output_array_close(ctx); + } + } + grn_ctx_output_map_close(ctx); +} + +static void +command_object_inspect_column_data_value_compress(grn_ctx *ctx, grn_obj *column) +{ + const char *compress = NULL; + grn_column_flags column_flags; + + column_flags = grn_column_get_flags(ctx, column); + switch (column_flags & GRN_OBJ_COMPRESS_MASK) { + case GRN_OBJ_COMPRESS_ZLIB : + compress = "zlib"; + break; + case GRN_OBJ_COMPRESS_LZ4 : + compress = "lz4"; + break; + case GRN_OBJ_COMPRESS_ZSTD : + compress = "zstd"; + break; + default : + break; + } + + if (compress) { + grn_ctx_output_cstr(ctx, compress); + } else { + grn_ctx_output_null(ctx); + } +} + +static void +command_object_inspect_column_value(grn_ctx *ctx, grn_obj *column) +{ + int n_elements = 1; + grn_bool is_index = (column->header.type == GRN_COLUMN_INDEX); + + if (is_index) { + n_elements += 5; + } else { + n_elements += 1; + } + grn_ctx_output_map_open(ctx, "value", n_elements); + { + grn_id range_id; + grn_column_flags column_flags; + + range_id = grn_obj_get_range(ctx, column); + column_flags = grn_column_get_flags(ctx, column); + + grn_ctx_output_cstr(ctx, "type"); + command_object_inspect_type(ctx, grn_ctx_at(ctx, range_id)); + if (is_index) { + grn_ctx_output_cstr(ctx, "section"); + grn_ctx_output_bool(ctx, (column_flags & GRN_OBJ_WITH_SECTION) != 0); + grn_ctx_output_cstr(ctx, "weight"); + grn_ctx_output_bool(ctx, (column_flags & GRN_OBJ_WITH_WEIGHT) != 0); + grn_ctx_output_cstr(ctx, "position"); + grn_ctx_output_bool(ctx, (column_flags & GRN_OBJ_WITH_POSITION) != 0); + grn_ctx_output_cstr(ctx, "size"); + if ((column_flags & GRN_OBJ_INDEX_SMALL) != 0) { + grn_ctx_output_cstr(ctx, "small"); + } else if ((column_flags & GRN_OBJ_INDEX_MEDIUM) != 0) { + grn_ctx_output_cstr(ctx, "medium"); + } else { + grn_ctx_output_cstr(ctx, "normal"); + } + grn_ctx_output_cstr(ctx, "statistics"); + command_object_inspect_column_index_value_statistics(ctx, + (grn_ii *)column); + } else { + grn_ctx_output_cstr(ctx, "compress"); + command_object_inspect_column_data_value_compress(ctx, column); + } + } + grn_ctx_output_map_close(ctx); +} + +static void +command_object_inspect_column_index_sources(grn_ctx *ctx, grn_obj *column) +{ + grn_obj *source_table; + grn_obj source_ids; + unsigned int i, n_ids; + + source_table = grn_ctx_at(ctx, grn_obj_get_range(ctx, column)); + + GRN_RECORD_INIT(&source_ids, GRN_OBJ_VECTOR, GRN_ID_NIL); + grn_obj_get_info(ctx, column, GRN_INFO_SOURCE, &source_ids); + + n_ids = GRN_BULK_VSIZE(&source_ids) / sizeof(grn_id); + grn_ctx_output_array_open(ctx, "sources", n_ids); + for (i = 0; i < n_ids; i++) { + grn_id source_id; + grn_obj *source; + + source_id = GRN_RECORD_VALUE_AT(&source_ids, i); + source = grn_ctx_at(ctx, source_id); + + grn_ctx_output_map_open(ctx, "source", 4); + { + grn_ctx_output_cstr(ctx, "id"); + if (grn_obj_is_table(ctx, source)) { + grn_ctx_output_null(ctx); + } else { + grn_ctx_output_uint64(ctx, source_id); + } + + grn_ctx_output_cstr(ctx, "name"); + if (grn_obj_is_table(ctx, source)) { + grn_ctx_output_cstr(ctx, "_key"); + } else { + command_object_inspect_column_name(ctx, source); + } + + grn_ctx_output_cstr(ctx, "table"); + command_object_inspect_table(ctx, source_table); + + grn_ctx_output_cstr(ctx, "full_name"); + if (grn_obj_is_table(ctx, source)) { + char name[GRN_TABLE_MAX_KEY_SIZE]; + unsigned int name_size; + name_size = grn_obj_name(ctx, source, name, GRN_TABLE_MAX_KEY_SIZE); + name[name_size] = '\0'; + grn_strcat(name, GRN_TABLE_MAX_KEY_SIZE, "._key"); + grn_ctx_output_cstr(ctx, name); + } else { + command_object_inspect_obj_name(ctx, source); + } + } + grn_ctx_output_map_close(ctx); + } + grn_ctx_output_array_close(ctx); + + GRN_OBJ_FIN(ctx, &source_ids); +} + +static void +command_object_inspect_column(grn_ctx *ctx, grn_obj *column) +{ + int n_elements = 7; + grn_bool is_index = (column->header.type == GRN_COLUMN_INDEX); + + if (is_index) { + n_elements += 1; + } + grn_ctx_output_map_open(ctx, "column", n_elements); + { + grn_ctx_output_cstr(ctx, "id"); + grn_ctx_output_uint64(ctx, grn_obj_id(ctx, column)); + grn_ctx_output_cstr(ctx, "name"); + command_object_inspect_column_name(ctx, column); + grn_ctx_output_cstr(ctx, "table"); + command_object_inspect_table(ctx, grn_ctx_at(ctx, column->header.domain)); + grn_ctx_output_cstr(ctx, "full_name"); + command_object_inspect_obj_name(ctx, column); + grn_ctx_output_cstr(ctx, "type"); + command_object_inspect_column_type(ctx, column); + grn_ctx_output_cstr(ctx, "value"); + command_object_inspect_column_value(ctx, column); + if (is_index) { + grn_ctx_output_cstr(ctx, "sources"); + command_object_inspect_column_index_sources(ctx, column); + } + grn_ctx_output_cstr(ctx, "disk_usage"); + command_object_inspect_disk_usage(ctx, column); + } + grn_ctx_output_map_close(ctx); +} + +static void +command_object_inspect_db(grn_ctx *ctx, grn_obj *obj) +{ + grn_db *db = (grn_db *)obj; + + grn_ctx_output_map_open(ctx, "database", 3); + { + grn_ctx_output_cstr(ctx, "type"); + command_object_inspect_obj_type(ctx, obj->header.type); + grn_ctx_output_cstr(ctx, "name_table"); + command_object_inspect_dispatch(ctx, db->keys); + grn_ctx_output_cstr(ctx, "disk_usage"); + command_object_inspect_disk_usage(ctx, obj); + } + grn_ctx_output_map_close(ctx); +} + +static void +command_object_inspect_dispatch(grn_ctx *ctx, grn_obj *obj) +{ + switch (obj->header.type) { + case GRN_TYPE : + command_object_inspect_type(ctx, obj); + break; + case GRN_TABLE_HASH_KEY : + case GRN_TABLE_PAT_KEY : + case GRN_TABLE_DAT_KEY : + case GRN_TABLE_NO_KEY : + command_object_inspect_table(ctx, obj); + break; + case GRN_COLUMN_FIX_SIZE : + case GRN_COLUMN_VAR_SIZE : + case GRN_COLUMN_INDEX : + command_object_inspect_column(ctx, obj); + break; + case GRN_DB : + command_object_inspect_db(ctx, obj); + break; + default : + { + GRN_PLUGIN_ERROR(ctx, + GRN_FUNCTION_NOT_IMPLEMENTED, + "[object][inspect] unsupported type: <%s>(%#x)", + grn_obj_type_to_string(obj->header.type), + obj->header.type); + grn_ctx_output_null(ctx); + break; + } + } +} + +static grn_obj * +command_object_inspect(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *name; + grn_obj *target; + + name = grn_plugin_proc_get_var(ctx, user_data, "name", -1); + if (GRN_TEXT_LEN(name) == 0) { + target = grn_ctx_db(ctx); + } else { + target = grn_ctx_get(ctx, + GRN_TEXT_VALUE(name), + GRN_TEXT_LEN(name)); + if (!target) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[object][inspect] nonexistent target: <%.*s>", + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name)); + grn_ctx_output_null(ctx); + return NULL; + } + } + + command_object_inspect_dispatch(ctx, target); + + return NULL; +} + +void +grn_proc_init_object_inspect(grn_ctx *ctx) +{ + grn_expr_var vars[1]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "name", -1); + grn_plugin_command_create(ctx, + "object_inspect", -1, + command_object_inspect, + 1, + vars); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_object_list.c b/storage/mroonga/vendor/groonga/lib/proc/proc_object_list.c new file mode 100644 index 00000000..adb4c91b --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_object_list.c @@ -0,0 +1,413 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2016 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_proc.h" +#include "../grn_db.h" + +#include <groonga/plugin.h> + +static void +command_object_list_dump_flags(grn_ctx *ctx, grn_obj_spec *spec) +{ + grn_obj flags; + + GRN_TEXT_INIT(&flags, 0); + + switch (spec->header.type) { + case GRN_TABLE_HASH_KEY : + case GRN_TABLE_PAT_KEY : + case GRN_TABLE_DAT_KEY : + case GRN_TABLE_NO_KEY : + grn_dump_table_create_flags(ctx, spec->header.flags, &flags); + break; + case GRN_COLUMN_VAR_SIZE : + case GRN_COLUMN_FIX_SIZE : + case GRN_COLUMN_INDEX : + grn_dump_column_create_flags(ctx, spec->header.flags, &flags); + break; + case GRN_TYPE : + if (spec->header.flags & GRN_OBJ_KEY_VAR_SIZE) { + GRN_TEXT_PUTS(ctx, &flags, "KEY_VAR_SIZE"); + } else { + switch (spec->header.flags & GRN_OBJ_KEY_MASK) { + case GRN_OBJ_KEY_UINT : + GRN_TEXT_PUTS(ctx, &flags, "KEY_UINT"); + break; + case GRN_OBJ_KEY_INT : + GRN_TEXT_PUTS(ctx, &flags, "KEY_INT"); + break; + case GRN_OBJ_KEY_FLOAT : + GRN_TEXT_PUTS(ctx, &flags, "KEY_FLOAT"); + break; + case GRN_OBJ_KEY_GEO_POINT : + GRN_TEXT_PUTS(ctx, &flags, "KEY_GEO_POINT"); + break; + } + } + break; + } + if (spec->header.flags & GRN_OBJ_CUSTOM_NAME) { + if (GRN_TEXT_LEN(&flags) > 0) { + GRN_TEXT_PUTS(ctx, &flags, "|"); + } + GRN_TEXT_PUTS(ctx, &flags, "CUSTOM_NAME"); + } + + grn_ctx_output_str(ctx, GRN_TEXT_VALUE(&flags), GRN_TEXT_LEN(&flags)); + + GRN_OBJ_FIN(ctx, &flags); +} + +static grn_obj * +command_object_list(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + grn_db *db; + uint32_t n_objects = 0; + grn_obj vector; + + db = (grn_db *)grn_ctx_db(ctx); + if (!db->specs) { + grn_ctx_output_map_open(ctx, "objects", n_objects); + grn_ctx_output_map_close(ctx); + return NULL; + } + + GRN_TABLE_EACH_BEGIN_FLAGS(ctx, (grn_obj *)db, cursor, id, + GRN_CURSOR_BY_ID | GRN_CURSOR_ASCENDING) { + grn_io_win jw; + uint32_t value_len; + char *value; + + value = grn_ja_ref(ctx, db->specs, id, &jw, &value_len); + if (value) { + n_objects++; + grn_ja_unref(ctx, &jw); + } + } GRN_TABLE_EACH_END(ctx, cursor); + + GRN_OBJ_INIT(&vector, GRN_VECTOR, 0, GRN_DB_TEXT); + + grn_ctx_output_map_open(ctx, "objects", n_objects); + GRN_TABLE_EACH_BEGIN_FLAGS(ctx, (grn_obj *)db, cursor, id, + GRN_CURSOR_BY_ID | GRN_CURSOR_ASCENDING) { + void *name; + int name_size; + grn_io_win jw; + uint32_t value_len; + char *value; + unsigned int n_elements; + + value = grn_ja_ref(ctx, db->specs, id, &jw, &value_len); + if (!value) { + continue; + } + + name_size = grn_table_cursor_get_key(ctx, cursor, &name); + + grn_ctx_output_str(ctx, name, name_size); + + GRN_BULK_REWIND(&vector); + if (grn_vector_decode(ctx, &vector, value, value_len) != GRN_SUCCESS) { + grn_ctx_output_map_open(ctx, "object", 4); + { + grn_ctx_output_cstr(ctx, "id"); + grn_ctx_output_int64(ctx, id); + grn_ctx_output_cstr(ctx, "name"); + grn_ctx_output_str(ctx, name, name_size); + grn_ctx_output_cstr(ctx, "opened"); + grn_ctx_output_bool(ctx, grn_ctx_is_opened(ctx, id)); + grn_ctx_output_cstr(ctx, "value_size"); + grn_ctx_output_uint64(ctx, value_len); + } + grn_ctx_output_map_close(ctx); + goto next; + } + + n_elements = grn_vector_size(ctx, &vector); + + { + uint32_t element_size; + grn_obj_spec *spec; + uint32_t n_properties = 8; + grn_bool need_sources = GRN_FALSE; + grn_bool need_token_filters = GRN_FALSE; + + element_size = grn_vector_get_element(ctx, + &vector, + GRN_SERIALIZED_SPEC_INDEX_SPEC, + (const char **)&spec, + NULL, + NULL); + if (element_size == 0) { + grn_ctx_output_map_open(ctx, "object", 4); + { + grn_ctx_output_cstr(ctx, "id"); + grn_ctx_output_int64(ctx, id); + grn_ctx_output_cstr(ctx, "name"); + grn_ctx_output_str(ctx, name, name_size); + grn_ctx_output_cstr(ctx, "opened"); + grn_ctx_output_bool(ctx, grn_ctx_is_opened(ctx, id)); + grn_ctx_output_cstr(ctx, "n_elements"); + grn_ctx_output_uint64(ctx, n_elements); + } + grn_ctx_output_map_close(ctx); + goto next; + } + + switch (spec->header.type) { + case GRN_COLUMN_INDEX : + need_sources = GRN_TRUE; + n_properties++; + break; + case GRN_TABLE_PAT_KEY : + case GRN_TABLE_DAT_KEY : + case GRN_TABLE_HASH_KEY : + case GRN_TABLE_NO_KEY : + need_token_filters = GRN_TRUE; + n_properties++; + break; + } + grn_ctx_output_map_open(ctx, "object", n_properties); + { + grn_ctx_output_cstr(ctx, "id"); + grn_ctx_output_uint64(ctx, id); + + grn_ctx_output_cstr(ctx, "name"); + grn_ctx_output_str(ctx, name, name_size); + + grn_ctx_output_cstr(ctx, "opened"); + grn_ctx_output_bool(ctx, grn_ctx_is_opened(ctx, id)); + + grn_ctx_output_cstr(ctx, "n_elements"); + grn_ctx_output_uint64(ctx, n_elements); + + grn_ctx_output_cstr(ctx, "type"); + grn_ctx_output_map_open(ctx, "type", 2); + { + grn_ctx_output_cstr(ctx, "id"); + grn_ctx_output_uint64(ctx, spec->header.type); + grn_ctx_output_cstr(ctx, "name"); + grn_ctx_output_cstr(ctx, grn_obj_type_to_string(spec->header.type)); + } + grn_ctx_output_map_close(ctx); + + grn_ctx_output_cstr(ctx, "flags"); + grn_ctx_output_map_open(ctx, "flags", 2); + { + grn_ctx_output_cstr(ctx, "value"); + grn_ctx_output_uint64(ctx, spec->header.flags); + grn_ctx_output_cstr(ctx, "names"); + command_object_list_dump_flags(ctx, spec); + } + grn_ctx_output_map_close(ctx); + + grn_ctx_output_cstr(ctx, "path"); + if (spec->header.flags & GRN_OBJ_CUSTOM_NAME) { + const char *path; + uint32_t path_size; + path_size = grn_vector_get_element(ctx, + &vector, + GRN_SERIALIZED_SPEC_INDEX_PATH, + &path, + NULL, + NULL); + grn_ctx_output_str(ctx, path, path_size); + } else { + switch (spec->header.type) { + case GRN_TABLE_HASH_KEY : + case GRN_TABLE_PAT_KEY : + case GRN_TABLE_DAT_KEY : + case GRN_TABLE_NO_KEY : + case GRN_COLUMN_VAR_SIZE : + case GRN_COLUMN_FIX_SIZE : + case GRN_COLUMN_INDEX : + { + char path[PATH_MAX]; + grn_db_generate_pathname(ctx, (grn_obj *)db, id, path); + grn_ctx_output_cstr(ctx, path); + } + break; + default : + grn_ctx_output_null(ctx); + break; + } + } + + switch (spec->header.type) { + case GRN_TYPE : + grn_ctx_output_cstr(ctx, "size"); + grn_ctx_output_uint64(ctx, spec->range); + break; + case GRN_PROC : + grn_ctx_output_cstr(ctx, "plugin_id"); + grn_ctx_output_uint64(ctx, spec->range); + break; + default : + grn_ctx_output_cstr(ctx, "range"); + grn_ctx_output_map_open(ctx, "range", 2); + { + char name[GRN_TABLE_MAX_KEY_SIZE]; + int name_size; + + name_size = grn_table_get_key(ctx, + (grn_obj *)db, + spec->range, + name, + GRN_TABLE_MAX_KEY_SIZE); + + grn_ctx_output_cstr(ctx, "id"); + grn_ctx_output_uint64(ctx, spec->range); + + grn_ctx_output_cstr(ctx, "name"); + if (name_size == 0) { + grn_ctx_output_null(ctx); + } else { + grn_ctx_output_str(ctx, name, name_size); + } + } + grn_ctx_output_map_close(ctx); + break; + } + + if (need_sources) { + const grn_id *source_ids; + uint32_t n_source_ids; + uint32_t i; + + if (n_elements > GRN_SERIALIZED_SPEC_INDEX_SOURCE) { + uint32_t element_size; + + element_size = grn_vector_get_element(ctx, + &vector, + GRN_SERIALIZED_SPEC_INDEX_SOURCE, + (const char **)&source_ids, + NULL, + NULL); + n_source_ids = element_size / sizeof(grn_id); + } else { + source_ids = NULL; + n_source_ids = 0; + } + + grn_ctx_output_cstr(ctx, "sources"); + grn_ctx_output_array_open(ctx, "sources", n_source_ids); + for (i = 0; i < n_source_ids; i++) { + grn_id source_id; + char name[GRN_TABLE_MAX_KEY_SIZE]; + int name_size; + + source_id = source_ids[i]; + name_size = grn_table_get_key(ctx, + (grn_obj *)db, + source_id, + name, + GRN_TABLE_MAX_KEY_SIZE); + + grn_ctx_output_map_open(ctx, "source", 2); + { + grn_ctx_output_cstr(ctx, "id"); + grn_ctx_output_uint64(ctx, source_id); + + grn_ctx_output_cstr(ctx, "name"); + if (name_size == 0) { + grn_ctx_output_null(ctx); + } else { + grn_ctx_output_str(ctx, name, name_size); + } + } + grn_ctx_output_map_close(ctx); + } + grn_ctx_output_array_close(ctx); + } + + if (need_token_filters) { + const grn_id *token_filter_ids; + uint32_t n_token_filter_ids; + uint32_t i; + + if (n_elements > GRN_SERIALIZED_SPEC_INDEX_TOKEN_FILTERS) { + uint32_t element_size; + + element_size = grn_vector_get_element(ctx, + &vector, + GRN_SERIALIZED_SPEC_INDEX_TOKEN_FILTERS, + (const char **)&token_filter_ids, + NULL, + NULL); + n_token_filter_ids = element_size / sizeof(grn_id); + } else { + token_filter_ids = NULL; + n_token_filter_ids = 0; + } + + grn_ctx_output_cstr(ctx, "token_filters"); + grn_ctx_output_array_open(ctx, "token_filters", n_token_filter_ids); + for (i = 0; i < n_token_filter_ids; i++) { + grn_id token_filter_id; + char name[GRN_TABLE_MAX_KEY_SIZE]; + int name_size; + + token_filter_id = token_filter_ids[i]; + name_size = grn_table_get_key(ctx, + (grn_obj *)db, + token_filter_id, + name, + GRN_TABLE_MAX_KEY_SIZE); + + grn_ctx_output_map_open(ctx, "token_filter", 2); + { + grn_ctx_output_cstr(ctx, "id"); + grn_ctx_output_uint64(ctx, token_filter_id); + + grn_ctx_output_cstr(ctx, "name"); + if (name_size == 0) { + grn_ctx_output_null(ctx); + } else { + grn_ctx_output_str(ctx, name, name_size); + } + } + grn_ctx_output_map_close(ctx); + } + grn_ctx_output_array_close(ctx); + } + } + grn_ctx_output_map_close(ctx); + } + + next : + grn_ja_unref(ctx, &jw); + } GRN_TABLE_EACH_END(ctx, cursor); + grn_ctx_output_map_close(ctx); + + GRN_OBJ_FIN(ctx, &vector); + + return NULL; +} + +void +grn_proc_init_object_list(grn_ctx *ctx) +{ + grn_plugin_command_create(ctx, + "object_list", -1, + command_object_list, + 0, + NULL); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_query.c b/storage/mroonga/vendor/groonga/lib/proc/proc_query.c new file mode 100644 index 00000000..6dcf63e1 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_query.c @@ -0,0 +1,118 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2016 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_proc.h" + +#include <groonga/plugin.h> + +static grn_obj * +command_query_expand(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + const char *expander; + size_t expander_size; + const char *query; + size_t query_size; + const char *flags_raw; + size_t flags_raw_size; + grn_expr_flags flags = GRN_EXPR_SYNTAX_QUERY; + const char *term_column; + size_t term_column_size; + const char *expanded_term_column; + size_t expanded_term_column_size; + grn_obj expanded_query; + + expander = grn_plugin_proc_get_var_string(ctx, + user_data, + "expander", + -1, + &expander_size); + query = grn_plugin_proc_get_var_string(ctx, + user_data, + "query", + -1, + &query_size); + flags_raw = grn_plugin_proc_get_var_string(ctx, + user_data, + "flags", + -1, + &flags_raw_size); + term_column = grn_plugin_proc_get_var_string(ctx, + user_data, + "term_column", + -1, + &term_column_size); + expanded_term_column = + grn_plugin_proc_get_var_string(ctx, + user_data, + "expanded_term_column", + -1, + &expanded_term_column_size); + + if (flags_raw_size > 0) { + flags |= grn_proc_expr_query_flags_parse(ctx, + flags_raw, + flags_raw_size, + "[query][expand]"); + } else { + flags |= GRN_EXPR_ALLOW_PRAGMA | GRN_EXPR_ALLOW_COLUMN; + } + + if (ctx->rc != GRN_SUCCESS) { + return NULL; + } + + GRN_TEXT_INIT(&expanded_query, 0); + grn_proc_syntax_expand_query(ctx, + query, + query_size, + flags, + expander, + expander_size, + term_column, + term_column_size, + expanded_term_column, + expanded_term_column_size, + &expanded_query, + "[query][expand]"); + if (ctx->rc == GRN_SUCCESS) { + grn_ctx_output_str(ctx, + GRN_TEXT_VALUE(&expanded_query), + GRN_TEXT_LEN(&expanded_query)); + } + GRN_OBJ_FIN(ctx, &expanded_query); + + return NULL; +} + +void +grn_proc_init_query_expand(grn_ctx *ctx) +{ + grn_expr_var vars[5]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "expander", -1); + grn_plugin_expr_var_init(ctx, &(vars[1]), "query", -1); + grn_plugin_expr_var_init(ctx, &(vars[2]), "flags", -1); + grn_plugin_expr_var_init(ctx, &(vars[3]), "term_column", -1); + grn_plugin_expr_var_init(ctx, &(vars[4]), "expanded_term_column", -1); + grn_plugin_command_create(ctx, + "query_expand", -1, + command_query_expand, + 5, + vars); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_query_log_flags.c b/storage/mroonga/vendor/groonga/lib/proc/proc_query_log_flags.c new file mode 100644 index 00000000..b05d1abf --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_query_log_flags.c @@ -0,0 +1,220 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 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_proc.h" + +#include <groonga/plugin.h> + +static grn_obj * +command_query_log_flags_get(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + unsigned int current_flags; + grn_obj inspected_flags; + + current_flags = grn_query_logger_get_flags(ctx); + GRN_TEXT_INIT(&inspected_flags, 0); + + grn_inspect_query_log_flags(ctx, &inspected_flags,current_flags); + grn_ctx_output_str(ctx, + GRN_TEXT_VALUE(&inspected_flags), + GRN_TEXT_LEN(&inspected_flags)); + + GRN_OBJ_FIN(ctx, &inspected_flags); + + return NULL; +} + +void +grn_proc_init_query_log_flags_get(grn_ctx *ctx) +{ + grn_plugin_command_create(ctx, + "query_log_flags_get", -1, + command_query_log_flags_get, + 0, + NULL); +} + +typedef enum { + UPDATE_SET, + UPDATE_ADD, + UPDATE_REMOVE +} grn_query_log_flags_update_mode; + +static void +grn_query_log_flags_update(grn_ctx *ctx, + grn_obj *flags_text, + grn_query_log_flags_update_mode mode, + const char *error_message_tag) +{ + unsigned int previous_flags; + unsigned int flags = 0; + + previous_flags = grn_query_logger_get_flags(ctx); + if (GRN_TEXT_LEN(flags_text) == 0) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "%s no query log flags", + error_message_tag); + grn_ctx_output_null(ctx); + return; + } + + if (!grn_query_log_flags_parse(GRN_TEXT_VALUE(flags_text), + GRN_TEXT_LEN(flags_text), + &flags)) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "%s invalid query log flags: <%.*s>", + error_message_tag, + (int)GRN_TEXT_LEN(flags_text), + GRN_TEXT_VALUE(flags_text)); + grn_ctx_output_null(ctx); + return; + } + + switch (mode) { + case UPDATE_SET : + grn_query_logger_set_flags(ctx, flags); + break; + case UPDATE_ADD : + grn_query_logger_add_flags(ctx, flags); + break; + case UPDATE_REMOVE : + grn_query_logger_remove_flags(ctx, flags); + break; + } + + { + unsigned int current_flags; + grn_obj inspected_flags; + + current_flags = grn_query_logger_get_flags(ctx); + GRN_TEXT_INIT(&inspected_flags, 0); + + grn_ctx_output_map_open(ctx, "query_log_flags", 2); + + grn_inspect_query_log_flags(ctx, &inspected_flags, previous_flags); + grn_ctx_output_cstr(ctx, "previous"); + grn_ctx_output_str(ctx, + GRN_TEXT_VALUE(&inspected_flags), + GRN_TEXT_LEN(&inspected_flags)); + + GRN_BULK_REWIND(&inspected_flags); + grn_inspect_query_log_flags(ctx, &inspected_flags, current_flags); + grn_ctx_output_cstr(ctx, "current"); + grn_ctx_output_str(ctx, + GRN_TEXT_VALUE(&inspected_flags), + GRN_TEXT_LEN(&inspected_flags)); + + grn_ctx_output_map_close(ctx); + + GRN_OBJ_FIN(ctx, &inspected_flags); + } + + return; +} + +static grn_obj * +command_query_log_flags_set(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *flags_text; + + flags_text = grn_plugin_proc_get_var(ctx, user_data, "flags", -1); + grn_query_log_flags_update(ctx, + flags_text, + UPDATE_SET, + "[query-log][flags][set]"); + return NULL; +} + +void +grn_proc_init_query_log_flags_set(grn_ctx *ctx) +{ + grn_expr_var vars[1]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "flags", -1); + grn_plugin_command_create(ctx, + "query_log_flags_set", -1, + command_query_log_flags_set, + 1, + vars); +} + +static grn_obj * +command_query_log_flags_add(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *flags_text; + + flags_text = grn_plugin_proc_get_var(ctx, user_data, "flags", -1); + grn_query_log_flags_update(ctx, + flags_text, + UPDATE_ADD, + "[query-log][flags][add]"); + return NULL; +} + +void +grn_proc_init_query_log_flags_add(grn_ctx *ctx) +{ + grn_expr_var vars[1]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "flags", -1); + grn_plugin_command_create(ctx, + "query_log_flags_add", -1, + command_query_log_flags_add, + 1, + vars); +} + +static grn_obj * +command_query_log_flags_remove(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *flags_text; + + flags_text = grn_plugin_proc_get_var(ctx, user_data, "flags", -1); + grn_query_log_flags_update(ctx, + flags_text, + UPDATE_REMOVE, + "[query-log][flags][remove]"); + return NULL; +} + +void +grn_proc_init_query_log_flags_remove(grn_ctx *ctx) +{ + grn_expr_var vars[1]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "flags", -1); + grn_plugin_command_create(ctx, + "query_log_flags_remove", -1, + command_query_log_flags_remove, + 1, + vars); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_schema.c b/storage/mroonga/vendor/groonga/lib/proc/proc_schema.c new file mode 100644 index 00000000..061c145a --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_schema.c @@ -0,0 +1,1226 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2015-2016 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_proc.h" + +#include "../grn_db.h" + +#include <groonga/plugin.h> + +typedef struct { + grn_bool is_close_opened_object_mode; +} grn_schema_data; + +static void +command_schema_output_id(grn_ctx *ctx, grn_obj *obj) +{ + if (obj) { + grn_id id; + id = grn_obj_id(ctx, obj); + grn_ctx_output_uint64(ctx, id); + } else { + grn_ctx_output_null(ctx); + } +} + +static void +command_schema_output_name(grn_ctx *ctx, grn_obj *obj) +{ + if (obj) { + char name[GRN_TABLE_MAX_KEY_SIZE]; + unsigned int name_size; + name_size = grn_obj_name(ctx, obj, name, GRN_TABLE_MAX_KEY_SIZE); + grn_ctx_output_str(ctx, name, name_size); + } else { + grn_ctx_output_null(ctx); + } +} + +static void +command_schema_output_column_name(grn_ctx *ctx, grn_obj *column) +{ + char name[GRN_TABLE_MAX_KEY_SIZE]; + unsigned int name_size; + name_size = grn_column_name(ctx, column, name, GRN_TABLE_MAX_KEY_SIZE); + grn_ctx_output_str(ctx, name, name_size); +} + +static void +command_schema_output_type(grn_ctx *ctx, const char *type_label, grn_obj *type) +{ + if (!type) { + grn_ctx_output_null(ctx); + return; + } + + grn_ctx_output_map_open(ctx, type_label, 3); + + grn_ctx_output_cstr(ctx, "id"); + command_schema_output_id(ctx, type); + + grn_ctx_output_cstr(ctx, "name"); + command_schema_output_name(ctx, type); + + grn_ctx_output_cstr(ctx, "type"); + if (grn_obj_is_table(ctx, type)) { + grn_ctx_output_cstr(ctx, "reference"); + } else { + grn_ctx_output_cstr(ctx, "type"); + } + grn_ctx_output_map_close(ctx); +} + +static void +command_schema_output_key_type(grn_ctx *ctx, grn_obj *key_type) +{ + command_schema_output_type(ctx, "key_type", key_type); +} + +static void +command_schema_output_value_type(grn_ctx *ctx, grn_obj *value_type) +{ + command_schema_output_type(ctx, "value_type", value_type); +} + +static void +command_schema_output_command(grn_ctx *ctx, + const char *command_name, + grn_obj *arguments) +{ + grn_ctx_output_map_open(ctx, "command", 3); + + grn_ctx_output_cstr(ctx, "name"); + grn_ctx_output_cstr(ctx, command_name); + + grn_ctx_output_cstr(ctx, "arguments"); + { + int i, n; + + n = grn_vector_size(ctx, arguments); + grn_ctx_output_map_open(ctx, "arguments", n / 2); + for (i = 0; i < n; i += 2) { + const char *name; + unsigned int name_size; + const char *value; + unsigned int value_size; + + name_size = grn_vector_get_element(ctx, arguments, i, &name, + NULL, NULL); + value_size = grn_vector_get_element(ctx, arguments, i + 1, &value, + NULL, NULL); + grn_ctx_output_str(ctx, name, name_size); + grn_ctx_output_str(ctx, value, value_size); + } + grn_ctx_output_map_close(ctx); + } + + grn_ctx_output_cstr(ctx, "command_line"); + { + int i, n; + grn_obj command_line; + + GRN_TEXT_INIT(&command_line, 0); + GRN_TEXT_PUTS(ctx, &command_line, command_name); + n = grn_vector_size(ctx, arguments); + for (i = 0; i < n; i += 2) { + const char *name; + unsigned int name_size; + const char *value; + unsigned int value_size; + + name_size = grn_vector_get_element(ctx, arguments, i, &name, + NULL, NULL); + value_size = grn_vector_get_element(ctx, arguments, i + 1, &value, + NULL, NULL); + grn_text_printf(ctx, &command_line, + " --%.*s %.*s", + name_size, name, + value_size, value); + } + grn_ctx_output_str(ctx, + GRN_TEXT_VALUE(&command_line), + GRN_TEXT_LEN(&command_line)); + GRN_OBJ_FIN(ctx, &command_line); + } + + grn_ctx_output_map_close(ctx); +} + +static void +command_schema_output_plugins(grn_ctx *ctx) +{ + grn_obj plugin_names; + unsigned int i, n; + + GRN_TEXT_INIT(&plugin_names, GRN_OBJ_VECTOR); + + grn_plugin_get_names(ctx, &plugin_names); + + grn_ctx_output_cstr(ctx, "plugins"); + + n = grn_vector_size(ctx, &plugin_names); + grn_ctx_output_map_open(ctx, "plugins", n); + for (i = 0; i < n; i++) { + const char *name; + unsigned int name_size; + + name_size = grn_vector_get_element(ctx, &plugin_names, i, &name, NULL, NULL); + grn_ctx_output_str(ctx, name, name_size); + + grn_ctx_output_map_open(ctx, "plugin", 1); + grn_ctx_output_cstr(ctx, "name"); + grn_ctx_output_str(ctx, name, name_size); + grn_ctx_output_map_close(ctx); + } + grn_ctx_output_map_close(ctx); + + GRN_OBJ_FIN(ctx, &plugin_names); +} + +static void +command_schema_output_types(grn_ctx *ctx) +{ + unsigned int n_types; + + n_types = 0; + GRN_DB_EACH_BEGIN_BY_KEY(ctx, cursor, id) { + if (grn_id_is_builtin_type(ctx, id)) { + n_types++; + } + } GRN_DB_EACH_END(ctx, cursor); + + grn_ctx_output_cstr(ctx, "types"); + + grn_ctx_output_map_open(ctx, "types", n_types); + GRN_DB_EACH_BEGIN_BY_KEY(ctx, cursor, id) { + grn_obj *type; + + if (!grn_id_is_builtin_type(ctx, id)) { + continue; + } + + type = grn_ctx_at(ctx, id); + + command_schema_output_name(ctx, type); + + grn_ctx_output_map_open(ctx, "type", 5); + + grn_ctx_output_cstr(ctx, "id"); + command_schema_output_id(ctx, type); + + grn_ctx_output_cstr(ctx, "name"); + command_schema_output_name(ctx, type); + + grn_ctx_output_cstr(ctx, "size"); + grn_ctx_output_int64(ctx, grn_type_size(ctx, type)); + + grn_ctx_output_cstr(ctx, "can_be_key_type"); + grn_ctx_output_bool(ctx, grn_type_size(ctx, type) <= GRN_TABLE_MAX_KEY_SIZE); + + grn_ctx_output_cstr(ctx, "can_be_value_type"); + grn_ctx_output_bool(ctx, !(type->header.flags & GRN_OBJ_KEY_VAR_SIZE)); + + grn_ctx_output_map_close(ctx); + } GRN_DB_EACH_END(ctx, cursor); + grn_ctx_output_map_close(ctx); +} + +static void +command_schema_output_tokenizers(grn_ctx *ctx, grn_schema_data *data) +{ + grn_obj tokenizer_ids; + unsigned int i, n; + + GRN_RECORD_INIT(&tokenizer_ids, GRN_OBJ_VECTOR, GRN_ID_NIL); + GRN_DB_EACH_BEGIN_BY_KEY(ctx, cursor, id) { + void *name; + int name_size; + grn_obj *object; + + name_size = grn_table_cursor_get_key(ctx, cursor, &name); + if (grn_obj_name_is_column(ctx, name, name_size)) { + continue; + } + + if (data->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + object = grn_ctx_at(ctx, id); + if (object) { + if (grn_obj_is_tokenizer_proc(ctx, object)) { + GRN_RECORD_PUT(ctx, &tokenizer_ids, id); + } + } else { + /* XXX: this clause is executed when MeCab tokenizer is enabled in + database but the groonga isn't supported MeCab. + We should return error mesage about it and error exit status + but it's too difficult for this architecture. :< */ + GRN_PLUGIN_CLEAR_ERROR(ctx); + } + + if (data->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + } GRN_DB_EACH_END(ctx, cursor); + + grn_ctx_output_cstr(ctx, "tokenizers"); + + n = GRN_BULK_VSIZE(&tokenizer_ids) / sizeof(grn_id); + grn_ctx_output_map_open(ctx, "tokenizers", n); + for (i = 0; i < n; i++) { + grn_id tokenizer_id; + grn_obj *tokenizer; + + tokenizer_id = GRN_RECORD_VALUE_AT(&tokenizer_ids, i); + tokenizer = grn_ctx_at(ctx, tokenizer_id); + + command_schema_output_name(ctx, tokenizer); + + grn_ctx_output_map_open(ctx, "tokenizer", 2); + + grn_ctx_output_cstr(ctx, "id"); + command_schema_output_id(ctx, tokenizer); + + grn_ctx_output_cstr(ctx, "name"); + command_schema_output_name(ctx, tokenizer); + + grn_ctx_output_map_close(ctx); + } + grn_ctx_output_map_close(ctx); + + GRN_OBJ_FIN(ctx, &tokenizer_ids); +} + +static void +command_schema_output_normalizers(grn_ctx *ctx, grn_schema_data *data) +{ + grn_obj normalizer_ids; + unsigned int i, n; + + GRN_RECORD_INIT(&normalizer_ids, GRN_OBJ_VECTOR, GRN_ID_NIL); + GRN_DB_EACH_BEGIN_BY_KEY(ctx, cursor, id) { + void *name; + int name_size; + grn_obj *object; + + name_size = grn_table_cursor_get_key(ctx, cursor, &name); + if (grn_obj_name_is_column(ctx, name, name_size)) { + continue; + } + + if (data->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + object = grn_ctx_at(ctx, id); + if (object) { + if (grn_obj_is_normalizer_proc(ctx, object)) { + GRN_RECORD_PUT(ctx, &normalizer_ids, id); + } + } else { + /* XXX: this clause is executed when MeCab normalizer is enabled in + database but the groonga isn't supported MeCab. + We should return error mesage about it and error exit status + but it's too difficult for this architecture. :< */ + GRN_PLUGIN_CLEAR_ERROR(ctx); + } + + if (data->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + } GRN_DB_EACH_END(ctx, cursor); + + grn_ctx_output_cstr(ctx, "normalizers"); + + n = GRN_BULK_VSIZE(&normalizer_ids) / sizeof(grn_id); + grn_ctx_output_map_open(ctx, "normalizers", n); + for (i = 0; i < n; i++) { + grn_id normalizer_id; + grn_obj *normalizer; + + normalizer_id = GRN_RECORD_VALUE_AT(&normalizer_ids, i); + normalizer = grn_ctx_at(ctx, normalizer_id); + + command_schema_output_name(ctx, normalizer); + + grn_ctx_output_map_open(ctx, "normalizer", 2); + + grn_ctx_output_cstr(ctx, "id"); + command_schema_output_id(ctx, normalizer); + + grn_ctx_output_cstr(ctx, "name"); + command_schema_output_name(ctx, normalizer); + + grn_ctx_output_map_close(ctx); + } + grn_ctx_output_map_close(ctx); + + GRN_OBJ_FIN(ctx, &normalizer_ids); +} + +static void +command_schema_output_token_filters(grn_ctx *ctx, grn_schema_data *data) +{ + grn_obj token_filter_ids; + unsigned int i, n; + + GRN_RECORD_INIT(&token_filter_ids, GRN_OBJ_VECTOR, GRN_ID_NIL); + GRN_DB_EACH_BEGIN_BY_KEY(ctx, cursor, id) { + void *name; + int name_size; + grn_obj *object; + + name_size = grn_table_cursor_get_key(ctx, cursor, &name); + if (grn_obj_name_is_column(ctx, name, name_size)) { + continue; + } + + if (data->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + object = grn_ctx_at(ctx, id); + if (object) { + if (grn_obj_is_token_filter_proc(ctx, object)) { + GRN_RECORD_PUT(ctx, &token_filter_ids, id); + } + } else { + /* XXX: this clause is executed when MeCab normalizer is enabled in + database but the groonga isn't supported MeCab. + We should return error mesage about it and error exit status + but it's too difficult for this architecture. :< */ + GRN_PLUGIN_CLEAR_ERROR(ctx); + } + + if (data->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + } GRN_DB_EACH_END(ctx, cursor); + + grn_ctx_output_cstr(ctx, "token_filters"); + + n = GRN_BULK_VSIZE(&token_filter_ids) / sizeof(grn_id); + grn_ctx_output_map_open(ctx, "token_filters", n); + for (i = 0; i < n; i++) { + grn_id token_filter_id; + grn_obj *token_filter; + + token_filter_id = GRN_RECORD_VALUE_AT(&token_filter_ids, i); + token_filter = grn_ctx_at(ctx, token_filter_id); + + command_schema_output_name(ctx, token_filter); + + grn_ctx_output_map_open(ctx, "token_filter", 2); + + grn_ctx_output_cstr(ctx, "id"); + command_schema_output_id(ctx, token_filter); + + grn_ctx_output_cstr(ctx, "name"); + command_schema_output_name(ctx, token_filter); + + grn_ctx_output_map_close(ctx); + } + grn_ctx_output_map_close(ctx); + + GRN_OBJ_FIN(ctx, &token_filter_ids); +} + +static const char * +command_schema_table_type_name(grn_ctx *ctx, grn_obj *table) +{ + const char *name = "unknown"; + + switch (table->header.type) { + case GRN_TABLE_NO_KEY : + name = "array"; + break; + case GRN_TABLE_HASH_KEY : + name = "hash table"; + break; + case GRN_TABLE_PAT_KEY : + name = "patricia trie"; + break; + case GRN_TABLE_DAT_KEY : + name = "double array trie"; + break; + default : + break; + } + + return name; +} + +static void +command_schema_table_output_key_type(grn_ctx *ctx, grn_obj *table) +{ + grn_obj *key_type = NULL; + + if (table->header.type != GRN_TABLE_NO_KEY && + table->header.domain != GRN_ID_NIL) { + key_type = grn_ctx_at(ctx, table->header.domain); + } + + command_schema_output_key_type(ctx, key_type); +} + +static void +command_schema_table_output_value_type(grn_ctx *ctx, grn_obj *table) +{ + grn_obj *value_type = NULL; + grn_id range = GRN_ID_NIL; + + if (table->header.type != GRN_TABLE_DAT_KEY) { + range = grn_obj_get_range(ctx, table); + } + if (range != GRN_ID_NIL) { + value_type = grn_ctx_at(ctx, range); + } + + command_schema_output_value_type(ctx, value_type); +} + +static void +command_schema_table_output_tokenizer(grn_ctx *ctx, grn_obj *table) +{ + grn_obj *tokenizer; + + tokenizer = grn_obj_get_info(ctx, table, GRN_INFO_DEFAULT_TOKENIZER, NULL); + if (!tokenizer) { + grn_ctx_output_null(ctx); + return; + } + + grn_ctx_output_map_open(ctx, "tokenizer", 2); + + grn_ctx_output_cstr(ctx, "id"); + command_schema_output_id(ctx, tokenizer); + + grn_ctx_output_cstr(ctx, "name"); + command_schema_output_name(ctx, tokenizer); + + grn_ctx_output_map_close(ctx); +} + +static void +command_schema_table_output_normalizer(grn_ctx *ctx, grn_obj *table) +{ + grn_obj *normalizer; + + normalizer = grn_obj_get_info(ctx, table, GRN_INFO_NORMALIZER, NULL); + if (!normalizer) { + grn_ctx_output_null(ctx); + return; + } + + grn_ctx_output_map_open(ctx, "normalizer", 2); + + grn_ctx_output_cstr(ctx, "id"); + command_schema_output_id(ctx, normalizer); + + grn_ctx_output_cstr(ctx, "name"); + command_schema_output_name(ctx, normalizer); + + grn_ctx_output_map_close(ctx); +} + +static void +command_schema_table_output_token_filters(grn_ctx *ctx, grn_obj *table) +{ + grn_obj token_filters; + int i, n; + + GRN_PTR_INIT(&token_filters, GRN_OBJ_VECTOR, GRN_DB_OBJECT); + if (table->header.type != GRN_TABLE_NO_KEY) { + grn_obj_get_info(ctx, table, GRN_INFO_TOKEN_FILTERS, &token_filters); + } + + n = GRN_BULK_VSIZE(&token_filters) / sizeof(grn_obj *); + grn_ctx_output_array_open(ctx, "token_filters", n); + for (i = 0; i < n; i++) { + grn_obj *token_filter; + + token_filter = GRN_PTR_VALUE_AT(&token_filters, i); + + grn_ctx_output_map_open(ctx, "token_filter", 2); + + grn_ctx_output_cstr(ctx, "id"); + command_schema_output_id(ctx, token_filter); + + grn_ctx_output_cstr(ctx, "name"); + command_schema_output_name(ctx, token_filter); + + grn_ctx_output_map_close(ctx); + } + grn_ctx_output_array_close(ctx); + + GRN_OBJ_FIN(ctx, &token_filters); +} + +static void +command_schema_table_command_collect_arguments(grn_ctx *ctx, + grn_obj *table, + grn_obj *arguments) +{ +#define ADD(name_, value_) \ + grn_vector_add_element(ctx, arguments, \ + name_, strlen(name_), \ + 0, GRN_DB_TEXT); \ + grn_vector_add_element(ctx, arguments, \ + value_, strlen(value_), \ + 0, GRN_DB_TEXT) + +#define ADD_OBJECT_NAME(name_, object_) do { \ + char object_name[GRN_TABLE_MAX_KEY_SIZE]; \ + unsigned int object_name_size; \ + object_name_size = grn_obj_name(ctx, object_, \ + object_name, \ + GRN_TABLE_MAX_KEY_SIZE); \ + object_name[object_name_size] = '\0'; \ + ADD(name_, object_name); \ + } while (GRN_FALSE) + + ADD_OBJECT_NAME("name", table); + + { + grn_obj flags; + grn_table_flags table_flags; + grn_table_flags ignored_flags = GRN_OBJ_KEY_NORMALIZE | GRN_OBJ_PERSISTENT; + GRN_TEXT_INIT(&flags, 0); + grn_table_get_info(ctx, table, &table_flags, NULL, NULL, NULL, NULL); + grn_dump_table_create_flags(ctx, + table_flags & ~ignored_flags, + &flags); + GRN_TEXT_PUTC(ctx, &flags, '\0'); + ADD("flags", GRN_TEXT_VALUE(&flags)); + GRN_OBJ_FIN(ctx, &flags); + } + + { + grn_obj *key_type = NULL; + + if (table->header.type != GRN_TABLE_NO_KEY && + table->header.domain != GRN_ID_NIL) { + key_type = grn_ctx_at(ctx, table->header.domain); + } + if (key_type) { + ADD_OBJECT_NAME("key_type", key_type); + } + } + + { + grn_obj *value_type = NULL; + grn_id range = GRN_ID_NIL; + + if (table->header.type != GRN_TABLE_DAT_KEY) { + range = grn_obj_get_range(ctx, table); + } + if (range != GRN_ID_NIL) { + value_type = grn_ctx_at(ctx, range); + } + if (value_type) { + ADD_OBJECT_NAME("value_type", value_type); + } + } + + { + grn_obj *tokenizer; + tokenizer = grn_obj_get_info(ctx, table, GRN_INFO_DEFAULT_TOKENIZER, NULL); + if (tokenizer) { + ADD_OBJECT_NAME("default_tokenizer", tokenizer); + } + } + + { + grn_obj *normalizer; + normalizer = grn_obj_get_info(ctx, table, GRN_INFO_NORMALIZER, NULL); + if (!normalizer && (table->header.flags & GRN_OBJ_KEY_NORMALIZE)) { + normalizer = grn_ctx_get(ctx, "NormalizerAuto", -1); + } + if (normalizer) { + ADD_OBJECT_NAME("normalizer", normalizer); + } + } + + if (table->header.type != GRN_TABLE_NO_KEY) { + grn_obj token_filters; + int n; + + GRN_PTR_INIT(&token_filters, GRN_OBJ_VECTOR, GRN_DB_OBJECT); + grn_obj_get_info(ctx, table, GRN_INFO_TOKEN_FILTERS, &token_filters); + n = GRN_BULK_VSIZE(&token_filters) / sizeof(grn_obj *); + if (n > 0) { + grn_obj token_filter_names; + int i; + + GRN_TEXT_INIT(&token_filter_names, 0); + for (i = 0; i < n; i++) { + grn_obj *token_filter; + char name[GRN_TABLE_MAX_KEY_SIZE]; + int name_size; + + token_filter = GRN_PTR_VALUE_AT(&token_filters, i); + name_size = grn_obj_name(ctx, token_filter, + name, GRN_TABLE_MAX_KEY_SIZE); + if (i > 0) { + GRN_TEXT_PUTC(ctx, &token_filter_names, ','); + } + GRN_TEXT_PUT(ctx, &token_filter_names, name, name_size); + } + GRN_TEXT_PUTC(ctx, &token_filter_names, '\0'); + ADD("token_filters", GRN_TEXT_VALUE(&token_filter_names)); + GRN_OBJ_FIN(ctx, &token_filter_names); + } + GRN_OBJ_FIN(ctx, &token_filters); + } + +#undef ADD_OBJECT_NAME +#undef ADD +} + +static void +command_schema_table_output_command(grn_ctx *ctx, grn_obj *table) +{ + grn_obj arguments; + + GRN_TEXT_INIT(&arguments, GRN_OBJ_VECTOR); + command_schema_table_command_collect_arguments(ctx, table, &arguments); + + command_schema_output_command(ctx, "table_create", &arguments); + + GRN_OBJ_FIN(ctx, &arguments); +} + +static void +command_schema_column_output_type(grn_ctx *ctx, grn_obj *column) +{ + switch (column->header.type) { + case GRN_COLUMN_FIX_SIZE : + case GRN_COLUMN_VAR_SIZE : + switch (column->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) { + case GRN_OBJ_COLUMN_SCALAR : + grn_ctx_output_cstr(ctx, "scalar"); + break; + case GRN_OBJ_COLUMN_VECTOR : + grn_ctx_output_cstr(ctx, "vector"); + break; + } + break; + case GRN_COLUMN_INDEX : + grn_ctx_output_cstr(ctx, "index"); + break; + } +} + +static void +command_schema_column_output_value_type(grn_ctx *ctx, grn_obj *column) +{ + grn_obj *value_type; + value_type = grn_ctx_at(ctx, grn_obj_get_range(ctx, column)); + command_schema_output_value_type(ctx, value_type); +} + +static void +command_schema_column_output_compress(grn_ctx *ctx, grn_obj *column) +{ + const char *compress = NULL; + + if (column->header.type != GRN_COLUMN_INDEX) { + switch (column->header.flags & GRN_OBJ_COMPRESS_MASK) { + case GRN_OBJ_COMPRESS_ZLIB : + compress = "zlib"; + break; + case GRN_OBJ_COMPRESS_LZ4 : + compress = "lz4"; + break; + case GRN_OBJ_COMPRESS_ZSTD : + compress = "zstd"; + break; + default : + break; + } + } + + if (compress) { + grn_ctx_output_cstr(ctx, compress); + } else { + grn_ctx_output_null(ctx); + } +} + +static void +command_schema_column_output_sources(grn_ctx *ctx, grn_obj *column) +{ + grn_obj *source_table; + grn_obj source_ids; + unsigned int i, n_ids; + + source_table = grn_ctx_at(ctx, grn_obj_get_range(ctx, column)); + + GRN_RECORD_INIT(&source_ids, GRN_OBJ_VECTOR, GRN_ID_NIL); + + if (column->header.type == GRN_COLUMN_INDEX) { + grn_obj_get_info(ctx, column, GRN_INFO_SOURCE, &source_ids); + } + + n_ids = GRN_BULK_VSIZE(&source_ids) / sizeof(grn_id); + grn_ctx_output_array_open(ctx, "sources", n_ids); + for (i = 0; i < n_ids; i++) { + grn_id source_id; + grn_obj *source; + + source_id = GRN_RECORD_VALUE_AT(&source_ids, i); + source = grn_ctx_at(ctx, source_id); + + grn_ctx_output_map_open(ctx, "source", 4); + + grn_ctx_output_cstr(ctx, "id"); + if (grn_obj_is_table(ctx, source)) { + command_schema_output_id(ctx, NULL); + } else { + command_schema_output_id(ctx, source); + } + + grn_ctx_output_cstr(ctx, "name"); + if (grn_obj_is_table(ctx, source)) { + grn_ctx_output_cstr(ctx, "_key"); + } else { + command_schema_output_column_name(ctx, source); + } + + grn_ctx_output_cstr(ctx, "table"); + command_schema_output_name(ctx, source_table); + + grn_ctx_output_cstr(ctx, "full_name"); + if (grn_obj_is_table(ctx, source)) { + char name[GRN_TABLE_MAX_KEY_SIZE]; + unsigned int name_size; + name_size = grn_obj_name(ctx, source, name, GRN_TABLE_MAX_KEY_SIZE); + name[name_size] = '\0'; + grn_strcat(name, GRN_TABLE_MAX_KEY_SIZE, "._key"); + grn_ctx_output_cstr(ctx, name); + } else { + command_schema_output_name(ctx, source); + } + + grn_ctx_output_map_close(ctx); + } + grn_ctx_output_array_close(ctx); + + GRN_OBJ_FIN(ctx, &source_ids); +} + +static void +command_schema_output_indexes(grn_ctx *ctx, grn_obj *object) +{ + uint32_t i; + grn_index_datum *index_data = NULL; + uint32_t n_index_data = 0; + + n_index_data = grn_column_get_all_index_data(ctx, object, NULL, 0); + if (n_index_data > 0) { + index_data = GRN_PLUGIN_MALLOC(ctx, + sizeof(grn_index_datum) * n_index_data); + if (!index_data) { + GRN_PLUGIN_ERROR(ctx, GRN_NO_MEMORY_AVAILABLE, + "[schema] failed to allocate memory for indexes"); + return; + } + grn_column_get_all_index_data(ctx, object, index_data, n_index_data); + } + + grn_ctx_output_array_open(ctx, "indexes", n_index_data); + for (i = 0; i < n_index_data; i++) { + grn_obj *lexicon; + + grn_ctx_output_map_open(ctx, "index", 5); + + grn_ctx_output_cstr(ctx, "id"); + command_schema_output_id(ctx, index_data[i].index); + + grn_ctx_output_cstr(ctx, "full_name"); + command_schema_output_name(ctx, index_data[i].index); + + grn_ctx_output_cstr(ctx, "table"); + lexicon = grn_ctx_at(ctx, index_data[i].index->header.domain); + command_schema_output_name(ctx, lexicon); + + grn_ctx_output_cstr(ctx, "name"); + command_schema_output_column_name(ctx, index_data[i].index); + + grn_ctx_output_cstr(ctx, "section"); + grn_ctx_output_uint64(ctx, index_data[i].section); + + grn_ctx_output_map_close(ctx); + } + grn_ctx_output_array_close(ctx); + + if (index_data) { + GRN_PLUGIN_FREE(ctx, index_data); + } +} + +static void +command_schema_column_command_collect_arguments(grn_ctx *ctx, + grn_obj *table, + grn_obj *column, + grn_obj *arguments) +{ +#define ADD(name_, value_) \ + grn_vector_add_element(ctx, arguments, \ + name_, strlen(name_), \ + 0, GRN_DB_TEXT); \ + grn_vector_add_element(ctx, arguments, \ + value_, strlen(value_), \ + 0, GRN_DB_TEXT) + +#define ADD_OBJECT_NAME(name_, object_) do { \ + char object_name[GRN_TABLE_MAX_KEY_SIZE]; \ + unsigned int object_name_size; \ + object_name_size = grn_obj_name(ctx, object_, \ + object_name, \ + GRN_TABLE_MAX_KEY_SIZE); \ + object_name[object_name_size] = '\0'; \ + ADD(name_, object_name); \ + } while (GRN_FALSE) + + ADD_OBJECT_NAME("table", table); + { + char column_name[GRN_TABLE_MAX_KEY_SIZE]; + unsigned int column_name_size; + column_name_size = grn_column_name(ctx, column, + column_name, GRN_TABLE_MAX_KEY_SIZE); + column_name[column_name_size] = '\0'; + ADD("name", column_name); + } + + { + grn_obj flags; + grn_column_flags column_flags; + + GRN_TEXT_INIT(&flags, 0); + column_flags = grn_column_get_flags(ctx, column); + grn_dump_column_create_flags(ctx, + column_flags & ~GRN_OBJ_PERSISTENT, + &flags); + GRN_TEXT_PUTC(ctx, &flags, '\0'); + ADD("flags", GRN_TEXT_VALUE(&flags)); + GRN_OBJ_FIN(ctx, &flags); + } + + { + grn_obj *value_type; + + value_type = grn_ctx_at(ctx, grn_obj_get_range(ctx, column)); + ADD_OBJECT_NAME("type", value_type); + } + + if (column->header.type == GRN_COLUMN_INDEX) { + grn_obj source_ids; + unsigned int n_ids; + + GRN_RECORD_INIT(&source_ids, GRN_OBJ_VECTOR, GRN_ID_NIL); + grn_obj_get_info(ctx, column, GRN_INFO_SOURCE, &source_ids); + + n_ids = GRN_BULK_VSIZE(&source_ids) / sizeof(grn_id); + if (n_ids > 0) { + grn_obj sources; + unsigned int i; + + GRN_TEXT_INIT(&sources, 0); + for (i = 0; i < n_ids; i++) { + grn_id source_id; + grn_obj *source; + char name[GRN_TABLE_MAX_KEY_SIZE]; + unsigned int name_size; + + source_id = GRN_RECORD_VALUE_AT(&source_ids, i); + source = grn_ctx_at(ctx, source_id); + + if (grn_obj_is_table(ctx, source)) { + grn_strcpy(name, GRN_TABLE_MAX_KEY_SIZE, "_key"); + name_size = strlen(name); + } else { + name_size = grn_column_name(ctx, source, name, GRN_TABLE_MAX_KEY_SIZE); + } + if (i > 0) { + GRN_TEXT_PUTC(ctx, &sources, ','); + } + GRN_TEXT_PUT(ctx, &sources, name, name_size); + } + GRN_TEXT_PUTC(ctx, &sources, '\0'); + ADD("source", GRN_TEXT_VALUE(&sources)); + GRN_OBJ_FIN(ctx, &sources); + } + GRN_OBJ_FIN(ctx, &source_ids); + } + +#undef ADD_OBJECT_NAME +#undef ADD +} + +static void +command_schema_column_output_command(grn_ctx *ctx, + grn_obj *table, + grn_obj *column) +{ + grn_obj arguments; + + GRN_TEXT_INIT(&arguments, GRN_OBJ_VECTOR); + command_schema_column_command_collect_arguments(ctx, table, column, &arguments); + + command_schema_output_command(ctx, "column_create", &arguments); + + GRN_OBJ_FIN(ctx, &arguments); +} + +static void +command_schema_column_output(grn_ctx *ctx, grn_obj *table, grn_obj *column) +{ + if (!column) { + return; + } + + command_schema_output_column_name(ctx, column); + + grn_ctx_output_map_open(ctx, "column", 13); + + grn_ctx_output_cstr(ctx, "id"); + command_schema_output_id(ctx, column); + + grn_ctx_output_cstr(ctx, "name"); + command_schema_output_column_name(ctx, column); + + grn_ctx_output_cstr(ctx, "table"); + command_schema_output_name(ctx, table); + + grn_ctx_output_cstr(ctx, "full_name"); + command_schema_output_name(ctx, column); + + grn_ctx_output_cstr(ctx, "type"); + command_schema_column_output_type(ctx, column); + + grn_ctx_output_cstr(ctx, "value_type"); + command_schema_column_output_value_type(ctx, column); + + grn_ctx_output_cstr(ctx, "compress"); + command_schema_column_output_compress(ctx, column); + + grn_ctx_output_cstr(ctx, "section"); + grn_ctx_output_bool(ctx, (column->header.flags & GRN_OBJ_WITH_SECTION) != 0); + + grn_ctx_output_cstr(ctx, "weight"); + grn_ctx_output_bool(ctx, (column->header.flags & GRN_OBJ_WITH_WEIGHT) != 0); + + grn_ctx_output_cstr(ctx, "position"); + grn_ctx_output_bool(ctx, (column->header.flags & GRN_OBJ_WITH_POSITION) != 0); + + grn_ctx_output_cstr(ctx, "sources"); + command_schema_column_output_sources(ctx, column); + + grn_ctx_output_cstr(ctx, "indexes"); + command_schema_output_indexes(ctx, column); + + grn_ctx_output_cstr(ctx, "command"); + command_schema_column_output_command(ctx, table, column); + + grn_ctx_output_map_close(ctx); +} + +static void +command_schema_table_output_columns(grn_ctx *ctx, + grn_obj *table, + grn_schema_data *data) +{ + grn_hash *columns; + + columns = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, + GRN_OBJ_TABLE_HASH_KEY | GRN_HASH_TINY); + if (!columns) { + grn_ctx_output_map_open(ctx, "columns", 0); + grn_ctx_output_map_close(ctx); + return; + } + + grn_table_columns(ctx, table, "", 0, (grn_obj *)columns); + grn_ctx_output_map_open(ctx, "columns", grn_hash_size(ctx, columns)); + { + grn_id *key; + GRN_HASH_EACH(ctx, columns, id, &key, NULL, NULL, { + grn_obj *column; + + if (data->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + column = grn_ctx_at(ctx, *key); + command_schema_column_output(ctx, table, column); + + if (data->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + }); + } + grn_ctx_output_map_close(ctx); + grn_hash_close(ctx, columns); +} + +static void +command_schema_output_table(grn_ctx *ctx, + grn_schema_data *data, + grn_obj *table) +{ + command_schema_output_name(ctx, table); + + grn_ctx_output_map_open(ctx, "table", 11); + + grn_ctx_output_cstr(ctx, "id"); + command_schema_output_id(ctx, table); + + grn_ctx_output_cstr(ctx, "name"); + command_schema_output_name(ctx, table); + + grn_ctx_output_cstr(ctx, "type"); + grn_ctx_output_cstr(ctx, command_schema_table_type_name(ctx, table)); + + grn_ctx_output_cstr(ctx, "key_type"); + command_schema_table_output_key_type(ctx, table); + + grn_ctx_output_cstr(ctx, "value_type"); + command_schema_table_output_value_type(ctx, table); + + grn_ctx_output_cstr(ctx, "tokenizer"); + command_schema_table_output_tokenizer(ctx, table); + + grn_ctx_output_cstr(ctx, "normalizer"); + command_schema_table_output_normalizer(ctx, table); + + grn_ctx_output_cstr(ctx, "token_filters"); + command_schema_table_output_token_filters(ctx, table); + + grn_ctx_output_cstr(ctx, "indexes"); + command_schema_output_indexes(ctx, table); + + grn_ctx_output_cstr(ctx, "command"); + command_schema_table_output_command(ctx, table); + + grn_ctx_output_cstr(ctx, "columns"); + command_schema_table_output_columns(ctx, table, data); + + grn_ctx_output_map_close(ctx); +} + +static void +command_schema_output_tables(grn_ctx *ctx, grn_schema_data *data) +{ + grn_obj table_ids; + unsigned int i, n; + + GRN_RECORD_INIT(&table_ids, GRN_OBJ_VECTOR, GRN_ID_NIL); + GRN_DB_EACH_BEGIN_BY_KEY(ctx, cursor, id) { + void *name; + int name_size; + grn_obj *object; + + if (grn_id_is_builtin(ctx, id)) { + continue; + } + + name_size = grn_table_cursor_get_key(ctx, cursor, &name); + if (grn_obj_name_is_column(ctx, name, name_size)) { + continue; + } + + if (data->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + object = grn_ctx_at(ctx, id); + if (!object) { + /* XXX: this clause is executed when MeCab tokenizer is enabled in + database but the groonga isn't supported MeCab. + We should return error mesage about it and error exit status + but it's too difficult for this architecture. :< */ + GRN_PLUGIN_CLEAR_ERROR(ctx); + goto next_loop; + } + + if (grn_obj_is_table(ctx, object)) { + GRN_RECORD_PUT(ctx, &table_ids, id); + } + + next_loop : + if (data->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + } GRN_TABLE_EACH_END(ctx, cursor); + + n = GRN_BULK_VSIZE(&table_ids) / sizeof(grn_id); + + grn_ctx_output_cstr(ctx, "tables"); + grn_ctx_output_map_open(ctx, "tables", n); + for (i = 0; i < n; i++) { + grn_id table_id; + grn_obj *table; + + if (data->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + table_id = GRN_RECORD_VALUE_AT(&table_ids, i); + table = grn_ctx_at(ctx, table_id); + + command_schema_output_table(ctx, data, table); + + if (data->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + } + grn_ctx_output_map_close(ctx); + + GRN_OBJ_FIN(ctx, &table_ids); +} + +static grn_obj * +command_schema(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_schema_data data; + + data.is_close_opened_object_mode = (grn_thread_get_limit() == 1); + + grn_ctx_output_map_open(ctx, "schema", 6); + command_schema_output_plugins(ctx); + command_schema_output_types(ctx); + command_schema_output_tokenizers(ctx, &data); + command_schema_output_normalizers(ctx, &data); + command_schema_output_token_filters(ctx, &data); + command_schema_output_tables(ctx, &data); + grn_ctx_output_map_close(ctx); + + return NULL; +} + +void +grn_proc_init_schema(grn_ctx *ctx) +{ + grn_plugin_command_create(ctx, + "schema", -1, + command_schema, + 0, + NULL); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_select.c b/storage/mroonga/vendor/groonga/lib/proc/proc_select.c new file mode 100644 index 00000000..a665b1cc --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_select.c @@ -0,0 +1,3809 @@ +/* -*- 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_proc.h" +#include "../grn_raw_string.h" +#include "../grn_expr.h" +#include "../grn_str.h" +#include "../grn_output.h" +#include "../grn_util.h" +#include "../grn_cache.h" +#include "../grn_ii.h" + +#include "../grn_ts.h" + +#include <groonga/plugin.h> + +#define GRN_SELECT_INTERNAL_VAR_MATCH_COLUMNS "$match_columns" + +#define DEFAULT_DRILLDOWN_LIMIT 10 +#define DEFAULT_DRILLDOWN_OUTPUT_COLUMNS "_key, _nsubrecs" + +typedef enum { + GRN_COLUMN_STAGE_INITIAL, + GRN_COLUMN_STAGE_FILTERED, + GRN_COLUMN_STAGE_OUTPUT +} grn_column_stage; + +typedef struct { + grn_raw_string label; + grn_column_stage stage; + grn_obj *type; + grn_obj_flags flags; + grn_raw_string value; + struct { + grn_raw_string sort_keys; + grn_raw_string group_keys; + } window; +} grn_column_data; + +typedef struct { + grn_hash *initial; + grn_hash *filtered; + grn_hash *output; +} grn_columns; + +typedef struct { + grn_raw_string match_columns; + grn_raw_string query; + grn_raw_string query_expander; + grn_raw_string query_flags; + grn_raw_string filter; + struct { + grn_obj *match_columns; + grn_obj *expression; + } condition; + grn_obj *filtered; +} grn_filter_data; + +typedef struct { + grn_raw_string label; + grn_filter_data filter; + grn_raw_string sort_keys; + grn_raw_string output_columns; + int offset; + int limit; + grn_obj *table; +} grn_slice_data; + +typedef struct { + grn_raw_string label; + grn_raw_string keys; + grn_table_sort_key *parsed_keys; + int n_parsed_keys; + grn_raw_string sort_keys; + grn_raw_string output_columns; + int offset; + int limit; + grn_table_group_flags calc_types; + grn_raw_string calc_target_name; + grn_raw_string filter; + grn_raw_string table_name; + grn_columns columns; + grn_table_group_result result; + grn_obj *filtered_result; +} grn_drilldown_data; + +typedef struct _grn_select_output_formatter grn_select_output_formatter; + +typedef struct { + /* inputs */ + grn_raw_string table; + grn_filter_data filter; + grn_raw_string scorer; + grn_raw_string sort_keys; + grn_raw_string output_columns; + int offset; + int limit; + grn_hash *slices; + grn_drilldown_data drilldown; + grn_hash *drilldowns; + grn_raw_string cache; + grn_raw_string match_escalation_threshold; + grn_raw_string adjuster; + grn_columns columns; + + /* for processing */ + struct { + grn_obj *target; + grn_obj *initial; + grn_obj *result; + grn_obj *sorted; + grn_obj *output; + } tables; + uint16_t cacheable; + uint16_t taintable; + struct { + int n_elements; + grn_select_output_formatter *formatter; + } output; +} grn_select_data; + +typedef void grn_select_output_slices_label_func(grn_ctx *ctx, + grn_select_data *data); +typedef void grn_select_output_slices_open_func(grn_ctx *ctx, + grn_select_data *data, + unsigned int n_result_sets); +typedef void grn_select_output_slices_close_func(grn_ctx *ctx, + grn_select_data *data); +typedef void grn_select_output_slice_label_func(grn_ctx *ctx, + grn_select_data *data, + grn_slice_data *slice); +typedef void grn_select_output_drilldowns_label_func(grn_ctx *ctx, + grn_select_data *data); +typedef void grn_select_output_drilldowns_open_func(grn_ctx *ctx, + grn_select_data *data, + unsigned int n_result_sets); +typedef void grn_select_output_drilldowns_close_func(grn_ctx *ctx, + grn_select_data *data); +typedef void grn_select_output_drilldown_label_func(grn_ctx *ctx, + grn_select_data *data, + grn_drilldown_data *drilldown); + +struct _grn_select_output_formatter { + grn_select_output_slices_label_func *slices_label; + grn_select_output_slices_open_func *slices_open; + grn_select_output_slices_close_func *slices_close; + grn_select_output_slice_label_func *slice_label; + grn_select_output_drilldowns_label_func *drilldowns_label; + grn_select_output_drilldowns_open_func *drilldowns_open; + grn_select_output_drilldowns_close_func *drilldowns_close; + grn_select_output_drilldown_label_func *drilldown_label; +}; + +grn_rc +grn_proc_syntax_expand_query(grn_ctx *ctx, + const char *query, + unsigned int query_len, + grn_expr_flags flags, + const char *query_expander_name, + unsigned int query_expander_name_len, + const char *term_column_name, + unsigned int term_column_name_len, + const char *expanded_term_column_name, + unsigned int expanded_term_column_name_len, + grn_obj *expanded_query, + const char *error_message_tag) +{ + grn_obj *query_expander; + + query_expander = grn_ctx_get(ctx, + query_expander_name, + query_expander_name_len); + if (!query_expander) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "%s nonexistent query expander: <%.*s>", + error_message_tag, + (int)query_expander_name_len, + query_expander_name); + return ctx->rc; + } + + if (expanded_term_column_name_len == 0) { + return grn_expr_syntax_expand_query(ctx, query, query_len, flags, + query_expander, expanded_query); + } + + if (!grn_obj_is_table(ctx, query_expander)) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, query_expander); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "%s query expander with expanded term column " + "must be table: <%.*s>", + error_message_tag, + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + return ctx->rc; + } + + { + grn_obj *term_column = NULL; + grn_obj *expanded_term_column; + + expanded_term_column = grn_obj_column(ctx, + query_expander, + expanded_term_column_name, + expanded_term_column_name_len); + if (!expanded_term_column) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, query_expander); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "%s nonexistent expanded term column: <%.*s>: " + "query expander: <%.*s>", + error_message_tag, + (int)expanded_term_column_name_len, + expanded_term_column_name, + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + return ctx->rc; + } + + if (term_column_name_len > 0) { + term_column = grn_obj_column(ctx, + query_expander, + term_column_name, + term_column_name_len); + if (!term_column) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, query_expander); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "%s nonexistent term column: <%.*s>: " + "query expander: <%.*s>", + error_message_tag, + (int)term_column_name_len, + term_column_name, + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + if (grn_obj_is_accessor(ctx, expanded_term_column)) { + grn_obj_unlink(ctx, expanded_term_column); + } + return ctx->rc; + } + } + + grn_expr_syntax_expand_query_by_table(ctx, + query, query_len, + flags, + term_column, + expanded_term_column, + expanded_query); + if (grn_obj_is_accessor(ctx, term_column)) { + grn_obj_unlink(ctx, term_column); + } + if (grn_obj_is_accessor(ctx, expanded_term_column)) { + grn_obj_unlink(ctx, expanded_term_column); + } + return ctx->rc; + } +} + +static grn_table_group_flags +grn_parse_table_group_calc_types(grn_ctx *ctx, + const char *calc_types, + unsigned int calc_types_len) +{ + grn_table_group_flags flags = 0; + const char *calc_types_end = calc_types + calc_types_len; + + while (calc_types < calc_types_end) { + if (*calc_types == ',' || *calc_types == ' ') { + calc_types += 1; + continue; + } + +#define CHECK_TABLE_GROUP_CALC_TYPE(name)\ + if (((unsigned long) (calc_types_end - calc_types) >= (unsigned long) (sizeof(#name) - 1)) && \ + (!memcmp(calc_types, #name, sizeof(#name) - 1))) {\ + flags |= GRN_TABLE_GROUP_CALC_ ## name;\ + calc_types += sizeof(#name) - 1;\ + continue;\ + } + + CHECK_TABLE_GROUP_CALC_TYPE(COUNT); + CHECK_TABLE_GROUP_CALC_TYPE(MAX); + CHECK_TABLE_GROUP_CALC_TYPE(MIN); + CHECK_TABLE_GROUP_CALC_TYPE(SUM); + CHECK_TABLE_GROUP_CALC_TYPE(AVG); + +#define GRN_TABLE_GROUP_CALC_NONE 0 + CHECK_TABLE_GROUP_CALC_TYPE(NONE); +#undef GRN_TABLE_GROUP_CALC_NONE + + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "invalid table group calc type: <%.*s>", + (int)(calc_types_end - calc_types), + calc_types); + return 0; +#undef CHECK_TABLE_GROUP_CALC_TYPE + } + + return flags; +} + +static const char * +grn_column_stage_name(grn_column_stage stage) +{ + switch (stage) { + case GRN_COLUMN_STAGE_INITIAL : + return "initial"; + case GRN_COLUMN_STAGE_FILTERED : + return "filtered"; + case GRN_COLUMN_STAGE_OUTPUT : + return "output"; + default : + return "unknown"; + } +} + +static grn_bool +grn_column_data_init(grn_ctx *ctx, + const char *label, + size_t label_len, + grn_column_stage stage, + grn_hash **columns) +{ + void *column_raw; + grn_column_data *column; + int added; + + if (!*columns) { + *columns = grn_hash_create(ctx, + NULL, + GRN_TABLE_MAX_KEY_SIZE, + sizeof(grn_column_data), + GRN_OBJ_TABLE_HASH_KEY | + GRN_OBJ_KEY_VAR_SIZE | + GRN_HASH_TINY); + } + if (!*columns) { + return GRN_FALSE; + } + + grn_hash_add(ctx, + *columns, + label, + label_len, + &column_raw, + &added); + if (!added) { + return GRN_TRUE; + } + + column = column_raw; + column->label.value = label; + column->label.length = label_len; + column->stage = stage; + column->type = grn_ctx_at(ctx, GRN_DB_TEXT); + column->flags = GRN_OBJ_COLUMN_SCALAR; + GRN_RAW_STRING_INIT(column->value); + GRN_RAW_STRING_INIT(column->window.sort_keys); + GRN_RAW_STRING_INIT(column->window.group_keys); + + return GRN_TRUE; +} + +static grn_bool +grn_column_data_fill(grn_ctx *ctx, + grn_column_data *column, + grn_obj *type_raw, + grn_obj *flags, + grn_obj *value, + grn_obj *window_sort_keys, + grn_obj *window_group_keys) +{ + if (type_raw && GRN_TEXT_LEN(type_raw) > 0) { + grn_obj *type; + + type = grn_ctx_get(ctx, GRN_TEXT_VALUE(type_raw), GRN_TEXT_LEN(type_raw)); + if (!type) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][columns][%s][%.*s] unknown type: <%.*s>", + grn_column_stage_name(column->stage), + (int)(column->label.length), + column->label.value, + (int)(GRN_TEXT_LEN(type_raw)), + GRN_TEXT_VALUE(type_raw)); + return GRN_FALSE; + } + if (!(grn_obj_is_type(ctx, type) || grn_obj_is_table(ctx, type))) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, type); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][columns][%s][%.*s] invalid type: %.*s", + grn_column_stage_name(column->stage), + (int)(column->label.length), + column->label.value, + (int)(GRN_TEXT_LEN(&inspected)), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + grn_obj_unlink(ctx, type); + return GRN_FALSE; + } + column->type = type; + } + + if (flags && GRN_TEXT_LEN(flags) > 0) { + char error_message_tag[GRN_TABLE_MAX_KEY_SIZE]; + + grn_snprintf(error_message_tag, + GRN_TABLE_MAX_KEY_SIZE, + GRN_TABLE_MAX_KEY_SIZE, + "[select][columns][%s][%.*s]", + grn_column_stage_name(column->stage), + (int)(column->label.length), + column->label.value); + column->flags = + grn_proc_column_parse_flags(ctx, + error_message_tag, + GRN_TEXT_VALUE(flags), + GRN_TEXT_VALUE(flags) + GRN_TEXT_LEN(flags)); + if (ctx->rc != GRN_SUCCESS) { + return GRN_FALSE; + } + } + + GRN_RAW_STRING_FILL(column->value, value); + GRN_RAW_STRING_FILL(column->window.sort_keys, window_sort_keys); + GRN_RAW_STRING_FILL(column->window.group_keys, window_group_keys); + + return GRN_TRUE; +} + +static grn_bool +grn_column_data_collect(grn_ctx *ctx, + grn_user_data *user_data, + grn_hash *columns, + const char *prefix_label, + size_t prefix_label_len) +{ + grn_hash_cursor *cursor = NULL; + + cursor = grn_hash_cursor_open(ctx, columns, + NULL, 0, NULL, 0, 0, -1, 0); + if (!cursor) { + return GRN_FALSE; + } + + while (grn_hash_cursor_next(ctx, cursor)) { + grn_column_data *column; + char key_name[GRN_TABLE_MAX_KEY_SIZE]; + grn_obj *type = NULL; + grn_obj *flags = NULL; + grn_obj *value = NULL; + struct { + grn_obj *sort_keys; + grn_obj *group_keys; + } window; + + window.sort_keys = NULL; + window.group_keys = NULL; + + grn_hash_cursor_get_value(ctx, cursor, (void **)&column); + +#define GET_VAR_RAW(parameter_key, name) \ + if (!name) { \ + grn_snprintf(key_name, \ + GRN_TABLE_MAX_KEY_SIZE, \ + GRN_TABLE_MAX_KEY_SIZE, \ + "%.*s%s[%.*s]." # name, \ + (int)prefix_label_len, \ + prefix_label, \ + parameter_key, \ + (int)(column->label.length), \ + column->label.value); \ + name = grn_plugin_proc_get_var(ctx, user_data, key_name, -1); \ + } + +#define GET_VAR(name) do { \ + GET_VAR_RAW("columns", name); \ + /* For backward compatibility */ \ + GET_VAR_RAW("column", name); \ + } while (GRN_FALSE) + + GET_VAR(type); + GET_VAR(flags); + GET_VAR(value); + GET_VAR(window.sort_keys); + GET_VAR(window.group_keys); + +#undef GET_VAR + +#undef GET_VAR_RAW + + grn_column_data_fill(ctx, column, + type, flags, value, + window.sort_keys, + window.group_keys); + } + grn_hash_cursor_close(ctx, cursor); + return GRN_TRUE; +} + +static void +grn_columns_init(grn_ctx *ctx, grn_columns *columns) +{ + columns->initial = NULL; + columns->filtered = NULL; + columns->output = NULL; +} + +static void +grn_columns_fin(grn_ctx *ctx, grn_columns *columns) +{ + if (columns->initial) { + grn_hash_close(ctx, columns->initial); + } + + if (columns->filtered) { + grn_hash_close(ctx, columns->filtered); + } + + if (columns->output) { + grn_hash_close(ctx, columns->output); + } +} + +static grn_bool +grn_columns_collect(grn_ctx *ctx, + grn_user_data *user_data, + grn_columns *columns, + const char *prefix, + const char *base_prefix, + size_t base_prefix_len) +{ + grn_obj *vars; + grn_table_cursor *cursor; + size_t prefix_len; + const char *suffix = "].stage"; + size_t suffix_len; + + vars = grn_plugin_proc_get_vars(ctx, user_data); + cursor = grn_table_cursor_open(ctx, vars, NULL, 0, NULL, 0, 0, -1, 0); + if (!cursor) { + return GRN_FALSE; + } + + prefix_len = strlen(prefix); + suffix_len = strlen(suffix); + while (grn_table_cursor_next(ctx, cursor)) { + void *key; + char *variable_name; + unsigned int variable_name_len; + char *column_name; + size_t column_name_len; + void *value_raw; + grn_obj *value; + grn_column_stage stage; + grn_hash **target_columns; + + variable_name_len = grn_table_cursor_get_key(ctx, cursor, &key); + variable_name = key; + if (variable_name_len < base_prefix_len + prefix_len + suffix_len + 1) { + continue; + } + + if (base_prefix_len > 0) { + if (memcmp(base_prefix, variable_name, base_prefix_len) != 0) { + continue; + } + } + + if (memcmp(prefix, variable_name + base_prefix_len, prefix_len) != 0) { + continue; + } + + if (memcmp(suffix, + variable_name + (variable_name_len - suffix_len), + suffix_len) != 0) { + continue; + } + + grn_table_cursor_get_value(ctx, cursor, &value_raw); + value = value_raw; + if (GRN_TEXT_EQUAL_CSTRING(value, "initial")) { + stage = GRN_COLUMN_STAGE_INITIAL; + target_columns = &(columns->initial); + } else if (GRN_TEXT_EQUAL_CSTRING(value, "filtered")) { + stage = GRN_COLUMN_STAGE_FILTERED; + target_columns = &(columns->filtered); + } else if (GRN_TEXT_EQUAL_CSTRING(value, "output")) { + stage = GRN_COLUMN_STAGE_OUTPUT; + target_columns = &(columns->output); + } else { + continue; + } + + column_name = variable_name + base_prefix_len + prefix_len; + column_name_len = + variable_name_len - base_prefix_len - prefix_len - suffix_len; + if (!grn_column_data_init(ctx, + column_name, + column_name_len, + stage, + target_columns)) { + grn_table_cursor_close(ctx, cursor); + return GRN_FALSE; + } + } + grn_table_cursor_close(ctx, cursor); + + return GRN_TRUE; +} + +static grn_bool +grn_columns_fill(grn_ctx *ctx, + grn_user_data *user_data, + grn_columns *columns, + const char *prefix, + size_t prefix_length) +{ + if (!grn_columns_collect(ctx, user_data, columns, + "columns[", prefix, prefix_length)) { + return GRN_FALSE; + } + + /* For backward compatibility */ + if (!grn_columns_collect(ctx, user_data, columns, + "column[", prefix, prefix_length)) { + return GRN_FALSE; + } + + if (columns->initial) { + if (!grn_column_data_collect(ctx, + user_data, + columns->initial, + prefix, + prefix_length)) { + return GRN_FALSE; + } + } + + if (columns->filtered) { + if (!grn_column_data_collect(ctx, + user_data, + columns->filtered, + prefix, + prefix_length)) { + return GRN_FALSE; + } + } + + if (columns->output) { + if (!grn_column_data_collect(ctx, + user_data, + columns->output, + prefix, + prefix_length)) { + return GRN_FALSE; + } + } + + return GRN_TRUE; +} + +static void +grn_filter_data_init(grn_ctx *ctx, grn_filter_data *data) +{ + GRN_RAW_STRING_INIT(data->match_columns); + GRN_RAW_STRING_INIT(data->query); + GRN_RAW_STRING_INIT(data->query_expander); + GRN_RAW_STRING_INIT(data->query_flags); + GRN_RAW_STRING_INIT(data->filter); + data->condition.match_columns = NULL; + data->condition.expression = NULL; + data->filtered = NULL; +} + +static void +grn_filter_data_fin(grn_ctx *ctx, grn_filter_data *data) +{ + if (data->filtered) { + grn_obj_unlink(ctx, data->filtered); + } + if (data->condition.expression) { + grn_obj_close(ctx, data->condition.expression); + } + if (data->condition.match_columns) { + grn_obj_close(ctx, data->condition.match_columns); + } +} + +static void +grn_filter_data_fill(grn_ctx *ctx, + grn_filter_data *data, + grn_obj *match_columns, + grn_obj *query, + grn_obj *query_expander, + grn_obj *query_flags, + grn_obj *filter) +{ + GRN_RAW_STRING_FILL(data->match_columns, match_columns); + GRN_RAW_STRING_FILL(data->query, query); + GRN_RAW_STRING_FILL(data->query_expander, query_expander); + GRN_RAW_STRING_FILL(data->query_flags, query_flags); + GRN_RAW_STRING_FILL(data->filter, filter); +} + +static grn_bool +grn_filter_data_execute(grn_ctx *ctx, + grn_filter_data *data, + grn_obj *table, + const char *tag) +{ + grn_obj *variable; + + if (data->query.length == 0 && data->filter.length == 0) { + return GRN_TRUE; + } + + GRN_EXPR_CREATE_FOR_QUERY(ctx, + table, + data->condition.expression, + variable); + if (!data->condition.expression) { + grn_rc rc = ctx->rc; + if (rc == GRN_SUCCESS) { + rc = GRN_NO_MEMORY_AVAILABLE; + } + GRN_PLUGIN_ERROR(ctx, + rc, + "%s[condition] " + "failed to create expression for condition: %s", + tag, + ctx->errbuf); + return GRN_FALSE; + } + + if (data->query.length > 0) { + if (data->match_columns.length > 0) { + GRN_EXPR_CREATE_FOR_QUERY(ctx, + table, + data->condition.match_columns, + variable); + if (!data->condition.match_columns) { + grn_rc rc = ctx->rc; + if (rc == GRN_SUCCESS) { + rc = GRN_NO_MEMORY_AVAILABLE; + } + GRN_PLUGIN_ERROR(ctx, + rc, + "%s[match_columns] " + "failed to create expression for match columns: " + "<%.*s>: %s", + tag, + (int)(data->match_columns.length), + data->match_columns.value, + ctx->errbuf); + return GRN_FALSE; + } + + grn_expr_parse(ctx, + data->condition.match_columns, + data->match_columns.value, + data->match_columns.length, + NULL, GRN_OP_MATCH, GRN_OP_AND, + GRN_EXPR_SYNTAX_SCRIPT); + if (ctx->rc != GRN_SUCCESS) { + return GRN_FALSE; + } + } + + { + grn_expr_flags flags; + grn_obj query_expander_buf; + const char *query = data->query.value; + unsigned int query_len = data->query.length; + + flags = GRN_EXPR_SYNTAX_QUERY; + if (data->query_flags.length) { + flags |= grn_proc_expr_query_flags_parse(ctx, + data->query_flags.value, + data->query_flags.length, + tag); + if (ctx->rc != GRN_SUCCESS) { + return GRN_FALSE; + } + } else { + flags |= GRN_EXPR_ALLOW_PRAGMA|GRN_EXPR_ALLOW_COLUMN; + } + + GRN_TEXT_INIT(&query_expander_buf, 0); + if (data->query_expander.length > 0) { + grn_rc rc; + rc = grn_proc_syntax_expand_query(ctx, + data->query.value, + data->query.length, + flags, + data->query_expander.value, + data->query_expander.length, + NULL, 0, + NULL, 0, + &query_expander_buf, + tag); + if (rc == GRN_SUCCESS) { + query = GRN_TEXT_VALUE(&query_expander_buf); + query_len = GRN_TEXT_LEN(&query_expander_buf); + } else { + GRN_OBJ_FIN(ctx, &query_expander_buf); + return GRN_FALSE; + } + } + + grn_expr_parse(ctx, + data->condition.expression, + query, + query_len, + data->condition.match_columns, + GRN_OP_MATCH, + GRN_OP_AND, + flags); + GRN_OBJ_FIN(ctx, &query_expander_buf); + + if (ctx->rc != GRN_SUCCESS) { + return GRN_FALSE; + } + } + } + + if (data->filter.length > 0) { + grn_expr_parse(ctx, + data->condition.expression, + data->filter.value, + data->filter.length, + data->condition.match_columns, + GRN_OP_MATCH, + GRN_OP_AND, + GRN_EXPR_SYNTAX_SCRIPT); + if (ctx->rc != GRN_SUCCESS) { + return GRN_FALSE; + } + + if (data->query.length > 0) { + grn_expr_append_op(ctx, data->condition.expression, GRN_OP_AND, 2); + } + + if (ctx->rc != GRN_SUCCESS) { + return GRN_FALSE; + } + } + + data->filtered = grn_table_select(ctx, + table, + data->condition.expression, + NULL, + GRN_OP_OR); + + return ctx->rc == GRN_SUCCESS; +} + +static void +grn_slice_data_init(grn_ctx *ctx, + grn_slice_data *slice, + const char *label, + size_t label_len) +{ + slice->label.value = label; + slice->label.length = label_len; + grn_filter_data_init(ctx, &(slice->filter)); + GRN_RAW_STRING_INIT(slice->sort_keys); + GRN_RAW_STRING_INIT(slice->output_columns); + slice->offset = 0; + slice->limit = GRN_SELECT_DEFAULT_LIMIT; + slice->table = NULL; +} + +static void +grn_slice_data_fin(grn_ctx *ctx, grn_slice_data *slice) +{ + grn_filter_data_fin(ctx, &(slice->filter)); +} + +static void +grn_slice_data_fill(grn_ctx *ctx, + grn_slice_data *slice, + grn_obj *match_columns, + grn_obj *query, + grn_obj *query_expander, + grn_obj *query_flags, + grn_obj *filter, + grn_obj *sort_keys, + grn_obj *output_columns, + grn_obj *offset, + grn_obj *limit) +{ + grn_filter_data_fill(ctx, + &(slice->filter), + match_columns, + query, + query_expander, + query_flags, + filter); + + GRN_RAW_STRING_FILL(slice->sort_keys, sort_keys); + + GRN_RAW_STRING_FILL(slice->output_columns, output_columns); + if (slice->output_columns.length == 0) { + slice->output_columns.value = GRN_SELECT_DEFAULT_OUTPUT_COLUMNS; + slice->output_columns.length = strlen(GRN_SELECT_DEFAULT_OUTPUT_COLUMNS); + } + + slice->offset = grn_proc_option_value_int32(ctx, offset, 0); + slice->limit = grn_proc_option_value_int32(ctx, + limit, + GRN_SELECT_DEFAULT_LIMIT); +} + +static void +grn_drilldown_data_init(grn_ctx *ctx, + grn_drilldown_data *drilldown, + const char *label, + size_t label_len) +{ + drilldown->label.value = label; + drilldown->label.length = label_len; + GRN_RAW_STRING_INIT(drilldown->keys); + drilldown->parsed_keys = NULL; + drilldown->n_parsed_keys = 0; + GRN_RAW_STRING_INIT(drilldown->sort_keys); + GRN_RAW_STRING_INIT(drilldown->output_columns); + drilldown->offset = 0; + drilldown->limit = DEFAULT_DRILLDOWN_LIMIT; + drilldown->calc_types = 0; + GRN_RAW_STRING_INIT(drilldown->calc_target_name); + GRN_RAW_STRING_INIT(drilldown->filter); + GRN_RAW_STRING_INIT(drilldown->table_name); + grn_columns_init(ctx, &(drilldown->columns)); + drilldown->result.table = NULL; + drilldown->filtered_result = NULL; +} + +static void +grn_drilldown_data_fin(grn_ctx *ctx, grn_drilldown_data *drilldown) +{ + grn_table_group_result *result; + + grn_columns_fin(ctx, &(drilldown->columns)); + + if (drilldown->filtered_result) { + grn_obj_close(ctx, drilldown->filtered_result); + } + + result = &(drilldown->result); + if (result->table) { + if (result->calc_target) { + grn_obj_unlink(ctx, result->calc_target); + } + if (result->table) { + grn_obj_close(ctx, result->table); + } + } +} + +static void +grn_drilldown_data_fill(grn_ctx *ctx, + grn_drilldown_data *drilldown, + grn_obj *keys, + grn_obj *sort_keys, + grn_obj *output_columns, + grn_obj *offset, + grn_obj *limit, + grn_obj *calc_types, + grn_obj *calc_target, + grn_obj *filter, + grn_obj *table) +{ + GRN_RAW_STRING_FILL(drilldown->keys, keys); + + GRN_RAW_STRING_FILL(drilldown->sort_keys, sort_keys); + + GRN_RAW_STRING_FILL(drilldown->output_columns, output_columns); + if (drilldown->output_columns.length == 0) { + drilldown->output_columns.value = DEFAULT_DRILLDOWN_OUTPUT_COLUMNS; + drilldown->output_columns.length = strlen(DEFAULT_DRILLDOWN_OUTPUT_COLUMNS); + } + + if (offset && GRN_TEXT_LEN(offset)) { + drilldown->offset = + grn_atoi(GRN_TEXT_VALUE(offset), GRN_BULK_CURR(offset), NULL); + } else { + drilldown->offset = 0; + } + + if (limit && GRN_TEXT_LEN(limit)) { + drilldown->limit = + grn_atoi(GRN_TEXT_VALUE(limit), GRN_BULK_CURR(limit), NULL); + } else { + drilldown->limit = DEFAULT_DRILLDOWN_LIMIT; + } + + if (calc_types && GRN_TEXT_LEN(calc_types)) { + drilldown->calc_types = + grn_parse_table_group_calc_types(ctx, + GRN_TEXT_VALUE(calc_types), + GRN_TEXT_LEN(calc_types)); + } else { + drilldown->calc_types = 0; + } + + GRN_RAW_STRING_FILL(drilldown->calc_target_name, calc_target); + + GRN_RAW_STRING_FILL(drilldown->filter, filter); + + GRN_RAW_STRING_FILL(drilldown->table_name, table); +} + +grn_expr_flags +grn_proc_expr_query_flags_parse(grn_ctx *ctx, + const char *query_flags, + size_t query_flags_size, + const char *error_message_tag) +{ + grn_expr_flags flags = 0; + const char *query_flags_end = query_flags + query_flags_size; + + while (query_flags < query_flags_end) { + if (*query_flags == '|' || *query_flags == ' ') { + query_flags += 1; + continue; + } + +#define CHECK_EXPR_FLAG(name) \ + if (((unsigned long) (query_flags_end - query_flags) >= (unsigned long) (sizeof(#name) - 1)) && \ + (memcmp(query_flags, #name, sizeof(#name) - 1) == 0) && \ + (((query_flags_end - query_flags) == (sizeof(#name) - 1)) || \ + (query_flags[sizeof(#name) - 1] == '|') || \ + (query_flags[sizeof(#name) - 1] == ' '))) { \ + flags |= GRN_EXPR_ ## name; \ + query_flags += sizeof(#name) - 1; \ + continue; \ + } + + CHECK_EXPR_FLAG(ALLOW_PRAGMA); + CHECK_EXPR_FLAG(ALLOW_COLUMN); + CHECK_EXPR_FLAG(ALLOW_UPDATE); + CHECK_EXPR_FLAG(ALLOW_LEADING_NOT); + CHECK_EXPR_FLAG(QUERY_NO_SYNTAX_ERROR); + +#define GRN_EXPR_NONE 0 + CHECK_EXPR_FLAG(NONE); +#undef GNR_EXPR_NONE + + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "%s invalid query flag: <%.*s>", + error_message_tag, + (int)(query_flags_end - query_flags), + query_flags); + return 0; +#undef CHECK_EXPR_FLAG + } + + return flags; +} + +static void +grn_select_expression_set_condition(grn_ctx *ctx, + grn_obj *expression, + grn_obj *condition) +{ + grn_obj *condition_ptr; + + if (!expression) { + return; + } + + condition_ptr = + grn_expr_get_or_add_var(ctx, expression, + GRN_SELECT_INTERNAL_VAR_CONDITION, + GRN_SELECT_INTERNAL_VAR_CONDITION_LEN); + GRN_PTR_INIT(condition_ptr, 0, GRN_DB_OBJECT); + GRN_PTR_SET(ctx, condition_ptr, condition); +} + +grn_bool +grn_proc_select_format_init(grn_ctx *ctx, + grn_obj_format *format, + grn_obj *result_set, + int n_hits, + int offset, + int limit, + const char *columns, + int columns_len, + grn_obj *condition) +{ + grn_rc rc; + + GRN_OBJ_FORMAT_INIT(format, n_hits, offset, limit, offset); + format->flags = + GRN_OBJ_FORMAT_WITH_COLUMN_NAMES| + GRN_OBJ_FORMAT_XML_ELEMENT_RESULTSET; + rc = grn_output_format_set_columns(ctx, + format, + result_set, + columns, + columns_len); + if (rc != GRN_SUCCESS) { + GRN_OBJ_FORMAT_FIN(ctx, format); + return GRN_FALSE; + } + + grn_select_expression_set_condition(ctx, format->expression, condition); + + return ctx->rc == GRN_SUCCESS; +} + +grn_bool +grn_proc_select_format_fin(grn_ctx *ctx, grn_obj_format *format) +{ + GRN_OBJ_FORMAT_FIN(ctx, format); + + return ctx->rc == GRN_SUCCESS; +} + +grn_bool +grn_proc_select_output_columns_open(grn_ctx *ctx, + grn_obj_format *format, + grn_obj *res, + int n_hits, + int offset, + int limit, + const char *columns, + int columns_len, + grn_obj *condition, + uint32_t n_additional_elements) +{ + grn_bool succeeded; + + if (!grn_proc_select_format_init(ctx, + format, + res, + n_hits, + offset, + limit, + columns, + columns_len, + condition)) { + return GRN_FALSE; + } + + GRN_OUTPUT_RESULT_SET_OPEN(res, format, n_additional_elements); + succeeded = (ctx->rc == GRN_SUCCESS); + if (!succeeded) { + GRN_OUTPUT_RESULT_SET_CLOSE(res, format); + } + + return succeeded; +} + +grn_bool +grn_proc_select_output_columns_close(grn_ctx *ctx, + grn_obj_format *format, + grn_obj *result_set) +{ + GRN_OUTPUT_RESULT_SET_CLOSE(result_set, format); + + return grn_proc_select_format_fin(ctx, format); +} + +grn_bool +grn_proc_select_output_columns(grn_ctx *ctx, + grn_obj *res, + int n_hits, + int offset, + int limit, + const char *columns, + int columns_len, + grn_obj *condition) +{ + grn_obj_format format; + uint32_t n_additional_elements = 0; + + if (!grn_proc_select_output_columns_open(ctx, + &format, + res, + n_hits, + offset, + limit, + columns, + columns_len, + condition, + n_additional_elements)) { + return GRN_FALSE; + } + + return grn_proc_select_output_columns_close(ctx, &format, res); +} + +static grn_obj * +grn_select_create_all_selected_result_table(grn_ctx *ctx, + grn_obj *table) +{ + grn_obj *result; + grn_posting posting; + + result = grn_table_create(ctx, NULL, 0, NULL, + GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, + table, NULL); + if (!result) { + return NULL; + } + + memset(&posting, 0, sizeof(grn_posting)); + GRN_TABLE_EACH_BEGIN(ctx, table, cursor, id) { + posting.rid = id; + grn_ii_posting_add(ctx, + &posting, + (grn_hash *)(result), + GRN_OP_OR); + } GRN_TABLE_EACH_END(ctx, cursor); + + return result; +} + +static grn_obj * +grn_select_create_no_sort_keys_sorted_table(grn_ctx *ctx, + grn_select_data *data, + grn_obj *table) +{ + grn_obj *sorted; + grn_table_cursor *cursor; + + sorted = grn_table_create(ctx, NULL, 0, NULL, + GRN_OBJ_TABLE_NO_KEY, + NULL, + table); + + if (!sorted) { + return NULL; + } + + cursor = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, + data->offset, + data->limit, + GRN_CURSOR_ASCENDING); + if (cursor) { + grn_id id; + while ((id = grn_table_cursor_next(ctx, cursor))) { + grn_id *value; + if (grn_array_add(ctx, (grn_array *)sorted, (void **)&value)) { + *value = id; + } + } + grn_table_cursor_close(ctx, cursor); + } + + return sorted; +} + + +static void +grn_select_apply_columns(grn_ctx *ctx, + grn_select_data *data, + grn_obj *table, + grn_hash *columns) +{ + grn_hash_cursor *columns_cursor; + + columns_cursor = grn_hash_cursor_open(ctx, columns, + NULL, 0, NULL, 0, 0, -1, 0); + if (!columns_cursor) { + return; + } + + while (grn_hash_cursor_next(ctx, columns_cursor) != GRN_ID_NIL) { + grn_column_data *column_data; + grn_obj *column; + grn_obj *expression; + grn_obj *record; + + grn_hash_cursor_get_value(ctx, columns_cursor, (void **)&column_data); + + column = grn_column_create(ctx, + table, + column_data->label.value, + column_data->label.length, + NULL, + column_data->flags, + column_data->type); + if (!column) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][column][%s][%.*s] failed to create column: %s", + grn_column_stage_name(column_data->stage), + (int)(column_data->label.length), + column_data->label.value, + ctx->errbuf); + break; + } + + GRN_EXPR_CREATE_FOR_QUERY(ctx, table, expression, record); + if (!expression) { + grn_obj_close(ctx, column); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][column][%s][%.*s] " + "failed to create expression to compute value: %s", + grn_column_stage_name(column_data->stage), + (int)(column_data->label.length), + column_data->label.value, + ctx->errbuf); + break; + } + grn_expr_parse(ctx, + expression, + column_data->value.value, + column_data->value.length, + NULL, + GRN_OP_MATCH, + GRN_OP_AND, + GRN_EXPR_SYNTAX_SCRIPT); + if (ctx->rc != GRN_SUCCESS) { + grn_obj_close(ctx, expression); + grn_obj_close(ctx, column); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][column][%s][%.*s] " + "failed to parse value: <%.*s>: %s", + grn_column_stage_name(column_data->stage), + (int)(column_data->label.length), + column_data->label.value, + (int)(column_data->value.length), + column_data->value.value, + ctx->errbuf); + break; + } + grn_select_expression_set_condition(ctx, + expression, + data->filter.condition.expression); + + if (column_data->window.sort_keys.length > 0 || + column_data->window.group_keys.length > 0) { + grn_window_definition definition; + grn_rc rc; + + if (column_data->window.sort_keys.length > 0) { + int n_sort_keys; + definition.sort_keys = + grn_table_sort_key_from_str(ctx, + column_data->window.sort_keys.value, + column_data->window.sort_keys.length, + table, &n_sort_keys); + definition.n_sort_keys = n_sort_keys; + if (!definition.sort_keys) { + grn_obj_close(ctx, expression); + grn_obj_close(ctx, column); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][column][%s][%.*s] " + "failed to parse sort keys: %s", + grn_column_stage_name(column_data->stage), + (int)(column_data->label.length), + column_data->label.value, + ctx->errbuf); + break; + } + } else { + definition.sort_keys = NULL; + definition.n_sort_keys = 0; + } + + if (column_data->window.group_keys.length > 0) { + int n_group_keys; + definition.group_keys = + grn_table_sort_key_from_str(ctx, + column_data->window.group_keys.value, + column_data->window.group_keys.length, + table, &n_group_keys); + definition.n_group_keys = n_group_keys; + if (!definition.group_keys) { + grn_obj_close(ctx, expression); + grn_obj_close(ctx, column); + if (definition.sort_keys) { + grn_table_sort_key_close(ctx, + definition.sort_keys, + definition.n_sort_keys); + } + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][column][%s][%.*s] " + "failed to parse group keys: %s", + grn_column_stage_name(column_data->stage), + (int)(column_data->label.length), + column_data->label.value, + ctx->errbuf); + break; + } + } else { + definition.group_keys = NULL; + definition.n_group_keys = 0; + } + + rc = grn_table_apply_window_function(ctx, + table, + column, + &definition, + expression); + if (definition.sort_keys) { + grn_table_sort_key_close(ctx, + definition.sort_keys, + definition.n_sort_keys); + } + if (definition.group_keys) { + grn_table_sort_key_close(ctx, + definition.group_keys, + definition.n_group_keys); + } + if (rc != GRN_SUCCESS) { + grn_obj_close(ctx, expression); + grn_obj_close(ctx, column); + break; + } + } else { + grn_rc rc; + rc = grn_table_apply_expr(ctx, table, column, expression); + if (rc != GRN_SUCCESS) { + grn_obj_close(ctx, expression); + grn_obj_close(ctx, column); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][column][%s][%.*s] " + "failed to apply expression to generate column values: " + "%s", + grn_column_stage_name(column_data->stage), + (int)(column_data->label.length), + column_data->label.value, + ctx->errbuf); + break; + } + } + + grn_obj_close(ctx, expression); + + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "columns[%.*s](%d)", + (int)(column_data->label.length), + column_data->label.value, + grn_table_size(ctx, table)); + } + + grn_hash_cursor_close(ctx, columns_cursor); +} + +static grn_bool +grn_select_apply_initial_columns(grn_ctx *ctx, + grn_select_data *data) +{ + if (!data->columns.initial) { + return GRN_TRUE; + } + + data->tables.initial = + grn_select_create_all_selected_result_table(ctx, data->tables.target); + if (!data->tables.initial) { + return GRN_FALSE; + } + + grn_select_apply_columns(ctx, + data, + data->tables.initial, + data->columns.initial); + + return ctx->rc == GRN_SUCCESS; +} + +static grn_bool +grn_select_filter(grn_ctx *ctx, + grn_select_data *data) +{ + if (!grn_filter_data_execute(ctx, + &(data->filter), + data->tables.initial, + "[select]")) { + return GRN_FALSE; + } + + data->tables.result = data->filter.filtered; + if (!data->tables.result) { + data->tables.result = data->tables.initial; + } + + { + grn_expr *expression; + expression = (grn_expr *)(data->filter.condition.expression); + if (expression) { + data->cacheable *= expression->cacheable; + data->taintable += expression->taintable; + } + } + + return GRN_TRUE; +} + +static grn_bool +grn_select_apply_filtered_columns(grn_ctx *ctx, + grn_select_data *data) +{ + if (!data->columns.filtered) { + return GRN_TRUE; + } + + if (data->tables.result == data->tables.initial) { + data->tables.result = + grn_select_create_all_selected_result_table(ctx, data->tables.initial); + if (!data->tables.result) { + return GRN_FALSE; + } + } + + grn_select_apply_columns(ctx, + data, + data->tables.result, + data->columns.filtered); + + return ctx->rc == GRN_SUCCESS; +} + +static int +grn_select_apply_adjuster_execute_ensure_factor(grn_ctx *ctx, + grn_obj *factor_object) +{ + if (!factor_object) { + return 1; + } else if (factor_object->header.domain == GRN_DB_INT32) { + return GRN_INT32_VALUE(factor_object); + } else { + grn_rc rc; + grn_obj int32_object; + int factor; + GRN_INT32_INIT(&int32_object, 0); + rc = grn_obj_cast(ctx, factor_object, &int32_object, GRN_FALSE); + if (rc == GRN_SUCCESS) { + factor = GRN_INT32_VALUE(&int32_object); + } else { + /* TODO: Log or return error? */ + factor = 1; + } + GRN_OBJ_FIN(ctx, &int32_object); + return factor; + } +} + +static void +grn_select_apply_adjuster_execute_adjust(grn_ctx *ctx, + grn_obj *table, + grn_obj *column, + grn_obj *value, + grn_obj *factor) +{ + grn_obj *index; + unsigned int n_indexes; + int factor_value; + + n_indexes = grn_column_index(ctx, column, GRN_OP_MATCH, &index, 1, NULL); + if (n_indexes == 0) { + char column_name[GRN_TABLE_MAX_KEY_SIZE]; + int column_name_size; + column_name_size = grn_obj_name(ctx, column, + column_name, GRN_TABLE_MAX_KEY_SIZE); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "adjuster requires index column for the target column: " + "<%.*s>", + column_name_size, + column_name); + return; + } + + factor_value = grn_select_apply_adjuster_execute_ensure_factor(ctx, factor); + + { + grn_search_optarg options; + memset(&options, 0, sizeof(grn_search_optarg)); + + options.mode = GRN_OP_EXACT; + options.similarity_threshold = 0; + options.max_interval = 0; + options.weight_vector = NULL; + options.vector_size = factor_value; + options.proc = NULL; + options.max_size = 0; + options.scorer = NULL; + + grn_obj_search(ctx, index, value, table, GRN_OP_ADJUST, &options); + } +} + +static void +grn_select_apply_adjuster_execute(grn_ctx *ctx, + grn_obj *table, + grn_obj *adjuster) +{ + grn_expr *expr = (grn_expr *)adjuster; + grn_expr_code *code, *code_end; + + code = expr->codes; + code_end = expr->codes + expr->codes_curr; + while (code < code_end) { + grn_obj *column, *value, *factor; + + if (code->op == GRN_OP_PLUS) { + code++; + continue; + } + + column = code->value; + code++; + value = code->value; + code++; + code++; /* op == GRN_OP_MATCH */ + if ((code_end - code) >= 2 && code[1].op == GRN_OP_STAR) { + factor = code->value; + code++; + code++; /* op == GRN_OP_STAR */ + } else { + factor = NULL; + } + grn_select_apply_adjuster_execute_adjust(ctx, table, column, value, factor); + } +} + +static grn_bool +grn_select_apply_adjuster(grn_ctx *ctx, + grn_select_data *data) +{ + grn_obj *adjuster; + grn_obj *record; + grn_rc rc; + + if (data->adjuster.length == 0) { + return GRN_TRUE; + } + + GRN_EXPR_CREATE_FOR_QUERY(ctx, data->tables.target, adjuster, record); + if (!adjuster) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][adjuster] " + "failed to create expression: %s", + ctx->errbuf); + return GRN_FALSE; + } + + rc = grn_expr_parse(ctx, adjuster, + data->adjuster.value, + data->adjuster.length, + NULL, + GRN_OP_MATCH, GRN_OP_ADJUST, + GRN_EXPR_SYNTAX_ADJUSTER); + if (rc != GRN_SUCCESS) { + grn_obj_unlink(ctx, adjuster); + GRN_PLUGIN_ERROR(ctx, + rc, + "[select][adjuster] " + "failed to parse: %s", + ctx->errbuf); + return GRN_FALSE; + } + + data->cacheable *= ((grn_expr *)adjuster)->cacheable; + data->taintable += ((grn_expr *)adjuster)->taintable; + grn_select_apply_adjuster_execute(ctx, data->tables.result, adjuster); + grn_obj_unlink(ctx, adjuster); + + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "adjust(%d)", grn_table_size(ctx, data->tables.result)); + + return GRN_TRUE; +} + +static grn_bool +grn_select_apply_scorer(grn_ctx *ctx, + grn_select_data *data) +{ + grn_obj *scorer; + grn_obj *record; + grn_rc rc = GRN_SUCCESS; + + if (data->scorer.length == 0) { + return GRN_TRUE; + } + + GRN_EXPR_CREATE_FOR_QUERY(ctx, data->tables.result, scorer, record); + if (!scorer) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][scorer] " + "failed to create expression: %s", + ctx->errbuf); + return GRN_FALSE; + } + + rc = grn_expr_parse(ctx, + scorer, + data->scorer.value, + data->scorer.length, + NULL, + GRN_OP_MATCH, + GRN_OP_AND, + GRN_EXPR_SYNTAX_SCRIPT|GRN_EXPR_ALLOW_UPDATE); + if (rc != GRN_SUCCESS) { + grn_obj_unlink(ctx, scorer); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][scorer] " + "failed to parse: %s", + ctx->errbuf); + return GRN_FALSE; + } + + data->cacheable *= ((grn_expr *)scorer)->cacheable; + data->taintable += ((grn_expr *)scorer)->taintable; + GRN_TABLE_EACH_BEGIN(ctx, data->tables.result, cursor, id) { + GRN_RECORD_SET(ctx, record, id); + grn_expr_exec(ctx, scorer, 0); + if (ctx->rc) { + rc = ctx->rc; + GRN_PLUGIN_ERROR(ctx, + rc, + "[select][scorer] " + "failed to execute: <%.*s>: %s", + (int)(data->scorer.length), + data->scorer.value, + ctx->errbuf); + break; + } + } GRN_TABLE_EACH_END(ctx, cursor); + grn_obj_unlink(ctx, scorer); + + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "score(%d)", grn_table_size(ctx, data->tables.result)); + + return rc == GRN_SUCCESS; +} + +static grn_bool +grn_select_sort(grn_ctx *ctx, + grn_select_data *data) +{ + grn_table_sort_key *keys; + uint32_t n_keys; + + if (data->sort_keys.length == 0) { + return GRN_TRUE; + } + + keys = grn_table_sort_key_from_str(ctx, + data->sort_keys.value, + data->sort_keys.length, + data->tables.result, + &n_keys); + if (!keys) { + if (ctx->rc == GRN_SUCCESS) { + return GRN_TRUE; + } else { + GRN_PLUGIN_ERROR(ctx, + ctx->rc, + "[select][sort] " + "failed to parse: <%.*s>: %s", + (int)(data->sort_keys.length), + data->sort_keys.value, + ctx->errbuf); + return GRN_FALSE; + } + } + + data->tables.sorted = grn_table_create(ctx, NULL, 0, NULL, + GRN_OBJ_TABLE_NO_KEY, + NULL, + data->tables.result); + if (!data->tables.sorted) { + GRN_PLUGIN_ERROR(ctx, + ctx->rc, + "[select][sort] " + "failed to create table to store sorted record: " + "<%.*s>: %s", + (int)(data->sort_keys.length), + data->sort_keys.value, + ctx->errbuf); + return GRN_FALSE; + } + + grn_table_sort(ctx, + data->tables.result, + data->offset, + data->limit, + data->tables.sorted, + keys, + n_keys); + + grn_table_sort_key_close(ctx, keys, n_keys); + + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "sort(%d)", data->limit); + + return ctx->rc == GRN_SUCCESS; +} + +static grn_bool +grn_select_apply_output_columns(grn_ctx *ctx, + grn_select_data *data) +{ + if (!data->columns.output) { + return GRN_TRUE; + } + + if (!data->tables.sorted) { + data->tables.sorted = + grn_select_create_no_sort_keys_sorted_table(ctx, + data, + data->tables.result); + if (!data->tables.sorted) { + return GRN_FALSE; + } + } + + grn_select_apply_columns(ctx, + data, + data->tables.sorted, + data->columns.output); + + return ctx->rc == GRN_SUCCESS; +} + +static grn_bool +grn_select_output_match_open(grn_ctx *ctx, + grn_select_data *data, + grn_obj_format *format, + uint32_t n_additional_elements) +{ + grn_bool succeeded = GRN_TRUE; + int offset; + grn_obj *output_table; + + if (data->tables.sorted) { + offset = 0; + output_table = data->tables.sorted; + } else { + offset = data->offset; + output_table = data->tables.result; + } + succeeded = + grn_proc_select_output_columns_open(ctx, + format, + output_table, + grn_table_size(ctx, data->tables.result), + offset, + data->limit, + data->output_columns.value, + data->output_columns.length, + data->filter.condition.expression, + n_additional_elements); + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "output(%d)", data->limit); + + return succeeded; +} + +static grn_bool +grn_select_output_match_close(grn_ctx *ctx, + grn_select_data *data, + grn_obj_format *format) +{ + grn_obj *output_table; + + if (data->tables.sorted) { + output_table = data->tables.sorted; + } else { + output_table = data->tables.result; + } + + return grn_proc_select_output_columns_close(ctx, format, output_table); +} + +static grn_bool +grn_select_output_match(grn_ctx *ctx, grn_select_data *data) +{ + grn_obj_format format; + uint32_t n_additional_elements = 0; + + if (!grn_select_output_match_open(ctx, data, &format, n_additional_elements)) { + return GRN_FALSE; + } + + return grn_select_output_match_close(ctx, data, &format); +} + +static grn_bool +grn_select_slice_execute(grn_ctx *ctx, + grn_select_data *data, + grn_obj *table, + grn_slice_data *slice) +{ + char tag[GRN_TABLE_MAX_KEY_SIZE]; + grn_filter_data *filter; + + grn_snprintf(tag, GRN_TABLE_MAX_KEY_SIZE, GRN_TABLE_MAX_KEY_SIZE, + "[select][slices][%.*s]", + (int)(slice->label.length), + slice->label.value); + filter = &(slice->filter); + if (filter->query.length == 0 && filter->filter.length == 0) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "%s slice requires query or filter", + tag); + return GRN_FALSE; + } + + if (!grn_filter_data_execute(ctx, filter, table, tag)) { + return GRN_FALSE; + } + + slice->table = filter->filtered; + + return GRN_TRUE; +} + +static grn_bool +grn_select_slices_execute(grn_ctx *ctx, + grn_select_data *data, + grn_obj *table, + grn_hash *slices) +{ + grn_bool succeeded = GRN_TRUE; + + GRN_HASH_EACH_BEGIN(ctx, slices, cursor, id) { + grn_slice_data *slice; + + grn_hash_cursor_get_value(ctx, cursor, (void **)&slice); + if (!grn_select_slice_execute(ctx, data, table, slice)) { + succeeded = GRN_FALSE; + break; + } + } GRN_HASH_EACH_END(ctx, cursor); + + return succeeded; +} + +static grn_bool +grn_select_prepare_slices(grn_ctx *ctx, + grn_select_data *data) +{ + if (!data->slices) { + return GRN_TRUE; + } + + if (!grn_select_slices_execute(ctx, data, data->tables.result, data->slices)) { + return GRN_FALSE; + } + + data->output.n_elements += 1; + + return GRN_TRUE; +} + +static grn_bool +grn_select_output_slices(grn_ctx *ctx, + grn_select_data *data) +{ + grn_bool succeeded = GRN_TRUE; + unsigned int n_available_results = 0; + + if (!data->slices) { + return GRN_TRUE; + } + + data->output.formatter->slices_label(ctx, data); + + GRN_HASH_EACH_BEGIN(ctx, data->slices, cursor, id) { + grn_slice_data *slice; + + grn_hash_cursor_get_value(ctx, cursor, (void **)&slice); + if (slice->table) { + n_available_results++; + } + } GRN_HASH_EACH_END(ctx, cursor); + + data->output.formatter->slices_open(ctx, data, n_available_results); + + GRN_HASH_EACH_BEGIN(ctx, data->slices, cursor, id) { + grn_slice_data *slice; + uint32_t n_hits; + int offset; + int limit; + + grn_hash_cursor_get_value(ctx, cursor, (void **)&slice); + if (!slice->table) { + continue; + } + + n_hits = grn_table_size(ctx, slice->table); + + offset = slice->offset; + limit = slice->limit; + grn_normalize_offset_and_limit(ctx, n_hits, &offset, &limit); + + if (slice->sort_keys.length > 0) { + grn_table_sort_key *sort_keys; + uint32_t n_sort_keys; + sort_keys = grn_table_sort_key_from_str(ctx, + slice->sort_keys.value, + slice->sort_keys.length, + slice->table, &n_sort_keys); + if (sort_keys) { + grn_obj *sorted; + sorted = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_NO_KEY, + NULL, slice->table); + if (sorted) { + grn_table_sort(ctx, slice->table, offset, limit, + sorted, sort_keys, n_sort_keys); + data->output.formatter->slice_label(ctx, data, slice); + if (!grn_proc_select_output_columns(ctx, + sorted, + n_hits, + 0, + limit, + slice->output_columns.value, + slice->output_columns.length, + slice->filter.condition.expression)) { + succeeded = GRN_FALSE; + } + grn_obj_unlink(ctx, sorted); + } + grn_table_sort_key_close(ctx, sort_keys, n_sort_keys); + } else { + succeeded = GRN_FALSE; + } + } else { + data->output.formatter->slice_label(ctx, data, slice); + if (!grn_proc_select_output_columns(ctx, + slice->table, + n_hits, + offset, + limit, + slice->output_columns.value, + slice->output_columns.length, + slice->filter.condition.expression)) { + succeeded = GRN_FALSE; + } + } + + if (!succeeded) { + break; + } + + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "slice(%d)[%.*s]", + n_hits, + (int)(slice->label.length), + slice->label.value); + } GRN_HASH_EACH_END(ctx, cursor); + + data->output.formatter->slices_close(ctx, data); + + return succeeded; +} + +static grn_bool +grn_select_drilldown_execute(grn_ctx *ctx, + grn_select_data *data, + grn_obj *table, + grn_hash *drilldowns, + grn_id id) +{ + grn_table_sort_key *keys = NULL; + unsigned int n_keys = 0; + grn_obj *target_table = table; + grn_drilldown_data *drilldown; + grn_table_group_result *result; + + drilldown = + (grn_drilldown_data *)grn_hash_get_value_(ctx, drilldowns, id, NULL); + result = &(drilldown->result); + + result->limit = 1; + result->flags = GRN_TABLE_GROUP_CALC_COUNT; + result->op = 0; + result->max_n_subrecs = 0; + result->key_begin = 0; + result->key_end = 0; + if (result->calc_target) { + grn_obj_unlink(ctx, result->calc_target); + } + result->calc_target = NULL; + + if (drilldown->table_name.length > 0) { + grn_id dependent_id; + dependent_id = grn_hash_get(ctx, + drilldowns, + drilldown->table_name.value, + drilldown->table_name.length, + NULL); + if (dependent_id == GRN_ID_NIL) { + if (data->slices) { + grn_slice_data *slice; + dependent_id = grn_hash_get(ctx, + data->slices, + drilldown->table_name.value, + drilldown->table_name.length, + NULL); + if (dependent_id) { + slice = + (grn_slice_data *)grn_hash_get_value_(ctx, data->slices, + dependent_id, NULL); + target_table = slice->table; + } + } + if (dependent_id == GRN_ID_NIL) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[select][drilldowns][%.*s][table] " + "nonexistent label: <%.*s>", + (int)(drilldown->label.length), + drilldown->label.value, + (int)(drilldown->table_name.length), + drilldown->table_name.value); + return GRN_FALSE; + } + } else { + grn_drilldown_data *dependent_drilldown; + grn_table_group_result *dependent_result; + + dependent_drilldown = + (grn_drilldown_data *)grn_hash_get_value_(ctx, + drilldowns, + dependent_id, + NULL); + dependent_result = &(dependent_drilldown->result); + target_table = dependent_result->table; + } + } + + if (drilldown->parsed_keys) { + result->key_end = drilldown->n_parsed_keys; + } else if (drilldown->keys.length > 0) { + keys = grn_table_sort_key_from_str(ctx, + drilldown->keys.value, + drilldown->keys.length, + target_table, &n_keys); + if (!keys) { + GRN_PLUGIN_CLEAR_ERROR(ctx); + return GRN_FALSE; + } + + result->key_end = n_keys - 1; + if (n_keys > 1) { + result->max_n_subrecs = 1; + } + } + + if (drilldown->calc_target_name.length > 0) { + result->calc_target = grn_obj_column(ctx, target_table, + drilldown->calc_target_name.value, + drilldown->calc_target_name.length); + } + if (result->calc_target) { + result->flags |= drilldown->calc_types; + } + + if (drilldown->parsed_keys) { + grn_table_group(ctx, + target_table, + drilldown->parsed_keys, + drilldown->n_parsed_keys, + result, + 1); + } else { + grn_table_group(ctx, target_table, keys, n_keys, result, 1); + } + + if (keys) { + grn_table_sort_key_close(ctx, keys, n_keys); + } + + if (!result->table) { + return GRN_FALSE; + } + + if (drilldown->columns.initial) { + grn_select_apply_columns(ctx, + data, + result->table, + drilldown->columns.initial); + } + + if (drilldown->filter.length > 0) { + grn_obj *expression; + grn_obj *record; + GRN_EXPR_CREATE_FOR_QUERY(ctx, result->table, expression, record); + if (!expression) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][drilldowns]%s%.*s%s[filter] " + "failed to create expression for filter: %s", + drilldown->label.length > 0 ? "[" : "", + (int)(drilldown->label.length), + drilldown->label.value, + drilldown->label.length > 0 ? "]" : "", + ctx->errbuf); + return GRN_FALSE; + } + grn_expr_parse(ctx, + expression, + drilldown->filter.value, + drilldown->filter.length, + NULL, + GRN_OP_MATCH, + GRN_OP_AND, + GRN_EXPR_SYNTAX_SCRIPT); + if (ctx->rc != GRN_SUCCESS) { + grn_obj_close(ctx, expression); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][drilldowns]%s%.*s%s[filter] " + "failed to parse filter: <%.*s>: %s", + drilldown->label.length > 0 ? "[" : "", + (int)(drilldown->label.length), + drilldown->label.value, + drilldown->label.length > 0 ? "]" : "", + (int)(drilldown->filter.length), + drilldown->filter.value, + ctx->errbuf); + return GRN_FALSE; + } + drilldown->filtered_result = grn_table_select(ctx, + result->table, + expression, + NULL, + GRN_OP_OR); + if (ctx->rc != GRN_SUCCESS) { + grn_obj_close(ctx, expression); + if (drilldown->filtered_result) { + grn_obj_close(ctx, drilldown->filtered_result); + drilldown->filtered_result = NULL; + } + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][drilldowns]%s%.*s%s[filter] " + "failed to execute filter: <%.*s>: %s", + drilldown->label.length > 0 ? "[" : "", + (int)(drilldown->label.length), + drilldown->label.value, + drilldown->label.length > 0 ? "]" : "", + (int)(drilldown->filter.length), + drilldown->filter.value, + ctx->errbuf); + return GRN_FALSE; + } + grn_obj_close(ctx, expression); + } + + { + unsigned int n_hits; + + if (drilldown->filtered_result) { + n_hits = grn_table_size(ctx, drilldown->filtered_result); + } else { + n_hits = grn_table_size(ctx, result->table); + } + if (data->drilldown.keys.length == 0) { + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "drilldowns[%.*s](%u)", + (int)(drilldown->label.length), + drilldown->label.value, + n_hits); + } else { + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "drilldown(%u)", + n_hits); + } + } + + return GRN_TRUE; +} + +typedef enum { + TSORT_STATUS_NOT_VISITED, + TSORT_STATUS_VISITING, + TSORT_STATUS_VISITED +} tsort_status; + +static grn_bool +drilldown_tsort_visit(grn_ctx *ctx, + grn_hash *drilldowns, + tsort_status *statuses, + grn_obj *ids, + grn_id id) +{ + grn_bool cycled = GRN_TRUE; + uint32_t index = id - 1; + + switch (statuses[index]) { + case TSORT_STATUS_VISITING : + cycled = GRN_TRUE; + break; + case TSORT_STATUS_VISITED : + cycled = GRN_FALSE; + break; + case TSORT_STATUS_NOT_VISITED : + cycled = GRN_FALSE; + statuses[index] = TSORT_STATUS_VISITING; + { + grn_drilldown_data *drilldown; + drilldown = + (grn_drilldown_data *)grn_hash_get_value_(ctx, drilldowns, id, NULL); + if (drilldown->table_name.length > 0) { + grn_id dependent_id; + dependent_id = grn_hash_get(ctx, drilldowns, + drilldown->table_name.value, + drilldown->table_name.length, + NULL); + if (dependent_id != GRN_ID_NIL) { + cycled = drilldown_tsort_visit(ctx, + drilldowns, + statuses, + ids, + dependent_id); + if (cycled) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[select][drilldowns][%.*s][table] " + "cycled dependency: <%.*s>", + (int)(drilldown->label.length), + drilldown->label.value, + (int)(drilldown->table_name.length), + drilldown->table_name.value); + } + } + } + } + if (!cycled) { + statuses[index] = TSORT_STATUS_VISITED; + GRN_RECORD_PUT(ctx, ids, id); + } + break; + } + + return cycled; +} + +static grn_bool +drilldown_tsort_body(grn_ctx *ctx, + grn_hash *drilldowns, + tsort_status *statuses, + grn_obj *ids) +{ + grn_bool succeeded = GRN_TRUE; + + GRN_HASH_EACH_BEGIN(ctx, drilldowns, cursor, id) { + if (drilldown_tsort_visit(ctx, drilldowns, statuses, ids, id)) { + succeeded = GRN_FALSE; + break; + } + } GRN_HASH_EACH_END(ctx, cursor); + + return succeeded; +} + +static void +drilldown_tsort_init(grn_ctx *ctx, + tsort_status *statuses, + size_t n_statuses) +{ + size_t i; + for (i = 0; i < n_statuses; i++) { + statuses[i] = TSORT_STATUS_NOT_VISITED; + } +} + +static grn_bool +drilldown_tsort(grn_ctx *ctx, + grn_hash *drilldowns, + grn_obj *ids) +{ + tsort_status *statuses; + size_t n_statuses; + grn_bool succeeded; + + n_statuses = grn_hash_size(ctx, drilldowns); + statuses = GRN_PLUGIN_MALLOCN(ctx, tsort_status, n_statuses); + if (!statuses) { + return GRN_FALSE; + } + + drilldown_tsort_init(ctx, statuses, n_statuses); + succeeded = drilldown_tsort_body(ctx, drilldowns, statuses, ids); + GRN_PLUGIN_FREE(ctx, statuses); + return succeeded; +} + +static grn_bool +grn_select_drilldowns_execute(grn_ctx *ctx, + grn_select_data *data) +{ + grn_bool succeeded = GRN_TRUE; + grn_obj tsorted_ids; + size_t i; + size_t n_drilldowns; + + GRN_RECORD_INIT(&tsorted_ids, GRN_OBJ_VECTOR, GRN_ID_NIL); + if (!drilldown_tsort(ctx, data->drilldowns, &tsorted_ids)) { + succeeded = GRN_FALSE; + goto exit; + } + + n_drilldowns = GRN_BULK_VSIZE(&tsorted_ids) / sizeof(grn_id); + for (i = 0; i < n_drilldowns; i++) { + grn_id id; + + id = GRN_RECORD_VALUE_AT(&tsorted_ids, i); + if (!grn_select_drilldown_execute(ctx, + data, + data->tables.result, + data->drilldowns, + id)) { + if (ctx->rc != GRN_SUCCESS) { + succeeded = GRN_FALSE; + break; + } + } + } + +exit : + GRN_OBJ_FIN(ctx, &tsorted_ids); + + return succeeded; +} + +static grn_drilldown_data * +grn_select_data_drilldowns_add(grn_ctx *ctx, + grn_select_data *data, + const char *label, + size_t label_len) +{ + grn_drilldown_data *drilldown = NULL; + int added; + + if (!data->drilldowns) { + data->drilldowns = grn_hash_create(ctx, + NULL, + GRN_TABLE_MAX_KEY_SIZE, + sizeof(grn_drilldown_data), + GRN_OBJ_TABLE_HASH_KEY | + GRN_OBJ_KEY_VAR_SIZE | + GRN_HASH_TINY); + if (!data->drilldowns) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][drilldowns] " + "failed to allocate drilldowns data: %s", + ctx->errbuf); + return NULL; + } + } + + grn_hash_add(ctx, + data->drilldowns, + label, + label_len, + (void **)&drilldown, + &added); + if (added) { + grn_drilldown_data_init(ctx, drilldown, label, label_len); + } + + return drilldown; +} + +static grn_bool +grn_select_prepare_drilldowns(grn_ctx *ctx, + grn_select_data *data) +{ + if (data->drilldown.keys.length > 0) { + data->drilldown.parsed_keys = + grn_table_sort_key_from_str(ctx, + data->drilldown.keys.value, + data->drilldown.keys.length, + data->tables.result, + &(data->drilldown.n_parsed_keys)); + if (data->drilldown.parsed_keys) { + int i; + grn_obj buffer; + + GRN_TEXT_INIT(&buffer, 0); + for (i = 0; i < data->drilldown.n_parsed_keys; i++) { + grn_drilldown_data *drilldown; + + GRN_BULK_REWIND(&buffer); + grn_text_printf(ctx, &buffer, "drilldown%d", i); + drilldown = grn_select_data_drilldowns_add(ctx, + data, + GRN_TEXT_VALUE(&buffer), + GRN_TEXT_LEN(&buffer)); + if (!drilldown) { + continue; + } + + drilldown->parsed_keys = data->drilldown.parsed_keys + i; + drilldown->n_parsed_keys = 1; + +#define COPY(field) \ + drilldown->field = data->drilldown.field + + COPY(sort_keys); + COPY(output_columns); + COPY(offset); + COPY(limit); + COPY(calc_types); + COPY(calc_target_name); + COPY(filter); + +#undef COPY + } + } + } + + if (!data->drilldowns) { + return GRN_TRUE; + } + + if (!grn_select_drilldowns_execute(ctx, data)) { + return GRN_FALSE; + } + + { + unsigned int n_available_results = 0; + + GRN_HASH_EACH_BEGIN(ctx, data->drilldowns, cursor, id) { + grn_drilldown_data *drilldown; + grn_table_group_result *result; + + grn_hash_cursor_get_value(ctx, cursor, (void **)&drilldown); + result = &(drilldown->result); + if (result->table) { + n_available_results++; + } + } GRN_HASH_EACH_END(ctx, cursor); + + if (data->drilldown.keys.length > 0) { + data->output.n_elements += n_available_results; + } else { + if (n_available_results > 0) { + data->output.n_elements += 1; + } + } + } + + return GRN_TRUE; +} + +static grn_bool +grn_select_output_drilldowns(grn_ctx *ctx, + grn_select_data *data) +{ + grn_bool succeeded = GRN_TRUE; + unsigned int n_available_results = 0; + grn_bool is_labeled; + + if (!data->drilldowns) { + return GRN_TRUE; + } + + data->output.formatter->drilldowns_label(ctx, data); + + GRN_HASH_EACH_BEGIN(ctx, data->drilldowns, cursor, id) { + grn_drilldown_data *drilldown; + grn_table_group_result *result; + + grn_hash_cursor_get_value(ctx, cursor, (void **)&drilldown); + result = &(drilldown->result); + if (result->table) { + n_available_results++; + } + } GRN_HASH_EACH_END(ctx, cursor); + + is_labeled = (data->drilldown.keys.length == 0); + + data->output.formatter->drilldowns_open(ctx, data, n_available_results); + + GRN_HASH_EACH_BEGIN(ctx, data->drilldowns, cursor, id) { + grn_drilldown_data *drilldown; + grn_table_group_result *result; + grn_obj *target_table; + uint32_t n_hits; + int offset; + int limit; + + grn_hash_cursor_get_value(ctx, cursor, (void **)&drilldown); + result = &(drilldown->result); + + if (!result->table) { + continue; + } + + if (drilldown->filtered_result) { + target_table = drilldown->filtered_result; + } else { + target_table = result->table; + } + + n_hits = grn_table_size(ctx, target_table); + + offset = drilldown->offset; + limit = drilldown->limit; + grn_normalize_offset_and_limit(ctx, n_hits, &offset, &limit); + + if (drilldown->sort_keys.length > 0) { + grn_table_sort_key *sort_keys; + uint32_t n_sort_keys; + sort_keys = grn_table_sort_key_from_str(ctx, + drilldown->sort_keys.value, + drilldown->sort_keys.length, + target_table, &n_sort_keys); + if (sort_keys) { + grn_obj *sorted; + sorted = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_NO_KEY, + NULL, target_table); + if (sorted) { + grn_table_sort(ctx, target_table, offset, limit, + sorted, sort_keys, n_sort_keys); + data->output.formatter->drilldown_label(ctx, data, drilldown); + if (!grn_proc_select_output_columns(ctx, + sorted, + n_hits, + 0, + limit, + drilldown->output_columns.value, + drilldown->output_columns.length, + data->filter.condition.expression)) { + succeeded = GRN_FALSE; + } + grn_obj_unlink(ctx, sorted); + } + grn_table_sort_key_close(ctx, sort_keys, n_sort_keys); + } else { + succeeded = GRN_FALSE; + } + } else { + data->output.formatter->drilldown_label(ctx, data, drilldown); + if (!grn_proc_select_output_columns(ctx, + target_table, + n_hits, + offset, + limit, + drilldown->output_columns.value, + drilldown->output_columns.length, + data->filter.condition.expression)) { + succeeded = GRN_FALSE; + } + } + + if (!succeeded) { + break; + } + + if (is_labeled) { + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "output.drilldowns[%.*s](%d)", + (int)(drilldown->label.length), + drilldown->label.value, + n_hits); + } else { + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "output.drilldown(%d)", n_hits); + } + } GRN_HASH_EACH_END(ctx, cursor); + + data->output.formatter->drilldowns_close(ctx, data); + + return succeeded; +} + +static grn_bool +grn_select_output(grn_ctx *ctx, grn_select_data *data) +{ + grn_bool succeeded = GRN_TRUE; + + if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) { + GRN_OUTPUT_ARRAY_OPEN("RESULT", data->output.n_elements); + succeeded = grn_select_output_match(ctx, data); + if (succeeded) { + succeeded = grn_select_output_slices(ctx, data); + } + if (succeeded) { + succeeded = grn_select_output_drilldowns(ctx, data); + } + GRN_OUTPUT_ARRAY_CLOSE(); + } else { + grn_obj_format format; + uint32_t n_additional_elements = 0; + + if (data->slices) { + n_additional_elements++; + } + if (data->drilldowns) { + n_additional_elements++; + } + + succeeded = grn_select_output_match_open(ctx, + data, + &format, + n_additional_elements); + if (succeeded) { + succeeded = grn_select_output_slices(ctx, data); + if (succeeded) { + succeeded = grn_select_output_drilldowns(ctx, data); + } + if (!grn_select_output_match_close(ctx, data, &format)) { + succeeded = GRN_FALSE; + } + } + } + + return succeeded; +} + +static void +grn_select_output_slices_label_v1(grn_ctx *ctx, grn_select_data *data) +{ +} + +static void +grn_select_output_slices_open_v1(grn_ctx *ctx, + grn_select_data *data, + unsigned int n_result_sets) +{ + GRN_OUTPUT_MAP_OPEN("SLICES", n_result_sets); +} + +static void +grn_select_output_slices_close_v1(grn_ctx *ctx, grn_select_data *data) +{ + GRN_OUTPUT_MAP_CLOSE(); +} + +static void +grn_select_output_slice_label_v1(grn_ctx *ctx, + grn_select_data *data, + grn_slice_data *slice) +{ + GRN_OUTPUT_STR(slice->label.value, slice->label.length); +} + +static void +grn_select_output_drilldowns_label_v1(grn_ctx *ctx, grn_select_data *data) +{ +} + +static void +grn_select_output_drilldowns_open_v1(grn_ctx *ctx, + grn_select_data *data, + unsigned int n_result_sets) +{ + if (data->drilldown.keys.length == 0) { + GRN_OUTPUT_MAP_OPEN("DRILLDOWNS", n_result_sets); + } +} + +static void +grn_select_output_drilldowns_close_v1(grn_ctx *ctx, grn_select_data *data) +{ + if (data->drilldown.keys.length == 0) { + GRN_OUTPUT_MAP_CLOSE(); + } +} + +static void +grn_select_output_drilldown_label_v1(grn_ctx *ctx, + grn_select_data *data, + grn_drilldown_data *drilldown) +{ + if (data->drilldown.keys.length == 0) { + GRN_OUTPUT_STR(drilldown->label.value, drilldown->label.length); + } +} + +static grn_select_output_formatter grn_select_output_formatter_v1 = { + grn_select_output_slices_label_v1, + grn_select_output_slices_open_v1, + grn_select_output_slices_close_v1, + grn_select_output_slice_label_v1, + grn_select_output_drilldowns_label_v1, + grn_select_output_drilldowns_open_v1, + grn_select_output_drilldowns_close_v1, + grn_select_output_drilldown_label_v1 +}; + +static void +grn_select_output_slices_label_v3(grn_ctx *ctx, grn_select_data *data) +{ + GRN_OUTPUT_CSTR("slices"); +} + +static void +grn_select_output_slices_open_v3(grn_ctx *ctx, + grn_select_data *data, + unsigned int n_result_sets) +{ + GRN_OUTPUT_MAP_OPEN("slices", n_result_sets); +} + +static void +grn_select_output_slices_close_v3(grn_ctx *ctx, grn_select_data *data) +{ + GRN_OUTPUT_MAP_CLOSE(); +} + +static void +grn_select_output_slice_label_v3(grn_ctx *ctx, + grn_select_data *data, + grn_slice_data *slice) +{ + GRN_OUTPUT_STR(slice->label.value, slice->label.length); +} + +static void +grn_select_output_drilldowns_label_v3(grn_ctx *ctx, grn_select_data *data) +{ + GRN_OUTPUT_CSTR("drilldowns"); +} + +static void +grn_select_output_drilldowns_open_v3(grn_ctx *ctx, + grn_select_data *data, + unsigned int n_result_sets) +{ + GRN_OUTPUT_MAP_OPEN("drilldowns", n_result_sets); +} + +static void +grn_select_output_drilldowns_close_v3(grn_ctx *ctx, grn_select_data *data) +{ + GRN_OUTPUT_MAP_CLOSE(); +} + +static void +grn_select_output_drilldown_label_v3(grn_ctx *ctx, + grn_select_data *data, + grn_drilldown_data *drilldown) +{ + if (data->drilldown.keys.length == 0) { + GRN_OUTPUT_STR(drilldown->label.value, drilldown->label.length); + } else { + grn_obj *key; + char name[GRN_TABLE_MAX_KEY_SIZE]; + int name_len; + + key = drilldown->parsed_keys[0].key; + switch (key->header.type) { + case GRN_COLUMN_FIX_SIZE : + case GRN_COLUMN_VAR_SIZE : + case GRN_COLUMN_INDEX : + name_len = grn_column_name(ctx, key, name, GRN_TABLE_MAX_KEY_SIZE); + break; + default : + name_len = grn_obj_name(ctx, key, name, GRN_TABLE_MAX_KEY_SIZE); + break; + } + GRN_OUTPUT_STR(name, name_len); + } +} + +static grn_select_output_formatter grn_select_output_formatter_v3 = { + grn_select_output_slices_label_v3, + grn_select_output_slices_open_v3, + grn_select_output_slices_close_v3, + grn_select_output_slice_label_v3, + grn_select_output_drilldowns_label_v3, + grn_select_output_drilldowns_open_v3, + grn_select_output_drilldowns_close_v3, + grn_select_output_drilldown_label_v3 +}; + +static grn_rc +grn_select(grn_ctx *ctx, grn_select_data *data) +{ + uint32_t nhits; + grn_obj *outbuf = ctx->impl->output.buf; + grn_content_type output_type = ctx->impl->output.type; + char cache_key[GRN_CACHE_MAX_KEY_SIZE]; + uint32_t cache_key_size; + long long int threshold, original_threshold = 0; + grn_cache *cache_obj = grn_cache_current_get(ctx); + + if (grn_ctx_get_command_version(ctx) < GRN_COMMAND_VERSION_3) { + data->output.formatter = &grn_select_output_formatter_v1; + } else { + data->output.formatter = &grn_select_output_formatter_v3; + } + + data->cacheable = 1; + data->taintable = 0; + + data->output.n_elements = 0; + + grn_raw_string_lstrip(ctx, &(data->filter.query)); + + cache_key_size = + data->table.length + 1 + + data->filter.match_columns.length + 1 + + data->filter.query.length + 1 + + data->filter.filter.length + 1 + + data->scorer.length + 1 + + data->sort_keys.length + 1 + + data->output_columns.length + 1 + + data->match_escalation_threshold.length + 1 + + data->filter.query_expander.length + 1 + + data->filter.query_flags.length + 1 + + data->adjuster.length + 1 + + sizeof(grn_content_type) + + sizeof(int) * 2 + + sizeof(grn_command_version) + + sizeof(grn_bool); + if (data->slices) { + GRN_HASH_EACH_BEGIN(ctx, data->slices, cursor, id) { + grn_slice_data *slice; + grn_hash_cursor_get_value(ctx, cursor, (void **)&slice); + grn_raw_string_lstrip(ctx, &(slice->filter.query)); + cache_key_size += + slice->filter.match_columns.length + 1 + + slice->filter.query.length + 1 + + slice->filter.query_expander.length + 1 + + slice->filter.query_flags.length + 1 + + slice->filter.filter.length + 1 + + slice->sort_keys.length + 1 + + slice->output_columns.length + 1 + + slice->label.length + 1 + + sizeof(int) * 2; + } GRN_HASH_EACH_END(ctx, cursor); + } +#define DRILLDOWN_CACHE_SIZE(drilldown) \ + drilldown->keys.length + 1 + \ + drilldown->sort_keys.length + 1 + \ + drilldown->output_columns.length + 1 + \ + drilldown->label.length + 1 + \ + drilldown->calc_target_name.length + 1 + \ + drilldown->filter.length + 1 + \ + drilldown->table_name.length + 1 + \ + sizeof(int) * 2 + \ + sizeof(grn_table_group_flags) + if (data->drilldown.keys.length > 0) { + grn_drilldown_data *drilldown = &(data->drilldown); + cache_key_size += DRILLDOWN_CACHE_SIZE(drilldown); + } + if (data->drilldowns) { + GRN_HASH_EACH_BEGIN(ctx, data->drilldowns, cursor, id) { + grn_drilldown_data *drilldown; + grn_hash_cursor_get_value(ctx, cursor, (void **)&drilldown); + cache_key_size += DRILLDOWN_CACHE_SIZE(drilldown); + } GRN_HASH_EACH_END(ctx, cursor); + } +#undef DRILLDOWN_CACHE_SIZE + if (cache_key_size <= GRN_CACHE_MAX_KEY_SIZE) { + char *cp = cache_key; + +#define PUT_CACHE_KEY(string) \ + if ((string).value) \ + grn_memcpy(cp, (string).value, (string).length); \ + cp += (string).length; \ + *cp++ = '\0' + + PUT_CACHE_KEY(data->table); + PUT_CACHE_KEY(data->filter.match_columns); + PUT_CACHE_KEY(data->filter.query); + PUT_CACHE_KEY(data->filter.filter); + PUT_CACHE_KEY(data->scorer); + PUT_CACHE_KEY(data->sort_keys); + PUT_CACHE_KEY(data->output_columns); + if (data->slices) { + GRN_HASH_EACH_BEGIN(ctx, data->slices, cursor, id) { + grn_slice_data *slice; + grn_hash_cursor_get_value(ctx, cursor, (void **)&slice); + PUT_CACHE_KEY(slice->filter.match_columns); + PUT_CACHE_KEY(slice->filter.query); + PUT_CACHE_KEY(slice->filter.query_expander); + PUT_CACHE_KEY(slice->filter.query_flags); + PUT_CACHE_KEY(slice->filter.filter); + PUT_CACHE_KEY(slice->sort_keys); + PUT_CACHE_KEY(slice->output_columns); + PUT_CACHE_KEY(slice->label); + grn_memcpy(cp, &(slice->offset), sizeof(int)); + cp += sizeof(int); + grn_memcpy(cp, &(slice->limit), sizeof(int)); + cp += sizeof(int); + } GRN_HASH_EACH_END(ctx, cursor); + } +#define PUT_CACHE_KEY_DRILLDOWN(drilldown) do { \ + PUT_CACHE_KEY(drilldown->keys); \ + PUT_CACHE_KEY(drilldown->sort_keys); \ + PUT_CACHE_KEY(drilldown->output_columns); \ + PUT_CACHE_KEY(drilldown->label); \ + PUT_CACHE_KEY(drilldown->calc_target_name); \ + PUT_CACHE_KEY(drilldown->filter); \ + PUT_CACHE_KEY(drilldown->table_name); \ + grn_memcpy(cp, &(drilldown->offset), sizeof(int)); \ + cp += sizeof(int); \ + grn_memcpy(cp, &(drilldown->limit), sizeof(int)); \ + cp += sizeof(int); \ + grn_memcpy(cp, \ + &(drilldown->calc_types), \ + sizeof(grn_table_group_flags)); \ + cp += sizeof(grn_table_group_flags); \ + } while (GRN_FALSE) + if (data->drilldown.keys.length > 0) { + grn_drilldown_data *drilldown = &(data->drilldown); + PUT_CACHE_KEY_DRILLDOWN(drilldown); + } + if (data->drilldowns) { + GRN_HASH_EACH_BEGIN(ctx, data->drilldowns, cursor, id) { + grn_drilldown_data *drilldown; + grn_hash_cursor_get_value(ctx, cursor, (void **)&drilldown); + PUT_CACHE_KEY_DRILLDOWN(drilldown); + } GRN_HASH_EACH_END(ctx, cursor); + } +#undef PUT_CACHE_KEY_DRILLDOWN + PUT_CACHE_KEY(data->match_escalation_threshold); + PUT_CACHE_KEY(data->filter.query_expander); + PUT_CACHE_KEY(data->filter.query_flags); + PUT_CACHE_KEY(data->adjuster); + grn_memcpy(cp, &output_type, sizeof(grn_content_type)); + cp += sizeof(grn_content_type); + grn_memcpy(cp, &(data->offset), sizeof(int)); + cp += sizeof(int); + grn_memcpy(cp, &(data->limit), sizeof(int)); + cp += sizeof(int); + grn_memcpy(cp, &(ctx->impl->command.version), sizeof(grn_command_version)); + cp += sizeof(grn_command_version); + grn_memcpy(cp, &(ctx->impl->output.is_pretty), sizeof(grn_bool)); + cp += sizeof(grn_bool); +#undef PUT_CACHE_KEY + + { + grn_rc rc; + rc = grn_cache_fetch(ctx, cache_obj, cache_key, cache_key_size, outbuf); + if (rc == GRN_SUCCESS) { + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_CACHE, + ":", "cache(%" GRN_FMT_LLD ")", + (long long int)GRN_TEXT_LEN(outbuf)); + return ctx->rc; + } + } + } + if (data->match_escalation_threshold.length) { + const char *end, *rest; + original_threshold = grn_ctx_get_match_escalation_threshold(ctx); + end = + data->match_escalation_threshold.value + + data->match_escalation_threshold.length; + threshold = grn_atoll(data->match_escalation_threshold.value, end, &rest); + if (end == rest) { + grn_ctx_set_match_escalation_threshold(ctx, threshold); + } + } + + data->tables.target = grn_ctx_get(ctx, data->table.value, data->table.length); + if (!data->tables.target) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][table] invalid name: <%.*s>", + (int)(data->table.length), + data->table.value); + goto exit; + } + + { + if (data->filter.filter.length > 0 && + (data->filter.filter.value[0] == '?') && + (ctx->impl->output.type == GRN_CONTENT_JSON)) { + ctx->rc = grn_ts_select(ctx, data->tables.target, + data->filter.filter.value + 1, + data->filter.filter.length - 1, + data->scorer.value, + data->scorer.length, + data->sort_keys.value, + data->sort_keys.length, + data->output_columns.value, + data->output_columns.length, + data->offset, + data->limit); + if (!ctx->rc && + data->cacheable > 0 && + cache_key_size <= GRN_CACHE_MAX_KEY_SIZE && + (!data->cache.value || + data->cache.length != 2 || + data->cache.value[0] != 'n' || + data->cache.value[1] != 'o')) { + grn_cache_update(ctx, cache_obj, cache_key, cache_key_size, outbuf); + } + goto exit; + } + + data->tables.initial = data->tables.target; + if (!grn_select_apply_initial_columns(ctx, data)) { + goto exit; + } + + if (!grn_select_filter(ctx, data)) { + goto exit; + } + + nhits = grn_table_size(ctx, data->tables.result); + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "select(%d)", nhits); + + if (!grn_select_apply_filtered_columns(ctx, data)) { + goto exit; + } + + { + grn_bool succeeded; + + /* For select results */ + data->output.n_elements = 1; + + if (!grn_select_apply_adjuster(ctx, data)) { + goto exit; + } + + if (!grn_select_apply_scorer(ctx, data)) { + goto exit; + } + + grn_normalize_offset_and_limit(ctx, nhits, + &(data->offset), &(data->limit)); + + if (!grn_select_sort(ctx, data)) { + goto exit; + } + + if (!grn_select_apply_output_columns(ctx, data)) { + goto exit; + } + + if (!grn_select_prepare_slices(ctx, data)) { + goto exit; + } + + if (!grn_select_prepare_drilldowns(ctx, data)) { + goto exit; + } + + succeeded = grn_select_output(ctx, data); + if (!succeeded) { + goto exit; + } + } + if (!ctx->rc && + data->cacheable && + cache_key_size <= GRN_CACHE_MAX_KEY_SIZE && + (!data->cache.value || + data->cache.length != 2 || + data->cache.value[0] != 'n' || + data->cache.value[1] != 'o')) { + grn_cache_update(ctx, cache_obj, cache_key, cache_key_size, outbuf); + } + if (data->taintable > 0) { + grn_db_touch(ctx, DB_OBJ(data->tables.target)->db); + } + } + +exit : + if (data->match_escalation_threshold.length > 0) { + grn_ctx_set_match_escalation_threshold(ctx, original_threshold); + } + + /* GRN_LOG(ctx, GRN_LOG_NONE, "%d", ctx->seqno); */ + + return ctx->rc; +} + +static grn_slice_data * +grn_select_data_slices_add(grn_ctx *ctx, + grn_select_data *data, + const char *label, + size_t label_len) +{ + grn_slice_data *slice = NULL; + int added; + + if (!data->slices) { + data->slices = grn_hash_create(ctx, + NULL, + GRN_TABLE_MAX_KEY_SIZE, + sizeof(grn_slice_data), + GRN_OBJ_TABLE_HASH_KEY | + GRN_OBJ_KEY_VAR_SIZE | + GRN_HASH_TINY); + if (!data->slices) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][slices] " + "failed to allocate slices data: %s", + ctx->errbuf); + return NULL; + } + } + + grn_hash_add(ctx, + data->slices, + label, + label_len, + (void **)&slice, + &added); + if (added) { + grn_slice_data_init(ctx, slice, label, label_len); + } + + return slice; +} + +static grn_bool +grn_select_data_fill_slice_labels(grn_ctx *ctx, + grn_user_data *user_data, + grn_select_data *data) +{ + grn_obj *vars; + grn_table_cursor *cursor; + const char *prefix = "slices["; + int prefix_len; + + vars = grn_plugin_proc_get_vars(ctx, user_data); + + cursor = grn_table_cursor_open(ctx, vars, NULL, 0, NULL, 0, 0, -1, 0); + if (!cursor) { + return GRN_FALSE; + } + + prefix_len = strlen(prefix); + while (grn_table_cursor_next(ctx, cursor)) { + void *key; + char *name; + int name_len; + name_len = grn_table_cursor_get_key(ctx, cursor, &key); + name = key; + if (name_len > prefix_len + 1 && + strncmp(prefix, name, prefix_len) == 0) { + const char *label_end; + size_t label_len; + label_end = memchr(name + prefix_len + 1, + ']', + name_len - prefix_len - 1); + if (!label_end) { + continue; + } + label_len = (label_end - name) - prefix_len; + grn_select_data_slices_add(ctx, + data, + name + prefix_len, + label_len); + } + } + grn_table_cursor_close(ctx, cursor); + + return GRN_TRUE; +} + +static grn_bool +grn_select_data_fill_slices(grn_ctx *ctx, + grn_user_data *user_data, + grn_select_data *data) +{ + if (!grn_select_data_fill_slice_labels(ctx, user_data, data)) { + return GRN_FALSE; + } + + GRN_HASH_EACH_BEGIN(ctx, data->slices, cursor, id) { + grn_slice_data *slice; + char slice_label[GRN_TABLE_MAX_KEY_SIZE]; + char key_name[GRN_TABLE_MAX_KEY_SIZE]; + grn_obj *match_columns; + grn_obj *query; + grn_obj *query_expander; + grn_obj *query_flags; + grn_obj *filter; + grn_obj *sort_keys; + grn_obj *output_columns; + grn_obj *offset; + grn_obj *limit; + + grn_hash_cursor_get_value(ctx, cursor, (void **)&slice); + + grn_snprintf(slice_label, + GRN_TABLE_MAX_KEY_SIZE, + GRN_TABLE_MAX_KEY_SIZE, + "slices[%.*s].", + (int)(slice->label.length), + slice->label.value); + +#define GET_VAR(name) \ + grn_snprintf(key_name, \ + GRN_TABLE_MAX_KEY_SIZE, \ + GRN_TABLE_MAX_KEY_SIZE, \ + "%s%s", slice_label, #name); \ + name = grn_plugin_proc_get_var(ctx, user_data, key_name, -1); + + GET_VAR(match_columns); + GET_VAR(query); + GET_VAR(query_expander); + GET_VAR(query_flags); + GET_VAR(filter); + GET_VAR(sort_keys); + GET_VAR(output_columns); + GET_VAR(offset); + GET_VAR(limit); + +#undef GET_VAR + + grn_slice_data_fill(ctx, + slice, + match_columns, + query, + query_expander, + query_flags, + filter, + sort_keys, + output_columns, + offset, + limit); + } GRN_HASH_EACH_END(ctx, cursor); + + return GRN_TRUE; +} + +static grn_bool +grn_select_data_fill_drilldown_labels(grn_ctx *ctx, + grn_user_data *user_data, + grn_select_data *data, + const char *prefix) +{ + grn_obj *vars; + grn_table_cursor *cursor; + int prefix_len; + + vars = grn_plugin_proc_get_vars(ctx, user_data); + + cursor = grn_table_cursor_open(ctx, vars, NULL, 0, NULL, 0, 0, -1, 0); + if (!cursor) { + return GRN_FALSE; + } + + prefix_len = strlen(prefix); + while (grn_table_cursor_next(ctx, cursor)) { + void *key; + char *name; + int name_len; + name_len = grn_table_cursor_get_key(ctx, cursor, &key); + name = key; + if (name_len > prefix_len + 1 && + strncmp(prefix, name, prefix_len) == 0) { + const char *label_end; + size_t label_len; + label_end = memchr(name + prefix_len + 1, + ']', + name_len - prefix_len - 1); + if (!label_end) { + continue; + } + label_len = (label_end - name) - prefix_len; + grn_select_data_drilldowns_add(ctx, + data, + name + prefix_len, + label_len); + } + } + grn_table_cursor_close(ctx, cursor); + + return GRN_TRUE; +} + +static grn_bool +grn_select_data_fill_drilldown_columns(grn_ctx *ctx, + grn_user_data *user_data, + grn_drilldown_data *drilldown, + const char *parameter_key) +{ + char prefix[GRN_TABLE_MAX_KEY_SIZE]; + + grn_snprintf(prefix, + GRN_TABLE_MAX_KEY_SIZE, + GRN_TABLE_MAX_KEY_SIZE, + "%s[%.*s].", + parameter_key, + (int)(drilldown->label.length), + drilldown->label.value); + return grn_columns_fill(ctx, + user_data, + &(drilldown->columns), + prefix, + strlen(prefix)); +} + +static grn_bool +grn_select_data_fill_drilldowns(grn_ctx *ctx, + grn_user_data *user_data, + grn_select_data *data) +{ + grn_obj *drilldown; + + drilldown = grn_plugin_proc_get_var(ctx, user_data, "drilldown", -1); + if (GRN_TEXT_LEN(drilldown) > 0) { + grn_obj *sort_keys; + + sort_keys = grn_plugin_proc_get_var(ctx, user_data, + "drilldown_sort_keys", -1); + if (GRN_TEXT_LEN(sort_keys) == 0) { + /* For backward compatibility */ + sort_keys = grn_plugin_proc_get_var(ctx, user_data, + "drilldown_sortby", -1); + } + grn_drilldown_data_fill(ctx, + &(data->drilldown), + drilldown, + sort_keys, + grn_plugin_proc_get_var(ctx, user_data, + "drilldown_output_columns", + -1), + grn_plugin_proc_get_var(ctx, user_data, + "drilldown_offset", -1), + grn_plugin_proc_get_var(ctx, user_data, + "drilldown_limit", -1), + grn_plugin_proc_get_var(ctx, user_data, + "drilldown_calc_types", -1), + grn_plugin_proc_get_var(ctx, user_data, + "drilldown_calc_target", -1), + grn_plugin_proc_get_var(ctx, user_data, + "drilldown_filter", -1), + NULL); + return GRN_TRUE; + } else { + grn_bool succeeded = GRN_TRUE; + + if (!grn_select_data_fill_drilldown_labels(ctx, user_data, data, + "drilldowns[")) { + return GRN_FALSE; + } + + /* For backward compatibility */ + if (!grn_select_data_fill_drilldown_labels(ctx, user_data, data, + "drilldown[")) { + return GRN_FALSE; + } + + GRN_HASH_EACH_BEGIN(ctx, data->drilldowns, cursor, id) { + grn_drilldown_data *drilldown; + grn_obj *keys = NULL; + grn_obj *sort_keys = NULL; + grn_obj *output_columns = NULL; + grn_obj *offset = NULL; + grn_obj *limit = NULL; + grn_obj *calc_types = NULL; + grn_obj *calc_target = NULL; + grn_obj *filter = NULL; + grn_obj *table = NULL; + + grn_hash_cursor_get_value(ctx, cursor, (void **)&drilldown); + + succeeded = grn_select_data_fill_drilldown_columns(ctx, + user_data, + drilldown, + "drilldowns"); + if (!succeeded) { + break; + } + + /* For backward compatibility */ + succeeded = grn_select_data_fill_drilldown_columns(ctx, + user_data, + drilldown, + "drilldown"); + if (!succeeded) { + break; + } + +#define GET_VAR_RAW(parameter_key, name) do { \ + if (!name) { \ + char key_name[GRN_TABLE_MAX_KEY_SIZE]; \ + grn_snprintf(key_name, \ + GRN_TABLE_MAX_KEY_SIZE, \ + GRN_TABLE_MAX_KEY_SIZE, \ + "%s[%.*s].%s", \ + (parameter_key), \ + (int)(drilldown->label.length), \ + drilldown->label.value, \ + #name); \ + name = grn_plugin_proc_get_var(ctx, user_data, key_name, -1); \ + } \ + } while (GRN_FALSE) + +#define GET_VAR(name) do { \ + GET_VAR_RAW("drilldowns", name); \ + /* For backward compatibility */ \ + GET_VAR_RAW("drilldown", name); \ + } while (GRN_FALSE) + + GET_VAR(keys); + GET_VAR(sort_keys); + if (!sort_keys) { + grn_obj *sortby = NULL; + GET_VAR(sortby); + sort_keys = sortby; + } + GET_VAR(output_columns); + GET_VAR(offset); + GET_VAR(limit); + GET_VAR(calc_types); + GET_VAR(calc_target); + GET_VAR(filter); + GET_VAR(table); + +#undef GET_VAR + +#undef GET_VAR_RAW + + grn_drilldown_data_fill(ctx, + drilldown, + keys, + sort_keys, + output_columns, + offset, + limit, + calc_types, + calc_target, + filter, + table); + } GRN_HASH_EACH_END(ctx, cursor); + + return succeeded; + } +} + +static grn_obj * +command_select(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_select_data data; + + grn_columns_init(ctx, &(data.columns)); + grn_filter_data_init(ctx, &(data.filter)); + + data.tables.target = NULL; + data.tables.initial = NULL; + data.tables.result = NULL; + data.tables.sorted = NULL; + + data.slices = NULL; + grn_drilldown_data_init(ctx, &(data.drilldown), NULL, 0); + data.drilldowns = NULL; + + data.table.value = grn_plugin_proc_get_var_string(ctx, user_data, + "table", -1, + &(data.table.length)); +#define GET_VAR(name) \ + grn_plugin_proc_get_var(ctx, user_data, name, strlen(name)) + + { + grn_obj *query_expander; + + query_expander = GET_VAR("query_expander"); + if (GRN_TEXT_LEN(query_expander) == 0) { + query_expander = GET_VAR("query_expansion"); + } + + grn_filter_data_fill(ctx, + &(data.filter), + GET_VAR("match_columns"), + GET_VAR("query"), + query_expander, + GET_VAR("query_flags"), + GET_VAR("filter")); + } +#undef GET_VAR + + data.scorer.value = + grn_plugin_proc_get_var_string(ctx, user_data, + "scorer", -1, + &(data.scorer.length)); + data.sort_keys.value = + grn_plugin_proc_get_var_string(ctx, user_data, + "sort_keys", -1, + &(data.sort_keys.length)); + if (data.sort_keys.length == 0) { + /* For backward compatibility */ + data.sort_keys.value = + grn_plugin_proc_get_var_string(ctx, user_data, + "sortby", -1, + &(data.sort_keys.length)); + } + data.output_columns.value = + grn_plugin_proc_get_var_string(ctx, user_data, + "output_columns", -1, + &(data.output_columns.length)); + if (!data.output_columns.value) { + data.output_columns.value = GRN_SELECT_DEFAULT_OUTPUT_COLUMNS; + data.output_columns.length = strlen(GRN_SELECT_DEFAULT_OUTPUT_COLUMNS); + } + data.offset = grn_plugin_proc_get_var_int32(ctx, user_data, + "offset", -1, + 0); + data.limit = grn_plugin_proc_get_var_int32(ctx, user_data, + "limit", -1, + GRN_SELECT_DEFAULT_LIMIT); + + data.cache.value = grn_plugin_proc_get_var_string(ctx, user_data, + "cache", -1, + &(data.cache.length)); + data.match_escalation_threshold.value = + grn_plugin_proc_get_var_string(ctx, user_data, + "match_escalation_threshold", -1, + &(data.match_escalation_threshold.length)); + + data.adjuster.value = + grn_plugin_proc_get_var_string(ctx, user_data, + "adjuster", -1, + &(data.adjuster.length)); + + if (!grn_select_data_fill_slices(ctx, user_data, &data)) { + goto exit; + } + + if (!grn_select_data_fill_drilldowns(ctx, user_data, &data)) { + goto exit; + } + + if (!grn_columns_fill(ctx, user_data, &(data.columns), NULL, 0)) { + goto exit; + } + + grn_select(ctx, &data); + +exit : + if (data.drilldowns) { + GRN_HASH_EACH_BEGIN(ctx, data.drilldowns, cursor, id) { + grn_drilldown_data *drilldown; + grn_hash_cursor_get_value(ctx, cursor, (void **)&drilldown); + grn_drilldown_data_fin(ctx, drilldown); + } GRN_HASH_EACH_END(ctx, cursor); + grn_hash_close(ctx, data.drilldowns); + } + + if (data.drilldown.parsed_keys) { + grn_table_sort_key_close(ctx, + data.drilldown.parsed_keys, + data.drilldown.n_parsed_keys); + } + grn_drilldown_data_fin(ctx, &(data.drilldown)); + + if (data.slices) { + GRN_HASH_EACH_BEGIN(ctx, data.slices, cursor, id) { + grn_slice_data *slice; + grn_hash_cursor_get_value(ctx, cursor, (void **)&slice); + grn_slice_data_fin(ctx, slice); + } GRN_HASH_EACH_END(ctx, cursor); + grn_hash_close(ctx, data.slices); + } + + if (data.tables.sorted) { + grn_obj_unlink(ctx, data.tables.sorted); + } + + if (data.tables.result == data.filter.filtered) { + data.tables.result = NULL; + } + grn_filter_data_fin(ctx, &(data.filter)); + + if (data.tables.result && + data.tables.result != data.tables.initial && + data.tables.result != data.tables.target) { + grn_obj_unlink(ctx, data.tables.result); + } + + if (data.tables.initial && data.tables.initial != data.tables.target) { + grn_obj_unlink(ctx, data.tables.initial); + } + + if (data.tables.target) { + grn_obj_unlink(ctx, data.tables.target); + } + + grn_columns_fin(ctx, &(data.columns)); + + return NULL; +} + +#define N_VARS 26 +#define DEFINE_VARS grn_expr_var vars[N_VARS] + +static void +init_vars(grn_ctx *ctx, grn_expr_var *vars) +{ + grn_plugin_expr_var_init(ctx, &(vars[0]), "name", -1); + grn_plugin_expr_var_init(ctx, &(vars[1]), "table", -1); + grn_plugin_expr_var_init(ctx, &(vars[2]), "match_columns", -1); + grn_plugin_expr_var_init(ctx, &(vars[3]), "query", -1); + grn_plugin_expr_var_init(ctx, &(vars[4]), "filter", -1); + grn_plugin_expr_var_init(ctx, &(vars[5]), "scorer", -1); + /* Deprecated since 6.0.3. Use sort_keys instead. */ + grn_plugin_expr_var_init(ctx, &(vars[6]), "sortby", -1); + grn_plugin_expr_var_init(ctx, &(vars[7]), "output_columns", -1); + grn_plugin_expr_var_init(ctx, &(vars[8]), "offset", -1); + grn_plugin_expr_var_init(ctx, &(vars[9]), "limit", -1); + grn_plugin_expr_var_init(ctx, &(vars[10]), "drilldown", -1); + /* Deprecated since 6.0.3. Use drilldown_sort_keys instead. */ + grn_plugin_expr_var_init(ctx, &(vars[11]), "drilldown_sortby", -1); + grn_plugin_expr_var_init(ctx, &(vars[12]), "drilldown_output_columns", -1); + grn_plugin_expr_var_init(ctx, &(vars[13]), "drilldown_offset", -1); + grn_plugin_expr_var_init(ctx, &(vars[14]), "drilldown_limit", -1); + grn_plugin_expr_var_init(ctx, &(vars[15]), "cache", -1); + grn_plugin_expr_var_init(ctx, &(vars[16]), "match_escalation_threshold", -1); + /* Deprecated. Use query_expander instead. */ + grn_plugin_expr_var_init(ctx, &(vars[17]), "query_expansion", -1); + grn_plugin_expr_var_init(ctx, &(vars[18]), "query_flags", -1); + grn_plugin_expr_var_init(ctx, &(vars[19]), "query_expander", -1); + grn_plugin_expr_var_init(ctx, &(vars[20]), "adjuster", -1); + grn_plugin_expr_var_init(ctx, &(vars[21]), "drilldown_calc_types", -1); + grn_plugin_expr_var_init(ctx, &(vars[22]), "drilldown_calc_target", -1); + grn_plugin_expr_var_init(ctx, &(vars[23]), "drilldown_filter", -1); + grn_plugin_expr_var_init(ctx, &(vars[24]), "sort_keys", -1); + grn_plugin_expr_var_init(ctx, &(vars[25]), "drilldown_sort_keys", -1); +} + +void +grn_proc_init_select(grn_ctx *ctx) +{ + DEFINE_VARS; + + init_vars(ctx, vars); + grn_plugin_command_create(ctx, + "select", -1, + command_select, + N_VARS - 1, + vars + 1); +} + +static grn_obj * +command_define_selector(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + uint32_t i, nvars; + grn_expr_var *vars; + + grn_proc_get_info(ctx, user_data, &vars, &nvars, NULL); + for (i = 1; i < nvars; i++) { + grn_obj *var; + var = grn_plugin_proc_get_var_by_offset(ctx, user_data, i); + GRN_TEXT_SET(ctx, &((vars + i)->value), + GRN_TEXT_VALUE(var), + GRN_TEXT_LEN(var)); + } + { + grn_obj *name; + name = grn_plugin_proc_get_var(ctx, user_data, "name", -1); + grn_plugin_command_create(ctx, + GRN_TEXT_VALUE(name), + GRN_TEXT_LEN(name), + command_select, + nvars - 1, + vars + 1); + } + GRN_OUTPUT_BOOL(!ctx->rc); + + return NULL; +} + +void +grn_proc_init_define_selector(grn_ctx *ctx) +{ + DEFINE_VARS; + + init_vars(ctx, vars); + grn_plugin_command_create(ctx, + "define_selector", -1, + command_define_selector, + N_VARS, + vars); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_snippet.c b/storage/mroonga/vendor/groonga/lib/proc/proc_snippet.c new file mode 100644 index 00000000..0c6ea681 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_snippet.c @@ -0,0 +1,319 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2009-2016 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_proc.h" +#include "../grn_expr.h" + +#include <groonga/plugin.h> +#include <string.h> + +#define GRN_FUNC_SNIPPET_HTML_CACHE_NAME "$snippet_html" + +static grn_obj * +snippet_exec(grn_ctx *ctx, grn_obj *snip, grn_obj *text, + grn_user_data *user_data, + const char *prefix, int prefix_length, + const char *suffix, int suffix_length) +{ + grn_rc rc; + unsigned int i, n_results, max_tagged_length; + grn_obj snippet_buffer; + grn_obj *snippets; + + if (GRN_TEXT_LEN(text) == 0) { + return NULL; + } + + rc = grn_snip_exec(ctx, snip, + GRN_TEXT_VALUE(text), GRN_TEXT_LEN(text), + &n_results, &max_tagged_length); + if (rc != GRN_SUCCESS) { + return NULL; + } + + if (n_results == 0) { + return grn_plugin_proc_alloc(ctx, user_data, GRN_DB_VOID, 0); + } + + snippets = grn_plugin_proc_alloc(ctx, user_data, GRN_DB_SHORT_TEXT, GRN_OBJ_VECTOR); + if (!snippets) { + return NULL; + } + + GRN_TEXT_INIT(&snippet_buffer, 0); + grn_bulk_space(ctx, &snippet_buffer, + prefix_length + max_tagged_length + suffix_length); + for (i = 0; i < n_results; i++) { + unsigned int snippet_length; + + GRN_BULK_REWIND(&snippet_buffer); + if (prefix_length) { + GRN_TEXT_PUT(ctx, &snippet_buffer, prefix, prefix_length); + } + rc = grn_snip_get_result(ctx, snip, i, + GRN_TEXT_VALUE(&snippet_buffer) + prefix_length, + &snippet_length); + if (rc == GRN_SUCCESS) { + grn_strncat(GRN_TEXT_VALUE(&snippet_buffer), + GRN_BULK_WSIZE(&snippet_buffer), + suffix, + suffix_length); + grn_vector_add_element(ctx, snippets, + GRN_TEXT_VALUE(&snippet_buffer), + prefix_length + snippet_length + suffix_length, + 0, GRN_DB_SHORT_TEXT); + } + } + GRN_OBJ_FIN(ctx, &snippet_buffer); + + return snippets; +} + +/* TODO: support caching for the same parameter. */ +static grn_obj * +func_snippet(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *snippets = NULL; + +#define N_REQUIRED_ARGS 1 +#define KEYWORD_SET_SIZE 3 + if (nargs > N_REQUIRED_ARGS) { + grn_obj *text = args[0]; + grn_obj *end_arg = args[nargs - 1]; + grn_obj *snip = NULL; + unsigned int width = 200; + unsigned int max_n_results = 3; + grn_snip_mapping *mapping = NULL; + int flags = GRN_SNIP_SKIP_LEADING_SPACES; + const char *prefix = NULL; + int prefix_length = 0; + const char *suffix = NULL; + int suffix_length = 0; + const char *normalizer_name = NULL; + int normalizer_name_length = 0; + const char *default_open_tag = NULL; + int default_open_tag_length = 0; + const char *default_close_tag = NULL; + int default_close_tag_length = 0; + int n_args_without_option = nargs; + + if (end_arg->header.type == GRN_TABLE_HASH_KEY) { + grn_obj *options = end_arg; + grn_hash_cursor *cursor; + void *key; + int key_size; + grn_obj *value; + + n_args_without_option--; + cursor = grn_hash_cursor_open(ctx, (grn_hash *)options, + NULL, 0, NULL, 0, + 0, -1, 0); + if (!cursor) { + GRN_PLUGIN_ERROR(ctx, GRN_NO_MEMORY_AVAILABLE, + "snippet(): couldn't open cursor"); + goto exit; + } + while (grn_hash_cursor_next(ctx, cursor) != GRN_ID_NIL) { + grn_hash_cursor_get_key_value(ctx, cursor, + &key, &key_size, + (void **)&value); + if (key_size == 5 && !memcmp(key, "width", 5)) { + width = GRN_UINT32_VALUE(value); + } else if (key_size == 13 && !memcmp(key, "max_n_results", 13)) { + max_n_results = GRN_UINT32_VALUE(value); + } else if (key_size == 19 && !memcmp(key, "skip_leading_spaces", 19)) { + if (GRN_BOOL_VALUE(value) == GRN_FALSE) { + flags &= ~GRN_SNIP_SKIP_LEADING_SPACES; + } + } else if (key_size == 11 && !memcmp(key, "html_escape", 11)) { + if (GRN_BOOL_VALUE(value)) { + mapping = GRN_SNIP_MAPPING_HTML_ESCAPE; + } + } else if (key_size == 6 && !memcmp(key, "prefix", 6)) { + prefix = GRN_TEXT_VALUE(value); + prefix_length = GRN_TEXT_LEN(value); + } else if (key_size == 6 && !memcmp(key, "suffix", 6)) { + suffix = GRN_TEXT_VALUE(value); + suffix_length = GRN_TEXT_LEN(value); + } else if (key_size == 10 && !memcmp(key, "normalizer", 10)) { + normalizer_name = GRN_TEXT_VALUE(value); + normalizer_name_length = GRN_TEXT_LEN(value); + } else if (key_size == 16 && !memcmp(key, "default_open_tag", 16)) { + default_open_tag = GRN_TEXT_VALUE(value); + default_open_tag_length = GRN_TEXT_LEN(value); + } else if (key_size == 17 && !memcmp(key, "default_close_tag", 17)) { + default_close_tag = GRN_TEXT_VALUE(value); + default_close_tag_length = GRN_TEXT_LEN(value); + } else { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "invalid option name: <%.*s>", + key_size, (char *)key); + grn_hash_cursor_close(ctx, cursor); + goto exit; + } + } + grn_hash_cursor_close(ctx, cursor); + } + + snip = grn_snip_open(ctx, flags, width, max_n_results, + default_open_tag, default_open_tag_length, + default_close_tag, default_close_tag_length, mapping); + if (snip) { + grn_rc rc; + unsigned int i; + if (!normalizer_name) { + grn_snip_set_normalizer(ctx, snip, GRN_NORMALIZER_AUTO); + } else if (normalizer_name_length > 0) { + grn_obj *normalizer; + normalizer = grn_ctx_get(ctx, normalizer_name, normalizer_name_length); + if (!grn_obj_is_normalizer_proc(ctx, normalizer)) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, normalizer); + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "snippet(): not normalizer: <%.*s>", + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + grn_obj_unlink(ctx, normalizer); + goto exit; + } + grn_snip_set_normalizer(ctx, snip, normalizer); + grn_obj_unlink(ctx, normalizer); + } + if (default_open_tag_length == 0 && default_close_tag_length == 0) { + unsigned int n_keyword_sets = + (n_args_without_option - N_REQUIRED_ARGS) / KEYWORD_SET_SIZE; + grn_obj **keyword_set_args = args + N_REQUIRED_ARGS; + for (i = 0; i < n_keyword_sets; i++) { + rc = grn_snip_add_cond(ctx, snip, + GRN_TEXT_VALUE(keyword_set_args[i * KEYWORD_SET_SIZE]), + GRN_TEXT_LEN(keyword_set_args[i * KEYWORD_SET_SIZE]), + GRN_TEXT_VALUE(keyword_set_args[i * KEYWORD_SET_SIZE + 1]), + GRN_TEXT_LEN(keyword_set_args[i * KEYWORD_SET_SIZE + 1]), + GRN_TEXT_VALUE(keyword_set_args[i * KEYWORD_SET_SIZE + 2]), + GRN_TEXT_LEN(keyword_set_args[i * KEYWORD_SET_SIZE + 2])); + } + } else { + unsigned int n_keywords = n_args_without_option - N_REQUIRED_ARGS; + grn_obj **keyword_args = args + N_REQUIRED_ARGS; + for (i = 0; i < n_keywords; i++) { + rc = grn_snip_add_cond(ctx, snip, + GRN_TEXT_VALUE(keyword_args[i]), + GRN_TEXT_LEN(keyword_args[i]), + NULL, 0, + NULL, 0); + } + } + snippets = snippet_exec(ctx, snip, text, user_data, + prefix, prefix_length, + suffix, suffix_length); + } + } +#undef KEYWORD_SET_SIZE +#undef N_REQUIRED_ARGS + +exit : + if (!snippets) { + snippets = grn_plugin_proc_alloc(ctx, user_data, GRN_DB_VOID, 0); + } + + return snippets; +} + +void +grn_proc_init_snippet(grn_ctx *ctx) +{ + grn_proc_create(ctx, "snippet", -1, GRN_PROC_FUNCTION, + func_snippet, NULL, NULL, 0, NULL); +} + +static grn_obj * +func_snippet_html(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *snippets = NULL; + + /* TODO: support parameters */ + if (nargs == 1) { + grn_obj *text = args[0]; + grn_obj *expression = NULL; + grn_obj *condition_ptr = NULL; + grn_obj *condition = NULL; + grn_obj *snip = NULL; + int flags = GRN_SNIP_SKIP_LEADING_SPACES; + unsigned int width = 200; + unsigned int max_n_results = 3; + const char *open_tag = "<span class=\"keyword\">"; + const char *close_tag = "</span>"; + grn_snip_mapping *mapping = GRN_SNIP_MAPPING_HTML_ESCAPE; + + grn_proc_get_info(ctx, user_data, NULL, NULL, &expression); + condition_ptr = grn_expr_get_var(ctx, expression, + GRN_SELECT_INTERNAL_VAR_CONDITION, + strlen(GRN_SELECT_INTERNAL_VAR_CONDITION)); + if (condition_ptr) { + condition = GRN_PTR_VALUE(condition_ptr); + } + + if (condition) { + grn_obj *snip_ptr; + snip_ptr = grn_expr_get_var(ctx, expression, + GRN_FUNC_SNIPPET_HTML_CACHE_NAME, + strlen(GRN_FUNC_SNIPPET_HTML_CACHE_NAME)); + if (snip_ptr) { + snip = GRN_PTR_VALUE(snip_ptr); + } else { + snip_ptr = + grn_expr_get_or_add_var(ctx, expression, + GRN_FUNC_SNIPPET_HTML_CACHE_NAME, + strlen(GRN_FUNC_SNIPPET_HTML_CACHE_NAME)); + GRN_OBJ_FIN(ctx, snip_ptr); + GRN_PTR_INIT(snip_ptr, GRN_OBJ_OWN, GRN_DB_OBJECT); + + snip = grn_snip_open(ctx, flags, width, max_n_results, + open_tag, strlen(open_tag), + close_tag, strlen(close_tag), + mapping); + if (snip) { + grn_snip_set_normalizer(ctx, snip, GRN_NORMALIZER_AUTO); + grn_expr_snip_add_conditions(ctx, condition, snip, + 0, NULL, NULL, NULL, NULL); + GRN_PTR_SET(ctx, snip_ptr, snip); + } + } + } + + if (snip) { + snippets = snippet_exec(ctx, snip, text, user_data, NULL, 0, NULL, 0); + } + } + + if (!snippets) { + snippets = grn_plugin_proc_alloc(ctx, user_data, GRN_DB_VOID, 0); + } + + return snippets; +} + +void +grn_proc_init_snippet_html(grn_ctx *ctx) +{ + grn_proc_create(ctx, "snippet_html", -1, GRN_PROC_FUNCTION, + func_snippet_html, NULL, NULL, 0, NULL); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_table.c b/storage/mroonga/vendor/groonga/lib/proc/proc_table.c new file mode 100644 index 00000000..3c40992d --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_table.c @@ -0,0 +1,910 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2009-2016 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_proc.h" + +#include "../grn_ctx.h" +#include "../grn_str.h" +#include "../grn_db.h" + +#include <groonga/plugin.h> + +static grn_table_flags +command_table_create_parse_flags(grn_ctx *ctx, + const char *nptr, + const char *end) +{ + grn_table_flags flags = 0; + while (nptr < end) { + size_t name_size; + + if (*nptr == '|' || *nptr == ' ') { + nptr += 1; + continue; + } + +#define CHECK_FLAG(name) \ + name_size = strlen(#name); \ + if ((unsigned long) (end - nptr) >= (unsigned long) name_size && \ + memcmp(nptr, #name, name_size) == 0) { \ + flags |= GRN_OBJ_ ## name; \ + nptr += name_size; \ + continue; \ + } + + CHECK_FLAG(TABLE_HASH_KEY); + CHECK_FLAG(TABLE_PAT_KEY); + CHECK_FLAG(TABLE_DAT_KEY); + CHECK_FLAG(TABLE_NO_KEY); + CHECK_FLAG(KEY_NORMALIZE); + CHECK_FLAG(KEY_WITH_SIS); + CHECK_FLAG(KEY_LARGE); + +#undef CHECK_FLAG + + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[table][create][flags] unknown flag: <%.*s>", + (int)(end - nptr), nptr); + return 0; + } + return flags; +} + +static grn_bool +grn_proc_table_set_token_filters_put(grn_ctx *ctx, + grn_obj *token_filters, + const char *token_filter_name, + int token_filter_name_length) +{ + grn_obj *token_filter; + + token_filter = grn_ctx_get(ctx, + token_filter_name, + token_filter_name_length); + if (token_filter) { + GRN_PTR_PUT(ctx, token_filters, token_filter); + return GRN_TRUE; + } else { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[table][create][token-filter] " + "nonexistent token filter: <%.*s>", + token_filter_name_length, token_filter_name); + return GRN_FALSE; + } +} + +static grn_bool +grn_proc_table_set_token_filters_fill(grn_ctx *ctx, + grn_obj *token_filters, + grn_obj *token_filter_names) +{ + const char *start, *current, *end; + const char *name_start, *name_end; + const char *last_name_end; + + start = GRN_TEXT_VALUE(token_filter_names); + end = start + GRN_TEXT_LEN(token_filter_names); + current = start; + name_start = NULL; + name_end = NULL; + last_name_end = start; + while (current < end) { + switch (current[0]) { + case ' ' : + if (name_start && !name_end) { + name_end = current; + } + break; + case ',' : + if (!name_start) { + goto break_loop; + } + if (!name_end) { + name_end = current; + } + if (!grn_proc_table_set_token_filters_put(ctx, + token_filters, + name_start, + name_end - name_start)) { + return GRN_FALSE; + } + last_name_end = name_end + 1; + name_start = NULL; + name_end = NULL; + break; + default : + if (!name_start) { + name_start = current; + } + break; + } + current++; + } + +break_loop: + if (!name_start) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[table][create][token-filter] empty token filter name: " + "<%.*s|%.*s|%.*s>", + (int)(last_name_end - start), start, + (int)(current - last_name_end), last_name_end, + (int)(end - current), current); + return GRN_FALSE; + } + + if (!name_end) { + name_end = current; + } + grn_proc_table_set_token_filters_put(ctx, + token_filters, + name_start, + name_end - name_start); + + return GRN_TRUE; +} + +grn_bool +grn_proc_table_set_token_filters(grn_ctx *ctx, + grn_obj *table, + grn_obj *token_filter_names) +{ + grn_bool succeeded = GRN_FALSE; + grn_obj token_filters; + + if (GRN_TEXT_LEN(token_filter_names) == 0) { + return GRN_TRUE; + } + + GRN_PTR_INIT(&token_filters, GRN_OBJ_VECTOR, 0); + succeeded = grn_proc_table_set_token_filters_fill(ctx, + &token_filters, + token_filter_names); + if (succeeded) { + grn_obj_set_info(ctx, table, GRN_INFO_TOKEN_FILTERS, &token_filters); + } + grn_obj_unlink(ctx, &token_filters); + + return succeeded; +} + +static grn_obj * +command_table_create(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *name; + grn_obj *flags_raw; + grn_obj *key_type_name; + grn_obj *value_type_name; + grn_obj *default_tokenizer_name; + grn_obj *normalizer_name; + grn_obj *token_filters_name; + grn_obj *table; + const char *rest; + grn_table_flags flags; + + name = grn_plugin_proc_get_var(ctx, user_data, "name", -1); + flags_raw = grn_plugin_proc_get_var(ctx, user_data, "flags", -1); + key_type_name = grn_plugin_proc_get_var(ctx, user_data, "key_type", -1); + value_type_name = grn_plugin_proc_get_var(ctx, user_data, "value_type", -1); + default_tokenizer_name = + grn_plugin_proc_get_var(ctx, user_data, "default_tokenizer", -1); + normalizer_name = + grn_plugin_proc_get_var(ctx, user_data, "normalizer", -1); + token_filters_name = + grn_plugin_proc_get_var(ctx, user_data, "token_filters", -1); + + flags = grn_atoi(GRN_TEXT_VALUE(flags_raw), + GRN_BULK_CURR(flags_raw), + &rest); + + if (GRN_TEXT_VALUE(flags_raw) == rest) { + flags = command_table_create_parse_flags(ctx, + GRN_TEXT_VALUE(flags_raw), + GRN_BULK_CURR(flags_raw)); + if (ctx->rc) { goto exit; } + } + + if (GRN_TEXT_LEN(name) == 0) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[table][create] should not create anonymous table"); + goto exit; + } + + { + grn_obj *key_type = NULL; + grn_obj *value_type = NULL; + + if (GRN_TEXT_LEN(key_type_name) > 0) { + key_type = grn_ctx_get(ctx, + GRN_TEXT_VALUE(key_type_name), + GRN_TEXT_LEN(key_type_name)); + if (!key_type) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[table][create] " + "key type doesn't exist: <%.*s> (%.*s)", + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name), + (int)GRN_TEXT_LEN(key_type_name), + GRN_TEXT_VALUE(key_type_name)); + goto exit; + } + } + + if (GRN_TEXT_LEN(value_type_name) > 0) { + value_type = grn_ctx_get(ctx, + GRN_TEXT_VALUE(value_type_name), + GRN_TEXT_LEN(value_type_name)); + if (!value_type) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[table][create] " + "value type doesn't exist: <%.*s> (%.*s)", + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name), + (int)GRN_TEXT_LEN(value_type_name), + GRN_TEXT_VALUE(value_type_name)); + goto exit; + } + } + + flags |= GRN_OBJ_PERSISTENT; + table = grn_table_create(ctx, + GRN_TEXT_VALUE(name), + GRN_TEXT_LEN(name), + NULL, flags, + key_type, + value_type); + if (!table) { + goto exit; + } + + if (GRN_TEXT_LEN(default_tokenizer_name) > 0) { + grn_obj *default_tokenizer; + + default_tokenizer = + grn_ctx_get(ctx, + GRN_TEXT_VALUE(default_tokenizer_name), + GRN_TEXT_LEN(default_tokenizer_name)); + if (!default_tokenizer) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[table][create][%.*s] unknown tokenizer: <%.*s>", + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name), + (int)GRN_TEXT_LEN(default_tokenizer_name), + GRN_TEXT_VALUE(default_tokenizer_name)); + grn_obj_remove(ctx, table); + goto exit; + } + grn_obj_set_info(ctx, table, + GRN_INFO_DEFAULT_TOKENIZER, + default_tokenizer); + } + + if (GRN_TEXT_LEN(normalizer_name) > 0) { + grn_obj *normalizer; + + normalizer = + grn_ctx_get(ctx, + GRN_TEXT_VALUE(normalizer_name), + GRN_TEXT_LEN(normalizer_name)); + if (!normalizer) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[table][create][%.*s] unknown normalizer: <%.*s>", + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name), + (int)GRN_TEXT_LEN(normalizer_name), + GRN_TEXT_VALUE(normalizer_name)); + grn_obj_remove(ctx, table); + goto exit; + } + grn_obj_set_info(ctx, table, GRN_INFO_NORMALIZER, normalizer); + } + + if (!grn_proc_table_set_token_filters(ctx, table, token_filters_name)) { + grn_obj_remove(ctx, table); + goto exit; + } + + grn_obj_unlink(ctx, table); + } + +exit : + grn_ctx_output_bool(ctx, ctx->rc == GRN_SUCCESS); + return NULL; +} + +void +grn_proc_init_table_create(grn_ctx *ctx) +{ + grn_expr_var vars[7]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "name", -1); + grn_plugin_expr_var_init(ctx, &(vars[1]), "flags", -1); + grn_plugin_expr_var_init(ctx, &(vars[2]), "key_type", -1); + grn_plugin_expr_var_init(ctx, &(vars[3]), "value_type", -1); + grn_plugin_expr_var_init(ctx, &(vars[4]), "default_tokenizer", -1); + grn_plugin_expr_var_init(ctx, &(vars[5]), "normalizer", -1); + grn_plugin_expr_var_init(ctx, &(vars[6]), "token_filters", -1); + grn_plugin_command_create(ctx, + "table_create", -1, + command_table_create, + 7, + vars); +} + +static int +output_table_info(grn_ctx *ctx, grn_obj *table) +{ + grn_id id; + grn_obj o; + const char *path; + grn_table_flags flags; + grn_obj *default_tokenizer; + grn_obj *normalizer; + grn_obj *token_filters; + + id = grn_obj_id(ctx, table); + path = grn_obj_path(ctx, table); + GRN_TEXT_INIT(&o, 0); + grn_ctx_output_array_open(ctx, "TABLE", 8); + grn_ctx_output_int64(ctx, id); + grn_proc_output_object_id_name(ctx, id); + grn_ctx_output_cstr(ctx, path); + GRN_BULK_REWIND(&o); + + grn_table_get_info(ctx, table, + &flags, + NULL, + &default_tokenizer, + &normalizer, + &token_filters); + grn_dump_table_create_flags(ctx, flags, &o); + grn_ctx_output_obj(ctx, &o, NULL); + grn_proc_output_object_id_name(ctx, table->header.domain); + grn_proc_output_object_id_name(ctx, grn_obj_get_range(ctx, table)); + grn_proc_output_object_name(ctx, default_tokenizer); + grn_proc_output_object_name(ctx, normalizer); + grn_ctx_output_array_close(ctx); + GRN_OBJ_FIN(ctx, &o); + return 1; +} + +static grn_obj * +command_table_list(grn_ctx *ctx, int nargs, grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *db; + grn_obj tables; + int n_top_level_elements; + int n_elements_for_header = 1; + int n_tables; + int i; + + db = grn_ctx_db(ctx); + + { + grn_table_cursor *cursor; + grn_id id; + grn_obj *prefix; + const void *min = NULL; + unsigned int min_size = 0; + int flags = 0; + + prefix = grn_plugin_proc_get_var(ctx, user_data, "prefix", -1); + if (GRN_TEXT_LEN(prefix) > 0) { + min = GRN_TEXT_VALUE(prefix); + min_size = GRN_TEXT_LEN(prefix); + flags |= GRN_CURSOR_PREFIX; + } + cursor = grn_table_cursor_open(ctx, db, + min, min_size, + NULL, 0, + 0, -1, flags); + if (!cursor) { + return NULL; + } + + GRN_PTR_INIT(&tables, GRN_OBJ_VECTOR, GRN_ID_NIL); + while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) { + grn_obj *object; + const char *name; + void *key; + int i, key_size; + grn_bool have_period = GRN_FALSE; + + key_size = grn_table_cursor_get_key(ctx, cursor, &key); + name = key; + for (i = 0; i < key_size; i++) { + if (name[i] == '.') { + have_period = GRN_TRUE; + break; + } + } + if (have_period) { + continue; + } + + object = grn_ctx_at(ctx, id); + if (object) { + if (grn_obj_is_table(ctx, object)) { + GRN_PTR_PUT(ctx, &tables, object); + } else { + grn_obj_unlink(ctx, object); + } + } else { + if (ctx->rc != GRN_SUCCESS) { + ERRCLR(ctx); + } + } + } + grn_table_cursor_close(ctx, cursor); + } + n_tables = GRN_BULK_VSIZE(&tables) / sizeof(grn_obj *); + n_top_level_elements = n_elements_for_header + n_tables; + grn_ctx_output_array_open(ctx, "TABLE_LIST", n_top_level_elements); + + grn_ctx_output_array_open(ctx, "HEADER", 8); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "id"); + grn_ctx_output_cstr(ctx, "UInt32"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "name"); + grn_ctx_output_cstr(ctx, "ShortText"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "path"); + grn_ctx_output_cstr(ctx, "ShortText"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "flags"); + grn_ctx_output_cstr(ctx, "ShortText"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "domain"); + grn_ctx_output_cstr(ctx, "ShortText"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "range"); + grn_ctx_output_cstr(ctx, "ShortText"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "default_tokenizer"); + grn_ctx_output_cstr(ctx, "ShortText"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_open(ctx, "PROPERTY", 2); + grn_ctx_output_cstr(ctx, "normalizer"); + grn_ctx_output_cstr(ctx, "ShortText"); + grn_ctx_output_array_close(ctx); + grn_ctx_output_array_close(ctx); + + for (i = 0; i < n_tables; i++) { + grn_obj *table = GRN_PTR_VALUE_AT(&tables, i); + output_table_info(ctx, table); + grn_obj_unlink(ctx, table); + } + GRN_OBJ_FIN(ctx, &tables); + + grn_ctx_output_array_close(ctx); + + return NULL; +} + +void +grn_proc_init_table_list(grn_ctx *ctx) +{ + grn_expr_var vars[1]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "prefix", -1); + grn_plugin_command_create(ctx, + "table_list", -1, + command_table_list, + 1, + vars); +} + +static grn_obj * +command_table_remove(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + grn_obj *name; + grn_obj *table; + grn_bool dependent; + + name = grn_plugin_proc_get_var(ctx, user_data, "name", -1); + dependent = grn_plugin_proc_get_var_bool(ctx, user_data, "dependent", -1, + GRN_FALSE); + table = grn_ctx_get(ctx, + GRN_TEXT_VALUE(name), + GRN_TEXT_LEN(name)); + if (!table) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[table][remove] table isn't found: <%.*s>", + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name)); + grn_ctx_output_bool(ctx, GRN_FALSE); + return NULL; + } + + if (!grn_obj_is_table(ctx, table)) { + const char *type_name; + type_name = grn_obj_type_to_string(table->header.type); + grn_obj_unlink(ctx, table); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[table][remove] not table: <%.*s>: <%s>", + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name), + type_name); + grn_ctx_output_bool(ctx, GRN_FALSE); + return NULL; + } + + if (dependent) { + grn_obj_remove_dependent(ctx, table); + } else { + grn_obj_remove(ctx, table); + } + grn_ctx_output_bool(ctx, !ctx->rc); + return NULL; +} + +void +grn_proc_init_table_remove(grn_ctx *ctx) +{ + grn_expr_var vars[2]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "name", -1); + grn_plugin_expr_var_init(ctx, &(vars[1]), "dependent", -1); + grn_plugin_command_create(ctx, + "table_remove", -1, + command_table_remove, + 2, + vars); +} + +static grn_obj * +command_table_rename(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + grn_rc rc = GRN_SUCCESS; + grn_obj *name; + grn_obj *new_name; + grn_obj *table = NULL; + + name = grn_plugin_proc_get_var(ctx, user_data, "name", -1); + new_name = grn_plugin_proc_get_var(ctx, user_data, "new_name", -1); + if (GRN_TEXT_LEN(name) == 0) { + rc = GRN_INVALID_ARGUMENT; + GRN_PLUGIN_ERROR(ctx, rc, "[table][rename] table name isn't specified"); + goto exit; + } + table = grn_ctx_get(ctx, GRN_TEXT_VALUE(name), GRN_TEXT_LEN(name)); + if (!table) { + rc = GRN_INVALID_ARGUMENT; + GRN_PLUGIN_ERROR(ctx, + rc, + "[table][rename] table isn't found: <%.*s>", + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name)); + goto exit; + } + if (GRN_TEXT_LEN(new_name) == 0) { + rc = GRN_INVALID_ARGUMENT; + GRN_PLUGIN_ERROR(ctx, + rc, + "[table][rename] new table name isn't specified: <%.*s>", + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name)); + goto exit; + } + rc = grn_table_rename(ctx, table, + GRN_TEXT_VALUE(new_name), + GRN_TEXT_LEN(new_name)); + if (rc != GRN_SUCCESS && ctx->rc == GRN_SUCCESS) { + GRN_PLUGIN_ERROR(ctx, + rc, + "[table][rename] failed to rename: <%.*s> -> <%.*s>", + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name), + (int)GRN_TEXT_LEN(new_name), + GRN_TEXT_VALUE(new_name)); + } +exit : + grn_ctx_output_bool(ctx, !rc); + if (table) { grn_obj_unlink(ctx, table); } + return NULL; +} + +void +grn_proc_init_table_rename(grn_ctx *ctx) +{ + grn_expr_var vars[2]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "name", -1); + grn_plugin_expr_var_init(ctx, &(vars[1]), "new_name", -1); + grn_plugin_command_create(ctx, + "table_rename", -1, + command_table_rename, + 2, + vars); +} + +static grn_rc +command_table_copy_resolve_target(grn_ctx *ctx, + const char *label, + grn_obj *name, + grn_obj **table) +{ + if (GRN_TEXT_LEN(name) == 0) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[table][copy] %s name isn't specified", + label); + return ctx->rc; + } + *table = grn_ctx_get(ctx, + GRN_TEXT_VALUE(name), + GRN_TEXT_LEN(name)); + if (!*table) { + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[table][copy] %s table isn't found: <%.*s>", + label, + (int)GRN_TEXT_LEN(name), + GRN_TEXT_VALUE(name)); + return ctx->rc; + } + + return ctx->rc; +} + +static void +command_table_copy_same_key_type(grn_ctx *ctx, + grn_obj *from_table, + grn_obj *to_table, + grn_obj *from_name, + grn_obj *to_name) +{ + GRN_TABLE_EACH_BEGIN_FLAGS(ctx, from_table, cursor, from_id, + GRN_CURSOR_BY_KEY | GRN_CURSOR_ASCENDING) { + void *key; + int key_size; + grn_id to_id; + + key_size = grn_table_cursor_get_key(ctx, cursor, &key); + to_id = grn_table_add(ctx, to_table, key, key_size, NULL); + if (to_id == GRN_ID_NIL) { + grn_obj key_buffer; + grn_obj inspected_key; + if (from_table->header.domain == GRN_DB_SHORT_TEXT) { + GRN_SHORT_TEXT_INIT(&key_buffer, 0); + } else { + GRN_VALUE_FIX_SIZE_INIT(&key_buffer, 0, from_table->header.domain); + } + grn_bulk_write(ctx, &key_buffer, key, key_size); + GRN_TEXT_INIT(&inspected_key, 0); + grn_inspect(ctx, &inspected_key, &key_buffer); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[table][copy] failed to copy key: <%.*s>: " + "<%.*s> -> <%.*s>", + (int)GRN_TEXT_LEN(&inspected_key), + GRN_TEXT_VALUE(&inspected_key), + (int)GRN_TEXT_LEN(from_name), + GRN_TEXT_VALUE(from_name), + (int)GRN_TEXT_LEN(to_name), + GRN_TEXT_VALUE(to_name)); + GRN_OBJ_FIN(ctx, &inspected_key); + GRN_OBJ_FIN(ctx, &key_buffer); + break; + } + } GRN_TABLE_EACH_END(ctx, cursor); +} + +static void +command_table_copy_different(grn_ctx *ctx, + grn_obj *from_table, + grn_obj *to_table, + grn_obj *from_name, + grn_obj *to_name) +{ + grn_obj from_key_buffer; + grn_obj to_key_buffer; + + if (from_table->header.domain == GRN_DB_SHORT_TEXT) { + GRN_SHORT_TEXT_INIT(&from_key_buffer, 0); + } else { + GRN_VALUE_FIX_SIZE_INIT(&from_key_buffer, 0, from_table->header.domain); + } + if (to_table->header.domain == GRN_DB_SHORT_TEXT) { + GRN_SHORT_TEXT_INIT(&to_key_buffer, 0); + } else { + GRN_VALUE_FIX_SIZE_INIT(&to_key_buffer, 0, to_table->header.domain); + } + + GRN_TABLE_EACH_BEGIN_FLAGS(ctx, from_table, cursor, from_id, + GRN_CURSOR_BY_KEY | GRN_CURSOR_ASCENDING) { + void *key; + int key_size; + grn_rc cast_rc; + grn_id to_id; + + GRN_BULK_REWIND(&from_key_buffer); + GRN_BULK_REWIND(&to_key_buffer); + + key_size = grn_table_cursor_get_key(ctx, cursor, &key); + grn_bulk_write(ctx, &from_key_buffer, key, key_size); + cast_rc = grn_obj_cast(ctx, &from_key_buffer, &to_key_buffer, GRN_FALSE); + if (cast_rc != GRN_SUCCESS) { + grn_obj *to_key_type; + grn_obj inspected_key; + grn_obj inspected_to_key_type; + + to_key_type = grn_ctx_at(ctx, to_table->header.domain); + GRN_TEXT_INIT(&inspected_key, 0); + GRN_TEXT_INIT(&inspected_to_key_type, 0); + grn_inspect(ctx, &inspected_key, &from_key_buffer); + grn_inspect(ctx, &inspected_to_key_type, to_key_type); + ERR(cast_rc, + "[table][copy] failed to cast key: <%.*s> -> %.*s: " + "<%.*s> -> <%.*s>", + (int)GRN_TEXT_LEN(&inspected_key), + GRN_TEXT_VALUE(&inspected_key), + (int)GRN_TEXT_LEN(&inspected_to_key_type), + GRN_TEXT_VALUE(&inspected_to_key_type), + (int)GRN_TEXT_LEN(from_name), + GRN_TEXT_VALUE(from_name), + (int)GRN_TEXT_LEN(to_name), + GRN_TEXT_VALUE(to_name)); + GRN_OBJ_FIN(ctx, &inspected_key); + GRN_OBJ_FIN(ctx, &inspected_to_key_type); + break; + } + + to_id = grn_table_add(ctx, to_table, + GRN_BULK_HEAD(&to_key_buffer), + GRN_BULK_VSIZE(&to_key_buffer), + NULL); + if (to_id == GRN_ID_NIL) { + grn_obj inspected_from_key; + grn_obj inspected_to_key; + GRN_TEXT_INIT(&inspected_from_key, 0); + GRN_TEXT_INIT(&inspected_to_key, 0); + grn_inspect(ctx, &inspected_from_key, &from_key_buffer); + grn_inspect(ctx, &inspected_to_key, &to_key_buffer); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[table][copy] failed to copy key: <%.*s> -> <%.*s>: " + "<%.*s> -> <%.*s>", + (int)GRN_TEXT_LEN(&inspected_from_key), + GRN_TEXT_VALUE(&inspected_from_key), + (int)GRN_TEXT_LEN(&inspected_to_key), + GRN_TEXT_VALUE(&inspected_to_key), + (int)GRN_TEXT_LEN(from_name), + GRN_TEXT_VALUE(from_name), + (int)GRN_TEXT_LEN(to_name), + GRN_TEXT_VALUE(to_name)); + GRN_OBJ_FIN(ctx, &inspected_from_key); + GRN_OBJ_FIN(ctx, &inspected_to_key); + break; + } + } GRN_TABLE_EACH_END(ctx, cursor); + GRN_OBJ_FIN(ctx, &from_key_buffer); + GRN_OBJ_FIN(ctx, &to_key_buffer); +} + +static grn_obj * +command_table_copy(grn_ctx *ctx, + int nargs, + grn_obj **args, + grn_user_data *user_data) +{ + grn_rc rc = GRN_SUCCESS; + grn_obj *from_table = NULL; + grn_obj *to_table = NULL; + grn_obj *from_name; + grn_obj *to_name; + + from_name = grn_plugin_proc_get_var(ctx, user_data, "from_name", -1); + to_name = grn_plugin_proc_get_var(ctx, user_data, "to_name", -1); + + rc = command_table_copy_resolve_target(ctx, "from", from_name, &from_table); + if (rc != GRN_SUCCESS) { + goto exit; + } + rc = command_table_copy_resolve_target(ctx, "to", to_name, &to_table); + if (rc != GRN_SUCCESS) { + goto exit; + } + + if (from_table->header.type == GRN_TABLE_NO_KEY || + to_table->header.type == GRN_TABLE_NO_KEY) { + GRN_PLUGIN_ERROR(ctx, + GRN_OPERATION_NOT_SUPPORTED, + "[table][copy] copy from/to TABLE_NO_KEY isn't supported: " + "<%.*s> -> <%.*s>", + (int)GRN_TEXT_LEN(from_name), + GRN_TEXT_VALUE(from_name), + (int)GRN_TEXT_LEN(to_name), + GRN_TEXT_VALUE(to_name)); + rc = ctx->rc; + goto exit; + } + + if (from_table == to_table) { + GRN_PLUGIN_ERROR(ctx, + GRN_OPERATION_NOT_SUPPORTED, + "[table][copy] from table and to table is the same: " + "<%.*s>", + (int)GRN_TEXT_LEN(from_name), + GRN_TEXT_VALUE(from_name)); + rc = ctx->rc; + goto exit; + } + + if (from_table->header.domain == to_table->header.domain) { + command_table_copy_same_key_type(ctx, + from_table, to_table, + from_name, to_name); + } else { + command_table_copy_different(ctx, + from_table, to_table, + from_name, to_name); + } + +exit : + grn_ctx_output_bool(ctx, rc == GRN_SUCCESS); + + if (to_table) { + grn_obj_unlink(ctx, to_table); + } + if (from_table) { + grn_obj_unlink(ctx, from_table); + } + + return NULL; +} + +void +grn_proc_init_table_copy(grn_ctx *ctx) +{ + grn_expr_var vars[2]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "from_name", -1); + grn_plugin_expr_var_init(ctx, &(vars[1]), "to_name", -1); + grn_plugin_command_create(ctx, + "table_copy", -1, + command_table_copy, + 2, + vars); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/proc_tokenize.c b/storage/mroonga/vendor/groonga/lib/proc/proc_tokenize.c new file mode 100644 index 00000000..206ebf58 --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/proc_tokenize.c @@ -0,0 +1,433 @@ +/* -*- c-basic-offset: 2 -*- */ +/* + Copyright(C) 2009-2016 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_proc.h" +#include "../grn_ctx.h" +#include "../grn_token_cursor.h" + +#include <groonga/plugin.h> + +static unsigned int +parse_tokenize_flags(grn_ctx *ctx, grn_obj *flag_names) +{ + unsigned int flags = 0; + const char *names, *names_end; + int length; + + names = GRN_TEXT_VALUE(flag_names); + length = GRN_TEXT_LEN(flag_names); + names_end = names + length; + while (names < names_end) { + if (*names == '|' || *names == ' ') { + names += 1; + continue; + } + +#define CHECK_FLAG(name)\ + if (((unsigned long) (names_end - names) >= (unsigned long) (sizeof(#name) - 1)) &&\ + (!memcmp(names, #name, sizeof(#name) - 1))) {\ + flags |= GRN_TOKEN_CURSOR_ ## name;\ + names += sizeof(#name) - 1;\ + continue;\ + } + + CHECK_FLAG(ENABLE_TOKENIZED_DELIMITER); + +#define GRN_TOKEN_CURSOR_NONE 0 + CHECK_FLAG(NONE); +#undef GRN_TOKEN_CURSOR_NONE + + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[tokenize] invalid flag: <%.*s>", + (int)(names_end - names), names); + return 0; +#undef CHECK_FLAG + } + + return flags; +} + +typedef struct { + grn_id id; + int32_t position; + grn_bool force_prefix; +} tokenize_token; + +static void +output_tokens(grn_ctx *ctx, grn_obj *tokens, grn_obj *lexicon, grn_obj *index_column) +{ + int i, n_tokens, n_elements; + grn_obj estimated_size; + + n_tokens = GRN_BULK_VSIZE(tokens) / sizeof(tokenize_token); + n_elements = 3; + if (index_column) { + n_elements++; + GRN_UINT32_INIT(&estimated_size, 0); + } + + grn_ctx_output_array_open(ctx, "TOKENS", n_tokens); + for (i = 0; i < n_tokens; i++) { + tokenize_token *token; + char value[GRN_TABLE_MAX_KEY_SIZE]; + unsigned int value_size; + + token = ((tokenize_token *)(GRN_BULK_HEAD(tokens))) + i; + + grn_ctx_output_map_open(ctx, "TOKEN", n_elements); + + grn_ctx_output_cstr(ctx, "value"); + value_size = grn_table_get_key(ctx, lexicon, token->id, + value, GRN_TABLE_MAX_KEY_SIZE); + grn_ctx_output_str(ctx, value, value_size); + + grn_ctx_output_cstr(ctx, "position"); + grn_ctx_output_int32(ctx, token->position); + + grn_ctx_output_cstr(ctx, "force_prefix"); + grn_ctx_output_bool(ctx, token->force_prefix); + + if (index_column) { + GRN_BULK_REWIND(&estimated_size); + grn_obj_get_value(ctx, index_column, token->id, &estimated_size); + grn_ctx_output_cstr(ctx, "estimated_size"); + grn_ctx_output_int64(ctx, GRN_UINT32_VALUE(&estimated_size)); + } + + grn_ctx_output_map_close(ctx); + } + + if (index_column) { + GRN_OBJ_FIN(ctx, &estimated_size); + } + + grn_ctx_output_array_close(ctx); +} + +static grn_obj * +create_lexicon_for_tokenize(grn_ctx *ctx, + grn_obj *tokenizer_name, + grn_obj *normalizer_name, + grn_obj *token_filter_names) +{ + grn_obj *lexicon; + grn_obj *tokenizer; + grn_obj *normalizer = NULL; + + tokenizer = grn_ctx_get(ctx, + GRN_TEXT_VALUE(tokenizer_name), + GRN_TEXT_LEN(tokenizer_name)); + if (!tokenizer) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[tokenize] nonexistent tokenizer: <%.*s>", + (int)GRN_TEXT_LEN(tokenizer_name), + GRN_TEXT_VALUE(tokenizer_name)); + return NULL; + } + + if (!grn_obj_is_tokenizer_proc(ctx, tokenizer)) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, tokenizer); + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[tokenize] not tokenizer: %.*s", + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + grn_obj_unlink(ctx, tokenizer); + return NULL; + } + + if (GRN_TEXT_LEN(normalizer_name) > 0) { + normalizer = grn_ctx_get(ctx, + GRN_TEXT_VALUE(normalizer_name), + GRN_TEXT_LEN(normalizer_name)); + if (!normalizer) { + grn_obj_unlink(ctx, tokenizer); + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[tokenize] nonexistent normalizer: <%.*s>", + (int)GRN_TEXT_LEN(normalizer_name), + GRN_TEXT_VALUE(normalizer_name)); + return NULL; + } + + if (!grn_obj_is_normalizer_proc(ctx, normalizer)) { + grn_obj inspected; + grn_obj_unlink(ctx, tokenizer); + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, normalizer); + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[tokenize] not normalizer: %.*s", + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + grn_obj_unlink(ctx, normalizer); + return NULL; + } + } + + lexicon = grn_table_create(ctx, NULL, 0, + NULL, + GRN_OBJ_TABLE_HASH_KEY, + grn_ctx_at(ctx, GRN_DB_SHORT_TEXT), + NULL); + grn_obj_set_info(ctx, lexicon, + GRN_INFO_DEFAULT_TOKENIZER, tokenizer); + grn_obj_unlink(ctx, tokenizer); + if (normalizer) { + grn_obj_set_info(ctx, lexicon, + GRN_INFO_NORMALIZER, normalizer); + grn_obj_unlink(ctx, normalizer); + } + grn_proc_table_set_token_filters(ctx, lexicon, token_filter_names); + + return lexicon; +} + +static void +tokenize(grn_ctx *ctx, grn_obj *lexicon, grn_obj *string, grn_tokenize_mode mode, + unsigned int flags, grn_obj *tokens) +{ + grn_token_cursor *token_cursor; + + token_cursor = + grn_token_cursor_open(ctx, lexicon, + GRN_TEXT_VALUE(string), GRN_TEXT_LEN(string), + mode, flags); + if (!token_cursor) { + return; + } + + while (token_cursor->status == GRN_TOKEN_CURSOR_DOING) { + grn_id token_id = grn_token_cursor_next(ctx, token_cursor); + tokenize_token *current_token; + if (token_id == GRN_ID_NIL) { + continue; + } + grn_bulk_space(ctx, tokens, sizeof(tokenize_token)); + current_token = ((tokenize_token *)(GRN_BULK_CURR(tokens))) - 1; + current_token->id = token_id; + current_token->position = token_cursor->pos; + current_token->force_prefix = token_cursor->force_prefix; + } + grn_token_cursor_close(ctx, token_cursor); +} + +static grn_obj * +command_table_tokenize(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *table_name; + grn_obj *string; + grn_obj *flag_names; + grn_obj *mode_name; + grn_obj *index_column_name; + + table_name = grn_plugin_proc_get_var(ctx, user_data, "table", -1); + string = grn_plugin_proc_get_var(ctx, user_data, "string", -1); + flag_names = grn_plugin_proc_get_var(ctx, user_data, "flags", -1); + mode_name = grn_plugin_proc_get_var(ctx, user_data, "mode", -1); + index_column_name = grn_plugin_proc_get_var(ctx, user_data, "index_column", -1); + + if (GRN_TEXT_LEN(table_name) == 0) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, "[table_tokenize] table name is missing"); + return NULL; + } + + if (GRN_TEXT_LEN(string) == 0) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, "[table_tokenize] string is missing"); + return NULL; + } + + { + unsigned int flags; + grn_obj *lexicon; + grn_obj *index_column = NULL; + + flags = parse_tokenize_flags(ctx, flag_names); + if (ctx->rc != GRN_SUCCESS) { + return NULL; + } + + lexicon = grn_ctx_get(ctx, GRN_TEXT_VALUE(table_name), GRN_TEXT_LEN(table_name)); + if (!lexicon) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[table_tokenize] nonexistent lexicon: <%.*s>", + (int)GRN_TEXT_LEN(table_name), + GRN_TEXT_VALUE(table_name)); + return NULL; + } + +#define MODE_NAME_EQUAL(name)\ + (GRN_TEXT_LEN(mode_name) == strlen(name) &&\ + memcmp(GRN_TEXT_VALUE(mode_name), name, strlen(name)) == 0) + + if (GRN_TEXT_LEN(index_column_name) > 0) { + index_column = grn_obj_column(ctx, lexicon, + GRN_TEXT_VALUE(index_column_name), + GRN_TEXT_LEN(index_column_name)); + if (!index_column) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[table_tokenize] nonexistent index column: <%.*s>", + (int)GRN_TEXT_LEN(index_column_name), + GRN_TEXT_VALUE(index_column_name)); + goto exit; + } + if (index_column->header.type != GRN_COLUMN_INDEX) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[table_tokenize] index column must be COLUMN_INDEX: <%.*s>", + (int)GRN_TEXT_LEN(index_column_name), + GRN_TEXT_VALUE(index_column_name)); + goto exit; + } + } + + { + grn_obj tokens; + GRN_VALUE_FIX_SIZE_INIT(&tokens, GRN_OBJ_VECTOR, GRN_ID_NIL); + if (GRN_TEXT_LEN(mode_name) == 0 || MODE_NAME_EQUAL("GET")) { + tokenize(ctx, lexicon, string, GRN_TOKEN_GET, flags, &tokens); + output_tokens(ctx, &tokens, lexicon, index_column); + } else if (MODE_NAME_EQUAL("ADD")) { + tokenize(ctx, lexicon, string, GRN_TOKEN_ADD, flags, &tokens); + output_tokens(ctx, &tokens, lexicon, index_column); + } else { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[table_tokenize] invalid mode: <%.*s>", + (int)GRN_TEXT_LEN(mode_name), GRN_TEXT_VALUE(mode_name)); + } + GRN_OBJ_FIN(ctx, &tokens); + } +#undef MODE_NAME_EQUAL + +exit: + grn_obj_unlink(ctx, lexicon); + if (index_column) { + grn_obj_unlink(ctx, index_column); + } + } + + return NULL; +} + +void +grn_proc_init_table_tokenize(grn_ctx *ctx) +{ + grn_expr_var vars[5]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "table", -1); + grn_plugin_expr_var_init(ctx, &(vars[1]), "string", -1); + grn_plugin_expr_var_init(ctx, &(vars[2]), "flags", -1); + grn_plugin_expr_var_init(ctx, &(vars[3]), "mode", -1); + grn_plugin_expr_var_init(ctx, &(vars[4]), "index_column", -1); + grn_plugin_command_create(ctx, + "table_tokenize", -1, + command_table_tokenize, + 5, + vars); +} + +static grn_obj * +command_tokenize(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ + grn_obj *tokenizer_name; + grn_obj *string; + grn_obj *normalizer_name; + grn_obj *flag_names; + grn_obj *mode_name; + grn_obj *token_filter_names; + + tokenizer_name = grn_plugin_proc_get_var(ctx, user_data, "tokenizer", -1); + string = grn_plugin_proc_get_var(ctx, user_data, "string", -1); + normalizer_name = grn_plugin_proc_get_var(ctx, user_data, "normalizer", -1); + flag_names = grn_plugin_proc_get_var(ctx, user_data, "flags", -1); + mode_name = grn_plugin_proc_get_var(ctx, user_data, "mode", -1); + token_filter_names = grn_plugin_proc_get_var(ctx, user_data, "token_filters", -1); + + if (GRN_TEXT_LEN(tokenizer_name) == 0) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, "[tokenize] tokenizer name is missing"); + return NULL; + } + + if (GRN_TEXT_LEN(string) == 0) { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, "[tokenize] string is missing"); + return NULL; + } + + { + unsigned int flags; + grn_obj *lexicon; + + flags = parse_tokenize_flags(ctx, flag_names); + if (ctx->rc != GRN_SUCCESS) { + return NULL; + } + + lexicon = create_lexicon_for_tokenize(ctx, + tokenizer_name, + normalizer_name, + token_filter_names); + if (!lexicon) { + return NULL; + } +#define MODE_NAME_EQUAL(name)\ + (GRN_TEXT_LEN(mode_name) == strlen(name) &&\ + memcmp(GRN_TEXT_VALUE(mode_name), name, strlen(name)) == 0) + + { + grn_obj tokens; + GRN_VALUE_FIX_SIZE_INIT(&tokens, GRN_OBJ_VECTOR, GRN_ID_NIL); + if (GRN_TEXT_LEN(mode_name) == 0 || MODE_NAME_EQUAL("ADD")) { + tokenize(ctx, lexicon, string, GRN_TOKEN_ADD, flags, &tokens); + output_tokens(ctx, &tokens, lexicon, NULL); + } else if (MODE_NAME_EQUAL("GET")) { + tokenize(ctx, lexicon, string, GRN_TOKEN_ADD, flags, &tokens); + GRN_BULK_REWIND(&tokens); + tokenize(ctx, lexicon, string, GRN_TOKEN_GET, flags, &tokens); + output_tokens(ctx, &tokens, lexicon, NULL); + } else { + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "[tokenize] invalid mode: <%.*s>", + (int)GRN_TEXT_LEN(mode_name), GRN_TEXT_VALUE(mode_name)); + } + GRN_OBJ_FIN(ctx, &tokens); + } +#undef MODE_NAME_EQUAL + + grn_obj_unlink(ctx, lexicon); + } + + return NULL; +} + +void +grn_proc_init_tokenize(grn_ctx *ctx) +{ + grn_expr_var vars[6]; + + grn_plugin_expr_var_init(ctx, &(vars[0]), "tokenizer", -1); + grn_plugin_expr_var_init(ctx, &(vars[1]), "string", -1); + grn_plugin_expr_var_init(ctx, &(vars[2]), "normalizer", -1); + grn_plugin_expr_var_init(ctx, &(vars[3]), "flags", -1); + grn_plugin_expr_var_init(ctx, &(vars[4]), "mode", -1); + grn_plugin_expr_var_init(ctx, &(vars[5]), "token_filters", -1); + grn_plugin_command_create(ctx, + "tokenize", -1, + command_tokenize, + 6, + vars); +} diff --git a/storage/mroonga/vendor/groonga/lib/proc/sources.am b/storage/mroonga/vendor/groonga/lib/proc/sources.am new file mode 100644 index 00000000..a945320f --- /dev/null +++ b/storage/mroonga/vendor/groonga/lib/proc/sources.am @@ -0,0 +1,18 @@ +libgrnproc_la_SOURCES = \ + proc_column.c \ + proc_config.c \ + proc_dump.c \ + proc_fuzzy_search.c \ + proc_highlight.c \ + proc_in_records.c \ + proc_lock.c \ + proc_object.c \ + proc_object_inspect.c \ + proc_object_list.c \ + proc_query.c \ + proc_query_log_flags.c \ + proc_schema.c \ + proc_select.c \ + proc_snippet.c \ + proc_table.c \ + proc_tokenize.c |