Kouhei Sutou
null+****@clear*****
Fri Feb 26 00:12:06 JST 2016
Kouhei Sutou 2016-02-26 00:12:06 +0900 (Fri, 26 Feb 2016) New Revision: b1f40bef175ffffab548b87c49aa3fb175a0c6de https://github.com/groonga/groonga/commit/b1f40bef175ffffab548b87c49aa3fb175a0c6de Message: Extract select command code Added files: lib/proc/proc_select.c Modified files: lib/grn_proc.h lib/proc.c lib/proc/sources.am Modified: lib/grn_proc.h (+18 -0) =================================================================== --- lib/grn_proc.h 2016-02-26 00:05:14 +0900 (e8f78e4) +++ lib/grn_proc.h 2016-02-26 00:12:06 +0900 (5fb83f1) @@ -24,6 +24,9 @@ extern "C" { #endif +#define GRN_SELECT_DEFAULT_LIMIT 10 +#define GRN_SELECT_DEFAULT_OUTPUT_COLUMNS "_id, _key, *" + #define GRN_SELECT_INTERNAL_VAR_CONDITION "$condition" void grn_proc_init_from_env(void); @@ -35,6 +38,7 @@ void grn_proc_init_clearlock(grn_ctx *ctx); void grn_proc_init_config_get(grn_ctx *ctx); void grn_proc_init_config_set(grn_ctx *ctx); void grn_proc_init_config_delete(grn_ctx *ctx); +void grn_proc_init_define_selector(grn_ctx *ctx); void grn_proc_init_edit_distance(grn_ctx *ctx); void grn_proc_init_fuzzy_search(grn_ctx *ctx); void grn_proc_init_highlight(grn_ctx *ctx); @@ -47,6 +51,7 @@ void grn_proc_init_lock_release(grn_ctx *ctx); void grn_proc_init_object_exist(grn_ctx *ctx); void grn_proc_init_object_remove(grn_ctx *ctx); void grn_proc_init_schema(grn_ctx *ctx); +void grn_proc_init_select(grn_ctx *ctx); void grn_proc_init_snippet(grn_ctx *ctx); void grn_proc_init_snippet_html(grn_ctx *ctx); void grn_proc_init_table_create(grn_ctx *ctx); @@ -71,6 +76,19 @@ void grn_proc_table_set_token_filters(grn_ctx *ctx, grn_obj *table, grn_obj *token_filter_names); +void 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_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, + grn_obj *expanded_query); #ifdef __cplusplus } Modified: lib/proc.c (+15 -1043) =================================================================== --- lib/proc.c 2016-02-26 00:05:14 +0900 (8fa27f2) +++ lib/proc.c 2016-02-26 00:12:06 +0900 (69608a2) @@ -26,8 +26,6 @@ #include "grn_token_cursor.h" #include "grn_expr.h" -#include "grn_ts.h" - #include <string.h> #include <stdlib.h> #include <fcntl.h> @@ -47,9 +45,6 @@ const char *grn_document_root = NULL; #define VAR GRN_PROC_GET_VAR_BY_OFFSET -#define GRN_SELECT_INTERNAL_VAR_MATCH_COLUMNS "$match_columns" - - static double grn_between_too_many_index_match_ratio = 0.01; static double grn_in_values_too_many_index_match_ratio = 0.01; @@ -131,1011 +126,10 @@ exit : # undef stat #endif /* stat */ -static grn_rc -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, - grn_obj *expanded_query) -{ - grn_obj *query_expander; - - query_expander = grn_ctx_get(ctx, - query_expander_name, query_expander_name_len); - if (!query_expander) { - ERR(GRN_INVALID_ARGUMENT, - "nonexistent query expansion column: <%.*s>", - query_expander_name_len, query_expander_name); - return GRN_INVALID_ARGUMENT; - } - - return grn_expr_syntax_expand_query(ctx, query, query_len, flags, - query_expander, expanded_query); -} - /**** procs ****/ -#define DEFAULT_LIMIT 10 -#define DEFAULT_OUTPUT_COLUMNS "_id, _key, *" -#define DEFAULT_DRILLDOWN_LIMIT 10 -#define DEFAULT_DRILLDOWN_OUTPUT_COLUMNS "_key, _nsubrecs" #define DUMP_COLUMNS "_id, _key, _value, *" -static grn_expr_flags -grn_parse_query_flags(grn_ctx *ctx, const char *query_flags, - unsigned int query_flags_len) -{ - grn_expr_flags flags = 0; - const char *query_flags_end = query_flags + query_flags_len; - - while (query_flags < query_flags_end) { - if (*query_flags == '|' || *query_flags == ' ') { - query_flags += 1; - continue; - } - -#define CHECK_EXPR_FLAG(name)\ - if (((query_flags_end - query_flags) >= (sizeof(#name) - 1)) &&\ - (!memcmp(query_flags, #name, 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); - -#define GRN_EXPR_NONE 0 - CHECK_EXPR_FLAG(NONE); -#undef GNR_EXPR_NONE - - ERR(GRN_INVALID_ARGUMENT, "invalid query flag: <%.*s>", - (int)(query_flags_end - query_flags), query_flags); - return 0; -#undef CHECK_EXPR_FLAG - } - - return flags; -} - -static int -grn_select_apply_adjuster_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_adjust(grn_ctx *ctx, grn_obj *table, grn_obj *res, - 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); - ERR(GRN_INVALID_ARGUMENT, - "adjuster requires index column for the target column: <%.*s>", - column_name_size, column_name); - return; - } - - factor_value = grn_select_apply_adjuster_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, res, GRN_OP_ADJUST, &options); - } -} - -static void -grn_select_apply_adjuster(grn_ctx *ctx, grn_obj *table, grn_obj *res, - 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_adjust(ctx, table, res, column, value, factor); - } -} - -static void -grn_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_rc rc; - grn_obj_format format; - - 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, res, columns, columns_len); - if (rc != GRN_SUCCESS) { - GRN_OBJ_FORMAT_FIN(ctx, &format); - return; - } - - if (format.expression) { - grn_obj *condition_ptr; - condition_ptr = - grn_expr_get_or_add_var(ctx, format.expression, - GRN_SELECT_INTERNAL_VAR_CONDITION, - strlen(GRN_SELECT_INTERNAL_VAR_CONDITION)); - GRN_PTR_INIT(condition_ptr, 0, GRN_DB_OBJECT); - GRN_PTR_SET(ctx, condition_ptr, condition); - } - GRN_OUTPUT_OBJ(res, &format); - GRN_OBJ_FORMAT_FIN(ctx, &format); -} - -typedef struct { - const char *label; - unsigned int label_len; - const char *keys; - unsigned int keys_len; - const char *sortby; - unsigned int sortby_len; - const char *output_columns; - unsigned int output_columns_len; - int offset; - int limit; - grn_table_group_flags calc_types; - const char *calc_target_name; - unsigned int calc_target_name_len; -} drilldown_info; - -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 (((calc_types_end - calc_types) >= (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 - - ERR(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 void -drilldown_info_fill(grn_ctx *ctx, - drilldown_info *drilldown, - grn_obj *keys, - grn_obj *sortby, - grn_obj *output_columns, - grn_obj *offset, - grn_obj *limit, - grn_obj *calc_types, - grn_obj *calc_target) -{ - if (keys) { - drilldown->keys = GRN_TEXT_VALUE(keys); - drilldown->keys_len = GRN_TEXT_LEN(keys); - } else { - drilldown->keys = NULL; - drilldown->keys_len = 0; - } - - if (sortby) { - drilldown->sortby = GRN_TEXT_VALUE(sortby); - drilldown->sortby_len = GRN_TEXT_LEN(sortby); - } else { - drilldown->sortby = NULL; - drilldown->sortby_len = 0; - } - - if (output_columns) { - drilldown->output_columns = GRN_TEXT_VALUE(output_columns); - drilldown->output_columns_len = GRN_TEXT_LEN(output_columns); - } else { - drilldown->output_columns = NULL; - drilldown->output_columns_len = 0; - } - if (!drilldown->output_columns_len) { - drilldown->output_columns = DEFAULT_DRILLDOWN_OUTPUT_COLUMNS; - drilldown->output_columns_len = 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; - } - - if (calc_target && GRN_TEXT_LEN(calc_target)) { - drilldown->calc_target_name = GRN_TEXT_VALUE(calc_target); - drilldown->calc_target_name_len = GRN_TEXT_LEN(calc_target); - } else { - drilldown->calc_target_name = NULL; - drilldown->calc_target_name_len = 0; - } -} - -static void -grn_select_drilldown(grn_ctx *ctx, grn_obj *table, - grn_table_sort_key *keys, uint32_t n_keys, - drilldown_info *drilldown) -{ - uint32_t i; - for (i = 0; i < n_keys; i++) { - grn_table_group_result g = {NULL, 0, 0, 1, GRN_TABLE_GROUP_CALC_COUNT, 0}; - uint32_t n_hits; - int offset; - int limit; - - if (drilldown->calc_target_name) { - g.calc_target = grn_obj_column(ctx, table, - drilldown->calc_target_name, - drilldown->calc_target_name_len); - } - if (g.calc_target) { - g.flags |= drilldown->calc_types; - } - - grn_table_group(ctx, table, &keys[i], 1, &g, 1); - if (ctx->rc != GRN_SUCCESS) { - break; - } - n_hits = grn_table_size(ctx, g.table); - - offset = drilldown->offset; - limit = drilldown->limit; - grn_normalize_offset_and_limit(ctx, n_hits, &offset, &limit); - - if (drilldown->sortby_len) { - grn_table_sort_key *sort_keys; - uint32_t n_sort_keys; - sort_keys = grn_table_sort_key_from_str(ctx, - drilldown->sortby, - drilldown->sortby_len, - g.table, &n_sort_keys); - if (sort_keys) { - grn_obj *sorted; - sorted = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_NO_KEY, - NULL, g.table); - if (sorted) { - grn_obj_format format; - grn_table_sort(ctx, g.table, offset, limit, - sorted, sort_keys, n_sort_keys); - GRN_OBJ_FORMAT_INIT(&format, n_hits, 0, limit, offset); - format.flags = - GRN_OBJ_FORMAT_WITH_COLUMN_NAMES| - GRN_OBJ_FORMAT_XML_ELEMENT_NAVIGATIONENTRY; - grn_obj_columns(ctx, sorted, - drilldown->output_columns, - drilldown->output_columns_len, - &format.columns); - GRN_OUTPUT_OBJ(sorted, &format); - GRN_OBJ_FORMAT_FIN(ctx, &format); - grn_obj_unlink(ctx, sorted); - } - grn_table_sort_key_close(ctx, sort_keys, n_sort_keys); - } - } else { - grn_obj_format format; - GRN_OBJ_FORMAT_INIT(&format, n_hits, offset, limit, offset); - format.flags = - GRN_OBJ_FORMAT_WITH_COLUMN_NAMES| - GRN_OBJ_FORMAT_XML_ELEMENT_NAVIGATIONENTRY; - grn_obj_columns(ctx, g.table, - drilldown->output_columns, - drilldown->output_columns_len, - &format.columns); - GRN_OUTPUT_OBJ(g.table, &format); - GRN_OBJ_FORMAT_FIN(ctx, &format); - } - grn_obj_unlink(ctx, g.table); - GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, - ":", "drilldown(%d)", n_hits); - } -} - -static void -grn_select_drilldowns(grn_ctx *ctx, grn_obj *table, - drilldown_info *drilldowns, unsigned int n_drilldowns, - grn_obj *condition) -{ - unsigned int i; - - /* TODO: Remove invalid key drilldowns from the count. */ - GRN_OUTPUT_MAP_OPEN("DRILLDOWNS", n_drilldowns); - for (i = 0; i < n_drilldowns; i++) { - drilldown_info *drilldown = &(drilldowns[i]); - grn_table_sort_key *keys = NULL; - unsigned int n_keys; - uint32_t n_hits; - int offset; - int limit; - grn_table_group_result result = { - NULL, 0, 0, 1, GRN_TABLE_GROUP_CALC_COUNT, 0, 0, NULL - }; - - keys = grn_table_sort_key_from_str(ctx, - drilldown->keys, - drilldown->keys_len, - table, &n_keys); - if (!keys) { - continue; - } - - GRN_OUTPUT_STR(drilldown->label, drilldown->label_len); - - result.key_begin = 0; - result.key_end = n_keys - 1; - if (n_keys > 1) { - result.max_n_subrecs = 1; - } - if (drilldown->calc_target_name) { - result.calc_target = grn_obj_column(ctx, table, - drilldown->calc_target_name, - drilldown->calc_target_name_len); - } - if (result.calc_target) { - result.flags |= drilldown->calc_types; - } - - grn_table_group(ctx, table, keys, n_keys, &result, 1); - n_hits = grn_table_size(ctx, result.table); - - offset = drilldown->offset; - limit = drilldown->limit; - grn_normalize_offset_and_limit(ctx, n_hits, &offset, &limit); - - if (drilldown->sortby_len) { - grn_table_sort_key *sort_keys; - uint32_t n_sort_keys; - sort_keys = grn_table_sort_key_from_str(ctx, - drilldown->sortby, - drilldown->sortby_len, - result.table, &n_sort_keys); - if (sort_keys) { - grn_obj *sorted; - sorted = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_NO_KEY, - NULL, result.table); - if (sorted) { - grn_table_sort(ctx, result.table, offset, limit, - sorted, sort_keys, n_sort_keys); - grn_select_output_columns(ctx, sorted, n_hits, 0, limit, - drilldown->output_columns, - drilldown->output_columns_len, - condition); - grn_obj_unlink(ctx, sorted); - } - grn_table_sort_key_close(ctx, sort_keys, n_sort_keys); - } - } else { - grn_select_output_columns(ctx, result.table, n_hits, offset, limit, - drilldown->output_columns, - drilldown->output_columns_len, - condition); - } - - grn_table_sort_key_close(ctx, keys, n_keys); - if (result.calc_target) { - grn_obj_unlink(ctx, result.calc_target); - } - grn_obj_unlink(ctx, result.table); - - GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, - ":", "drilldown(%d)[%.*s]", n_hits, - (int)(drilldown->label_len), drilldown->label); - } - GRN_OUTPUT_MAP_CLOSE(); -} - -static grn_rc -grn_select(grn_ctx *ctx, const char *table, unsigned int table_len, - const char *match_columns, unsigned int match_columns_len, - const char *query, unsigned int query_len, - const char *filter, unsigned int filter_len, - const char *scorer, unsigned int scorer_len, - const char *sortby, unsigned int sortby_len, - const char *output_columns, unsigned int output_columns_len, - int offset, int limit, - drilldown_info *drilldowns, - unsigned int n_drilldowns, - const char *cache, unsigned int cache_len, - const char *match_escalation_threshold, unsigned int match_escalation_threshold_len, - const char *query_expander, unsigned int query_expander_len, - const char *query_flags, unsigned int query_flags_len, - const char *adjuster, unsigned int adjuster_len) -{ - uint32_t nkeys, nhits; - uint16_t cacheable = 1, taintable = 0; - grn_table_sort_key *keys; - grn_obj *outbuf = ctx->impl->output.buf; - grn_content_type output_type = ctx->impl->output.type; - grn_obj *table_, *match_columns_ = NULL, *cond = NULL, *scorer_, *res = NULL, *sorted; - 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); - - { - const char *query_end = query + query_len; - int space_len; - while (query < query_end) { - space_len = grn_isspace(query, ctx->encoding); - if (space_len == 0) { - break; - } - query += space_len; - query_len -= space_len; - } - } - - cache_key_size = table_len + 1 + match_columns_len + 1 + query_len + 1 + - filter_len + 1 + scorer_len + 1 + sortby_len + 1 + output_columns_len + 1 + - match_escalation_threshold_len + 1 + - query_expander_len + 1 + query_flags_len + 1 + adjuster_len + 1 + - sizeof(grn_content_type) + sizeof(int) * 2; - { - unsigned int i; - for (i = 0; i < n_drilldowns; i++) { - drilldown_info *drilldown = &(drilldowns[i]); - cache_key_size += - drilldown->keys_len + 1 + - drilldown->sortby_len + 1 + - drilldown->output_columns_len + 1 + - sizeof(int) * 2; - } - } - if (cache_key_size <= GRN_CACHE_MAX_KEY_SIZE) { - grn_obj *cache_value; - char *cp = cache_key; - grn_memcpy(cp, table, table_len); - cp += table_len; *cp++ = '\0'; - grn_memcpy(cp, match_columns, match_columns_len); - cp += match_columns_len; *cp++ = '\0'; - grn_memcpy(cp, query, query_len); - cp += query_len; *cp++ = '\0'; - grn_memcpy(cp, filter, filter_len); - cp += filter_len; *cp++ = '\0'; - grn_memcpy(cp, scorer, scorer_len); - cp += scorer_len; *cp++ = '\0'; - grn_memcpy(cp, sortby, sortby_len); - cp += sortby_len; *cp++ = '\0'; - grn_memcpy(cp, output_columns, output_columns_len); - cp += output_columns_len; *cp++ = '\0'; - { - unsigned int i; - for (i = 0; i < n_drilldowns; i++) { - drilldown_info *drilldown = &(drilldowns[i]); - grn_memcpy(cp, drilldown->keys, drilldown->keys_len); - cp += drilldown->keys_len; *cp++ = '\0'; - grn_memcpy(cp, drilldown->sortby, drilldown->sortby_len); - cp += drilldown->sortby_len; *cp++ = '\0'; - grn_memcpy(cp, drilldown->output_columns, drilldown->output_columns_len); - cp += drilldown->output_columns_len; *cp++ = '\0'; - } - } - grn_memcpy(cp, match_escalation_threshold, match_escalation_threshold_len); - cp += match_escalation_threshold_len; *cp++ = '\0'; - grn_memcpy(cp, query_expander, query_expander_len); - cp += query_expander_len; *cp++ = '\0'; - grn_memcpy(cp, query_flags, query_flags_len); - cp += query_flags_len; *cp++ = '\0'; - grn_memcpy(cp, adjuster, adjuster_len); - cp += adjuster_len; *cp++ = '\0'; - grn_memcpy(cp, &output_type, sizeof(grn_content_type)); - cp += sizeof(grn_content_type); - grn_memcpy(cp, &offset, sizeof(int)); - cp += sizeof(int); - grn_memcpy(cp, &limit, sizeof(int)); - cp += sizeof(int); - { - unsigned int i; - for (i = 0; i < n_drilldowns; i++) { - drilldown_info *drilldown = &(drilldowns[i]); - grn_memcpy(cp, &(drilldown->offset), sizeof(int)); - cp += sizeof(int); - grn_memcpy(cp, &(drilldown->limit), sizeof(int)); - cp += sizeof(int); - } - } - cache_value = grn_cache_fetch(ctx, cache_obj, cache_key, cache_key_size); - if (cache_value) { - GRN_TEXT_PUT(ctx, outbuf, - GRN_TEXT_VALUE(cache_value), - GRN_TEXT_LEN(cache_value)); - grn_cache_unref(ctx, cache_obj, cache_key, cache_key_size); - GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_CACHE, - ":", "cache(%" GRN_FMT_LLD ")", - (long long int)GRN_TEXT_LEN(cache_value)); - return ctx->rc; - } - } - if (match_escalation_threshold_len) { - const char *end, *rest; - original_threshold = grn_ctx_get_match_escalation_threshold(ctx); - end = match_escalation_threshold + match_escalation_threshold_len; - threshold = grn_atoll(match_escalation_threshold, end, &rest); - if (end == rest) { - grn_ctx_set_match_escalation_threshold(ctx, threshold); - } - } - if ((table_ = grn_ctx_get(ctx, table, table_len))) { - // match_columns_ = grn_obj_column(ctx, table_, match_columns, match_columns_len); - if (filter_len && (filter[0] == '?') && - (ctx->impl->output.type == GRN_CONTENT_JSON)) { - ctx->rc = grn_ts_select(ctx, table_, filter + 1, filter_len - 1, - scorer, scorer_len, - sortby, sortby_len, - output_columns, output_columns_len, - offset, limit); - if (!ctx->rc && cacheable && cache_key_size <= GRN_CACHE_MAX_KEY_SIZE && - (!cache || cache_len != 2 || cache[0] != 'n' || cache[1] != 'o')) { - grn_cache_update(ctx, cache_obj, cache_key, cache_key_size, outbuf); - } - goto exit; - } - if (query_len || filter_len) { - grn_obj *v; - GRN_EXPR_CREATE_FOR_QUERY(ctx, table_, cond, v); - if (cond) { - if (match_columns_len) { - GRN_EXPR_CREATE_FOR_QUERY(ctx, table_, match_columns_, v); - if (match_columns_) { - grn_expr_parse(ctx, match_columns_, match_columns, match_columns_len, - NULL, GRN_OP_MATCH, GRN_OP_AND, - GRN_EXPR_SYNTAX_SCRIPT); - if (ctx->rc) { - goto exit; - } - } else { - /* todo */ - } - } - if (query_len) { - grn_expr_flags flags; - grn_obj query_expander_buf; - flags = GRN_EXPR_SYNTAX_QUERY; - if (query_flags_len) { - flags |= grn_parse_query_flags(ctx, query_flags, query_flags_len); - if (ctx->rc) { - goto exit; - } - } else { - flags |= GRN_EXPR_ALLOW_PRAGMA|GRN_EXPR_ALLOW_COLUMN; - } - GRN_TEXT_INIT(&query_expander_buf, 0); - if (query_expander_len) { - if (expand_query(ctx, query, query_len, flags, - query_expander, query_expander_len, - &query_expander_buf) == 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); - goto exit; - } - } - grn_expr_parse(ctx, cond, query, query_len, - match_columns_, GRN_OP_MATCH, GRN_OP_AND, flags); - GRN_OBJ_FIN(ctx, &query_expander_buf); - if (!ctx->rc && filter_len) { - grn_expr_parse(ctx, cond, filter, filter_len, - match_columns_, GRN_OP_MATCH, GRN_OP_AND, - GRN_EXPR_SYNTAX_SCRIPT); - if (!ctx->rc) { grn_expr_append_op(ctx, cond, GRN_OP_AND, 2); } - } - } else { - grn_expr_parse(ctx, cond, filter, filter_len, - match_columns_, GRN_OP_MATCH, GRN_OP_AND, - GRN_EXPR_SYNTAX_SCRIPT); - } - cacheable *= ((grn_expr *)cond)->cacheable; - taintable += ((grn_expr *)cond)->taintable; - /* - grn_obj strbuf; - GRN_TEXT_INIT(&strbuf, 0); - grn_expr_inspect(ctx, &strbuf, cond); - GRN_TEXT_PUTC(ctx, &strbuf, '\0'); - GRN_LOG(ctx, GRN_LOG_NOTICE, "query=(%s)", GRN_TEXT_VALUE(&strbuf)); - GRN_OBJ_FIN(ctx, &strbuf); - */ - if (!ctx->rc) { res = grn_table_select(ctx, table_, cond, NULL, GRN_OP_OR); } - } else { - /* todo */ - ERRCLR(ctx); - } - } else { - res = table_; - } - nhits = res ? grn_table_size(ctx, res) : 0; - GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, - ":", "select(%d)", nhits); - - if (res) { - uint32_t ngkeys; - grn_table_sort_key *gkeys = NULL; - int result_size = 1; - if (!ctx->rc && n_drilldowns > 0) { - if (n_drilldowns == 1 && !drilldowns[0].label) { - gkeys = grn_table_sort_key_from_str(ctx, - drilldowns[0].keys, - drilldowns[0].keys_len, - res, &ngkeys); - if (gkeys) { - result_size += ngkeys; - } - } else { - result_size += 1; - } - } - - if (adjuster && adjuster_len) { - grn_obj *adjuster_; - grn_obj *v; - GRN_EXPR_CREATE_FOR_QUERY(ctx, table_, adjuster_, v); - if (adjuster_ && v) { - grn_rc rc; - rc = grn_expr_parse(ctx, adjuster_, adjuster, adjuster_len, NULL, - GRN_OP_MATCH, GRN_OP_ADJUST, - GRN_EXPR_SYNTAX_ADJUSTER); - if (rc) { - grn_obj_unlink(ctx, adjuster_); - goto exit; - } - cacheable *= ((grn_expr *)adjuster_)->cacheable; - taintable += ((grn_expr *)adjuster_)->taintable; - grn_select_apply_adjuster(ctx, table_, res, adjuster_); - grn_obj_unlink(ctx, adjuster_); - } - GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, - ":", "adjust(%d)", nhits); - } - - if (scorer && scorer_len) { - grn_obj *v; - GRN_EXPR_CREATE_FOR_QUERY(ctx, res, scorer_, v); - if (scorer_ && v) { - grn_table_cursor *tc; - grn_expr_parse(ctx, scorer_, scorer, scorer_len, NULL, GRN_OP_MATCH, GRN_OP_AND, - GRN_EXPR_SYNTAX_SCRIPT|GRN_EXPR_ALLOW_UPDATE); - cacheable *= ((grn_expr *)scorer_)->cacheable; - taintable += ((grn_expr *)scorer_)->taintable; - if ((tc = grn_table_cursor_open(ctx, res, NULL, 0, NULL, 0, 0, -1, 0))) { - grn_id id; - while ((id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL) { - GRN_RECORD_SET(ctx, v, id); - grn_expr_exec(ctx, scorer_, 0); - if (ctx->rc) { - break; - } - } - grn_table_cursor_close(ctx, tc); - } - grn_obj_unlink(ctx, scorer_); - } - GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, - ":", "score(%d)", nhits); - } - - GRN_OUTPUT_ARRAY_OPEN("RESULT", result_size); - - grn_normalize_offset_and_limit(ctx, nhits, &offset, &limit); - - if (sortby_len && - (keys = grn_table_sort_key_from_str(ctx, sortby, sortby_len, res, &nkeys))) { - if ((sorted = grn_table_create(ctx, NULL, 0, NULL, - GRN_OBJ_TABLE_NO_KEY, NULL, res))) { - grn_table_sort(ctx, res, offset, limit, sorted, keys, nkeys); - GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, - ":", "sort(%d)", limit); - grn_select_output_columns(ctx, sorted, nhits, 0, limit, - output_columns, output_columns_len, cond); - grn_obj_unlink(ctx, sorted); - } - grn_table_sort_key_close(ctx, keys, nkeys); - } else { - if (!ctx->rc) { - grn_select_output_columns(ctx, res, nhits, offset, limit, - output_columns, output_columns_len, cond); - } - } - GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, - ":", "output(%d)", limit); - if (!ctx->rc) { - if (gkeys) { - drilldown_info *drilldown = &(drilldowns[0]); - grn_select_drilldown(ctx, res, gkeys, ngkeys, drilldown); - } else if (n_drilldowns > 0) { - grn_select_drilldowns(ctx, res, drilldowns, n_drilldowns, cond); - } - } - if (gkeys) { - grn_table_sort_key_close(ctx, gkeys, ngkeys); - } - if (res != table_) { grn_obj_unlink(ctx, res); } - } else { - GRN_OUTPUT_ARRAY_OPEN("RESULT", 0); - } - GRN_OUTPUT_ARRAY_CLOSE(); - if (!ctx->rc && cacheable && cache_key_size <= GRN_CACHE_MAX_KEY_SIZE - && (!cache || cache_len != 2 || *cache != 'n' || *(cache + 1) != 'o')) { - grn_cache_update(ctx, cache_obj, cache_key, cache_key_size, outbuf); - } - if (taintable) { grn_db_touch(ctx, DB_OBJ(table_)->db); } - grn_obj_unlink(ctx, table_); - } else { - ERR(GRN_INVALID_ARGUMENT, "invalid table name: <%.*s>", table_len, table); - } -exit : - if (match_escalation_threshold_len) { - grn_ctx_set_match_escalation_threshold(ctx, original_threshold); - } - if (match_columns_) { - grn_obj_unlink(ctx, match_columns_); - } - if (cond) { - grn_obj_unlink(ctx, cond); - } - /* GRN_LOG(ctx, GRN_LOG_NONE, "%d", ctx->seqno); */ - return ctx->rc; -} - -static void -proc_select_find_all_drilldown_labels(grn_ctx *ctx, grn_user_data *user_data, - grn_obj *labels) -{ - grn_obj *vars = GRN_PROC_GET_VARS(); - grn_table_cursor *cursor; - cursor = grn_table_cursor_open(ctx, vars, NULL, 0, NULL, 0, 0, -1, 0); - if (cursor) { - const char *prefix = "drilldown["; - int prefix_len = strlen(prefix); - const char *suffix = "].keys"; - int suffix_len = strlen(suffix); - 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 + suffix_len)) { - continue; - } - if (strncmp(prefix, name, prefix_len) != 0) { - continue; - } - if (strncmp(suffix, name + name_len - suffix_len, suffix_len) != 0) { - continue; - } - grn_vector_add_element(ctx, labels, - name + prefix_len, - name_len - prefix_len - suffix_len, - 0, GRN_ID_NIL); - } - grn_table_cursor_close(ctx, cursor); - } -} - -static grn_obj * -proc_select(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) -{ -#define MAX_N_DRILLDOWNS 10 - int offset = GRN_TEXT_LEN(VAR(7)) - ? grn_atoi(GRN_TEXT_VALUE(VAR(7)), GRN_BULK_CURR(VAR(7)), NULL) - : 0; - int limit = GRN_TEXT_LEN(VAR(8)) - ? grn_atoi(GRN_TEXT_VALUE(VAR(8)), GRN_BULK_CURR(VAR(8)), NULL) - : DEFAULT_LIMIT; - const char *output_columns = GRN_TEXT_VALUE(VAR(6)); - uint32_t output_columns_len = GRN_TEXT_LEN(VAR(6)); - drilldown_info drilldowns[MAX_N_DRILLDOWNS]; - unsigned int n_drilldowns = 0; - grn_obj drilldown_labels; - grn_obj *query_expansion = VAR(16); - grn_obj *query_expander = VAR(18); - grn_obj *adjuster = VAR(19); - - if (GRN_TEXT_LEN(query_expander) == 0 && GRN_TEXT_LEN(query_expansion) > 0) { - query_expander = query_expansion; - } - - if (!output_columns_len) { - output_columns = DEFAULT_OUTPUT_COLUMNS; - output_columns_len = strlen(DEFAULT_OUTPUT_COLUMNS); - } - - GRN_TEXT_INIT(&drilldown_labels, GRN_OBJ_VECTOR); - if (GRN_TEXT_LEN(VAR(9))) { - drilldown_info *drilldown = &(drilldowns[0]); - drilldown->label = NULL; - drilldown->label_len = 0; - drilldown_info_fill(ctx, drilldown, - VAR(9), VAR(10), VAR(11), VAR(12), VAR(13), - VAR(20), VAR(21)); - n_drilldowns++; - } else { - unsigned int i; - proc_select_find_all_drilldown_labels(ctx, user_data, &drilldown_labels); - n_drilldowns = grn_vector_size(ctx, &drilldown_labels); - for (i = 0; i < n_drilldowns; i++) { - drilldown_info *drilldown = &(drilldowns[i]); - const char *label; - int label_len; - char key_name[GRN_TABLE_MAX_KEY_SIZE]; - grn_obj *keys; - grn_obj *sortby; - grn_obj *output_columns; - grn_obj *offset; - grn_obj *limit; - grn_obj *calc_types; - grn_obj *calc_target; - - label_len = grn_vector_get_element(ctx, &drilldown_labels, i, - &label, NULL, NULL); - drilldown->label = label; - drilldown->label_len = label_len; - -#define GET_VAR(name)\ - grn_snprintf(key_name, \ - GRN_TABLE_MAX_KEY_SIZE, \ - GRN_TABLE_MAX_KEY_SIZE, \ - "drilldown[%.*s]." # name, label_len, label); \ - name = GRN_PROC_GET_VAR(key_name); - - GET_VAR(keys); - GET_VAR(sortby); - GET_VAR(output_columns); - GET_VAR(offset); - GET_VAR(limit); - GET_VAR(calc_types); - GET_VAR(calc_target); - -#undef GET_VAR - - drilldown_info_fill(ctx, drilldown, - keys, sortby, output_columns, offset, limit, - calc_types, calc_target); - } - } - if (grn_select(ctx, GRN_TEXT_VALUE(VAR(0)), GRN_TEXT_LEN(VAR(0)), - GRN_TEXT_VALUE(VAR(1)), GRN_TEXT_LEN(VAR(1)), - GRN_TEXT_VALUE(VAR(2)), GRN_TEXT_LEN(VAR(2)), - GRN_TEXT_VALUE(VAR(3)), GRN_TEXT_LEN(VAR(3)), - GRN_TEXT_VALUE(VAR(4)), GRN_TEXT_LEN(VAR(4)), - GRN_TEXT_VALUE(VAR(5)), GRN_TEXT_LEN(VAR(5)), - output_columns, output_columns_len, - offset, limit, - drilldowns, n_drilldowns, - GRN_TEXT_VALUE(VAR(14)), GRN_TEXT_LEN(VAR(14)), - GRN_TEXT_VALUE(VAR(15)), GRN_TEXT_LEN(VAR(15)), - GRN_TEXT_VALUE(query_expander), GRN_TEXT_LEN(query_expander), - GRN_TEXT_VALUE(VAR(17)), GRN_TEXT_LEN(VAR(17)), - GRN_TEXT_VALUE(adjuster), GRN_TEXT_LEN(adjuster))) { - } - GRN_OBJ_FIN(ctx, &drilldown_labels); -#undef MAX_N_DRILLDOWNS - - return NULL; -} - -static grn_obj * -proc_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_TEXT_SET(ctx, &((vars + i)->value), - GRN_TEXT_VALUE(VAR(i)), GRN_TEXT_LEN(VAR(i))); - } - grn_proc_create(ctx, - GRN_TEXT_VALUE(VAR(0)), GRN_TEXT_LEN(VAR(0)), - GRN_PROC_COMMAND, proc_select, NULL, NULL, nvars - 1, vars + 1); - GRN_OUTPUT_BOOL(!ctx->rc); - return NULL; -} - static grn_obj * proc_load(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) { @@ -4262,10 +3256,12 @@ run_query(grn_ctx *ctx, grn_obj *table, if (query_expander_name && query_expander_name->header.domain == GRN_DB_TEXT && GRN_TEXT_LEN(query_expander_name) > 0) { - rc = expand_query(ctx, query_string, query_string_len, flags, - GRN_TEXT_VALUE(query_expander_name), - GRN_TEXT_LEN(query_expander_name), - &expanded_query); + 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), + &expanded_query); if (rc != GRN_SUCCESS) { GRN_OBJ_FIN(ctx, &expanded_query); goto exit; @@ -5387,7 +4383,7 @@ proc_range_filter(grn_ctx *ctx, int nargs, grn_obj **args, } real_limit = GRN_INT32_VALUE(&int32_value); } else { - real_limit = DEFAULT_LIMIT; + real_limit = GRN_SELECT_DEFAULT_LIMIT; } GRN_OBJ_FIN(ctx, &int32_value); @@ -5528,13 +4524,13 @@ proc_range_filter(grn_ctx *ctx, int nargs, grn_obj **args, 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 = DEFAULT_OUTPUT_COLUMNS; + raw_output_columns = GRN_SELECT_DEFAULT_OUTPUT_COLUMNS; raw_output_columns_len = strlen(raw_output_columns); } - grn_select_output_columns(ctx, res, -1, real_offset, real_limit, - raw_output_columns, - raw_output_columns_len, - filter_expr); + grn_proc_select_output_columns(ctx, res, -1, real_offset, real_limit, + raw_output_columns, + raw_output_columns_len, + filter_expr); } exit : @@ -6118,34 +5114,10 @@ exit : void grn_db_init_builtin_query(grn_ctx *ctx) { - grn_expr_var vars[23]; + grn_expr_var vars[10]; - DEF_VAR(vars[0], "name"); - DEF_VAR(vars[1], "table"); - DEF_VAR(vars[2], "match_columns"); - DEF_VAR(vars[3], "query"); - DEF_VAR(vars[4], "filter"); - DEF_VAR(vars[5], "scorer"); - DEF_VAR(vars[6], "sortby"); - DEF_VAR(vars[7], "output_columns"); - DEF_VAR(vars[8], "offset"); - DEF_VAR(vars[9], "limit"); - DEF_VAR(vars[10], "drilldown"); - DEF_VAR(vars[11], "drilldown_sortby"); - DEF_VAR(vars[12], "drilldown_output_columns"); - DEF_VAR(vars[13], "drilldown_offset"); - DEF_VAR(vars[14], "drilldown_limit"); - DEF_VAR(vars[15], "cache"); - DEF_VAR(vars[16], "match_escalation_threshold"); - /* Deprecated. Use query_expander instead. */ - DEF_VAR(vars[17], "query_expansion"); - DEF_VAR(vars[18], "query_flags"); - DEF_VAR(vars[19], "query_expander"); - DEF_VAR(vars[20], "adjuster"); - DEF_VAR(vars[21], "drilldown_calc_types"); - DEF_VAR(vars[22], "drilldown_calc_target"); - DEF_COMMAND("define_selector", proc_define_selector, 23, vars); - DEF_COMMAND("select", proc_select, 22, vars + 1); + grn_proc_init_define_selector(ctx); + grn_proc_init_select(ctx); DEF_VAR(vars[0], "values"); DEF_VAR(vars[1], "table"); Added: lib/proc/proc_select.c (+1189 -0) 100644 =================================================================== --- /dev/null +++ lib/proc/proc_select.c 2016-02-26 00:12:06 +0900 (65cc8b5) @@ -0,0 +1,1189 @@ +/* -*- 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-1301 USA +*/ + +#include "../grn_proc.h" +#include "../grn_expr.h" +#include "../grn_str.h" +#include "../grn_output.h" +#include "../grn_util.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" + +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, + grn_obj *expanded_query) +{ + 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, + "nonexistent query expansion column: <%.*s>", + query_expander_name_len, query_expander_name); + return ctx->rc; + } + + return grn_expr_syntax_expand_query(ctx, query, query_len, flags, + query_expander, expanded_query); +} + +static grn_expr_flags +grn_parse_query_flags(grn_ctx *ctx, const char *query_flags, + unsigned int query_flags_len) +{ + grn_expr_flags flags = 0; + const char *query_flags_end = query_flags + query_flags_len; + + while (query_flags < query_flags_end) { + if (*query_flags == '|' || *query_flags == ' ') { + query_flags += 1; + continue; + } + +#define CHECK_EXPR_FLAG(name)\ + if (((query_flags_end - query_flags) >= (sizeof(#name) - 1)) &&\ + (!memcmp(query_flags, #name, 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); + +#define GRN_EXPR_NONE 0 + CHECK_EXPR_FLAG(NONE); +#undef GNR_EXPR_NONE + + GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, + "invalid query flag: <%.*s>", + (int)(query_flags_end - query_flags), + query_flags); + return 0; +#undef CHECK_EXPR_FLAG + } + + return flags; +} + +static int +grn_select_apply_adjuster_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_adjust(grn_ctx *ctx, grn_obj *table, grn_obj *res, + 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); + ERR(GRN_INVALID_ARGUMENT, + "adjuster requires index column for the target column: <%.*s>", + column_name_size, column_name); + return; + } + + factor_value = grn_select_apply_adjuster_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, res, GRN_OP_ADJUST, &options); + } +} + +static void +grn_select_apply_adjuster(grn_ctx *ctx, grn_obj *table, grn_obj *res, + 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_adjust(ctx, table, res, column, value, factor); + } +} + +void +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_rc rc; + grn_obj_format format; + + 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, res, columns, columns_len); + if (rc != GRN_SUCCESS) { + GRN_OBJ_FORMAT_FIN(ctx, &format); + return; + } + + if (format.expression) { + grn_obj *condition_ptr; + condition_ptr = + grn_expr_get_or_add_var(ctx, format.expression, + GRN_SELECT_INTERNAL_VAR_CONDITION, + strlen(GRN_SELECT_INTERNAL_VAR_CONDITION)); + GRN_PTR_INIT(condition_ptr, 0, GRN_DB_OBJECT); + GRN_PTR_SET(ctx, condition_ptr, condition); + } + GRN_OUTPUT_OBJ(res, &format); + GRN_OBJ_FORMAT_FIN(ctx, &format); +} + +typedef struct { + const char *label; + unsigned int label_len; + const char *keys; + unsigned int keys_len; + const char *sortby; + unsigned int sortby_len; + const char *output_columns; + unsigned int output_columns_len; + int offset; + int limit; + grn_table_group_flags calc_types; + const char *calc_target_name; + unsigned int calc_target_name_len; +} drilldown_info; + +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 (((calc_types_end - calc_types) >= (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 + + ERR(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 void +drilldown_info_fill(grn_ctx *ctx, + drilldown_info *drilldown, + grn_obj *keys, + grn_obj *sortby, + grn_obj *output_columns, + grn_obj *offset, + grn_obj *limit, + grn_obj *calc_types, + grn_obj *calc_target) +{ + if (keys) { + drilldown->keys = GRN_TEXT_VALUE(keys); + drilldown->keys_len = GRN_TEXT_LEN(keys); + } else { + drilldown->keys = NULL; + drilldown->keys_len = 0; + } + + if (sortby) { + drilldown->sortby = GRN_TEXT_VALUE(sortby); + drilldown->sortby_len = GRN_TEXT_LEN(sortby); + } else { + drilldown->sortby = NULL; + drilldown->sortby_len = 0; + } + + if (output_columns) { + drilldown->output_columns = GRN_TEXT_VALUE(output_columns); + drilldown->output_columns_len = GRN_TEXT_LEN(output_columns); + } else { + drilldown->output_columns = NULL; + drilldown->output_columns_len = 0; + } + if (!drilldown->output_columns_len) { + drilldown->output_columns = DEFAULT_DRILLDOWN_OUTPUT_COLUMNS; + drilldown->output_columns_len = 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; + } + + if (calc_target && GRN_TEXT_LEN(calc_target)) { + drilldown->calc_target_name = GRN_TEXT_VALUE(calc_target); + drilldown->calc_target_name_len = GRN_TEXT_LEN(calc_target); + } else { + drilldown->calc_target_name = NULL; + drilldown->calc_target_name_len = 0; + } +} + +static void +grn_select_drilldown(grn_ctx *ctx, grn_obj *table, + grn_table_sort_key *keys, uint32_t n_keys, + drilldown_info *drilldown) +{ + uint32_t i; + for (i = 0; i < n_keys; i++) { + grn_table_group_result g = {NULL, 0, 0, 1, GRN_TABLE_GROUP_CALC_COUNT, 0}; + uint32_t n_hits; + int offset; + int limit; + + if (drilldown->calc_target_name) { + g.calc_target = grn_obj_column(ctx, table, + drilldown->calc_target_name, + drilldown->calc_target_name_len); + } + if (g.calc_target) { + g.flags |= drilldown->calc_types; + } + + grn_table_group(ctx, table, &keys[i], 1, &g, 1); + if (ctx->rc != GRN_SUCCESS) { + break; + } + n_hits = grn_table_size(ctx, g.table); + + offset = drilldown->offset; + limit = drilldown->limit; + grn_normalize_offset_and_limit(ctx, n_hits, &offset, &limit); + + if (drilldown->sortby_len) { + grn_table_sort_key *sort_keys; + uint32_t n_sort_keys; + sort_keys = grn_table_sort_key_from_str(ctx, + drilldown->sortby, + drilldown->sortby_len, + g.table, &n_sort_keys); + if (sort_keys) { + grn_obj *sorted; + sorted = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_NO_KEY, + NULL, g.table); + if (sorted) { + grn_obj_format format; + grn_table_sort(ctx, g.table, offset, limit, + sorted, sort_keys, n_sort_keys); + GRN_OBJ_FORMAT_INIT(&format, n_hits, 0, limit, offset); + format.flags = + GRN_OBJ_FORMAT_WITH_COLUMN_NAMES| + GRN_OBJ_FORMAT_XML_ELEMENT_NAVIGATIONENTRY; + grn_obj_columns(ctx, sorted, + drilldown->output_columns, + drilldown->output_columns_len, + &format.columns); + GRN_OUTPUT_OBJ(sorted, &format); + GRN_OBJ_FORMAT_FIN(ctx, &format); + grn_obj_unlink(ctx, sorted); + } + grn_table_sort_key_close(ctx, sort_keys, n_sort_keys); + } + } else { + grn_obj_format format; + GRN_OBJ_FORMAT_INIT(&format, n_hits, offset, limit, offset); + format.flags = + GRN_OBJ_FORMAT_WITH_COLUMN_NAMES| + GRN_OBJ_FORMAT_XML_ELEMENT_NAVIGATIONENTRY; + grn_obj_columns(ctx, g.table, + drilldown->output_columns, + drilldown->output_columns_len, + &format.columns); + GRN_OUTPUT_OBJ(g.table, &format); + GRN_OBJ_FORMAT_FIN(ctx, &format); + } + grn_obj_unlink(ctx, g.table); + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "drilldown(%d)", n_hits); + } +} + +static void +grn_select_drilldowns(grn_ctx *ctx, grn_obj *table, + drilldown_info *drilldowns, unsigned int n_drilldowns, + grn_obj *condition) +{ + unsigned int i; + + /* TODO: Remove invalid key drilldowns from the count. */ + GRN_OUTPUT_MAP_OPEN("DRILLDOWNS", n_drilldowns); + for (i = 0; i < n_drilldowns; i++) { + drilldown_info *drilldown = &(drilldowns[i]); + grn_table_sort_key *keys = NULL; + unsigned int n_keys; + uint32_t n_hits; + int offset; + int limit; + grn_table_group_result result = { + NULL, 0, 0, 1, GRN_TABLE_GROUP_CALC_COUNT, 0, 0, NULL + }; + + keys = grn_table_sort_key_from_str(ctx, + drilldown->keys, + drilldown->keys_len, + table, &n_keys); + if (!keys) { + continue; + } + + GRN_OUTPUT_STR(drilldown->label, drilldown->label_len); + + result.key_begin = 0; + result.key_end = n_keys - 1; + if (n_keys > 1) { + result.max_n_subrecs = 1; + } + if (drilldown->calc_target_name) { + result.calc_target = grn_obj_column(ctx, table, + drilldown->calc_target_name, + drilldown->calc_target_name_len); + } + if (result.calc_target) { + result.flags |= drilldown->calc_types; + } + + grn_table_group(ctx, table, keys, n_keys, &result, 1); + n_hits = grn_table_size(ctx, result.table); + + offset = drilldown->offset; + limit = drilldown->limit; + grn_normalize_offset_and_limit(ctx, n_hits, &offset, &limit); + + if (drilldown->sortby_len) { + grn_table_sort_key *sort_keys; + uint32_t n_sort_keys; + sort_keys = grn_table_sort_key_from_str(ctx, + drilldown->sortby, + drilldown->sortby_len, + result.table, &n_sort_keys); + if (sort_keys) { + grn_obj *sorted; + sorted = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_NO_KEY, + NULL, result.table); + if (sorted) { + grn_table_sort(ctx, result.table, offset, limit, + sorted, sort_keys, n_sort_keys); + grn_proc_select_output_columns(ctx, sorted, n_hits, 0, limit, + drilldown->output_columns, + drilldown->output_columns_len, + condition); + grn_obj_unlink(ctx, sorted); + } + grn_table_sort_key_close(ctx, sort_keys, n_sort_keys); + } + } else { + grn_proc_select_output_columns(ctx, result.table, n_hits, offset, limit, + drilldown->output_columns, + drilldown->output_columns_len, + condition); + } + + grn_table_sort_key_close(ctx, keys, n_keys); + if (result.calc_target) { + grn_obj_unlink(ctx, result.calc_target); + } + grn_obj_unlink(ctx, result.table); + + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "drilldown(%d)[%.*s]", n_hits, + (int)(drilldown->label_len), drilldown->label); + } + GRN_OUTPUT_MAP_CLOSE(); +} + +static grn_rc +grn_select(grn_ctx *ctx, const char *table, unsigned int table_len, + const char *match_columns, unsigned int match_columns_len, + const char *query, unsigned int query_len, + const char *filter, unsigned int filter_len, + const char *scorer, unsigned int scorer_len, + const char *sortby, unsigned int sortby_len, + const char *output_columns, unsigned int output_columns_len, + int offset, int limit, + drilldown_info *drilldowns, + unsigned int n_drilldowns, + const char *cache, unsigned int cache_len, + const char *match_escalation_threshold, unsigned int match_escalation_threshold_len, + const char *query_expander, unsigned int query_expander_len, + const char *query_flags, unsigned int query_flags_len, + const char *adjuster, unsigned int adjuster_len) +{ + uint32_t nkeys, nhits; + uint16_t cacheable = 1, taintable = 0; + grn_table_sort_key *keys; + grn_obj *outbuf = ctx->impl->output.buf; + grn_content_type output_type = ctx->impl->output.type; + grn_obj *table_, *match_columns_ = NULL, *cond = NULL, *scorer_, *res = NULL, *sorted; + 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); + + { + const char *query_end = query + query_len; + int space_len; + while (query < query_end) { + space_len = grn_isspace(query, ctx->encoding); + if (space_len == 0) { + break; + } + query += space_len; + query_len -= space_len; + } + } + + cache_key_size = table_len + 1 + match_columns_len + 1 + query_len + 1 + + filter_len + 1 + scorer_len + 1 + sortby_len + 1 + output_columns_len + 1 + + match_escalation_threshold_len + 1 + + query_expander_len + 1 + query_flags_len + 1 + adjuster_len + 1 + + sizeof(grn_content_type) + sizeof(int) * 2; + { + unsigned int i; + for (i = 0; i < n_drilldowns; i++) { + drilldown_info *drilldown = &(drilldowns[i]); + cache_key_size += + drilldown->keys_len + 1 + + drilldown->sortby_len + 1 + + drilldown->output_columns_len + 1 + + sizeof(int) * 2; + } + } + if (cache_key_size <= GRN_CACHE_MAX_KEY_SIZE) { + grn_obj *cache_value; + char *cp = cache_key; + grn_memcpy(cp, table, table_len); + cp += table_len; *cp++ = '\0'; + grn_memcpy(cp, match_columns, match_columns_len); + cp += match_columns_len; *cp++ = '\0'; + grn_memcpy(cp, query, query_len); + cp += query_len; *cp++ = '\0'; + grn_memcpy(cp, filter, filter_len); + cp += filter_len; *cp++ = '\0'; + grn_memcpy(cp, scorer, scorer_len); + cp += scorer_len; *cp++ = '\0'; + grn_memcpy(cp, sortby, sortby_len); + cp += sortby_len; *cp++ = '\0'; + grn_memcpy(cp, output_columns, output_columns_len); + cp += output_columns_len; *cp++ = '\0'; + { + unsigned int i; + for (i = 0; i < n_drilldowns; i++) { + drilldown_info *drilldown = &(drilldowns[i]); + grn_memcpy(cp, drilldown->keys, drilldown->keys_len); + cp += drilldown->keys_len; *cp++ = '\0'; + grn_memcpy(cp, drilldown->sortby, drilldown->sortby_len); + cp += drilldown->sortby_len; *cp++ = '\0'; + grn_memcpy(cp, drilldown->output_columns, drilldown->output_columns_len); + cp += drilldown->output_columns_len; *cp++ = '\0'; + } + } + grn_memcpy(cp, match_escalation_threshold, match_escalation_threshold_len); + cp += match_escalation_threshold_len; *cp++ = '\0'; + grn_memcpy(cp, query_expander, query_expander_len); + cp += query_expander_len; *cp++ = '\0'; + grn_memcpy(cp, query_flags, query_flags_len); + cp += query_flags_len; *cp++ = '\0'; + grn_memcpy(cp, adjuster, adjuster_len); + cp += adjuster_len; *cp++ = '\0'; + grn_memcpy(cp, &output_type, sizeof(grn_content_type)); + cp += sizeof(grn_content_type); + grn_memcpy(cp, &offset, sizeof(int)); + cp += sizeof(int); + grn_memcpy(cp, &limit, sizeof(int)); + cp += sizeof(int); + { + unsigned int i; + for (i = 0; i < n_drilldowns; i++) { + drilldown_info *drilldown = &(drilldowns[i]); + grn_memcpy(cp, &(drilldown->offset), sizeof(int)); + cp += sizeof(int); + grn_memcpy(cp, &(drilldown->limit), sizeof(int)); + cp += sizeof(int); + } + } + cache_value = grn_cache_fetch(ctx, cache_obj, cache_key, cache_key_size); + if (cache_value) { + GRN_TEXT_PUT(ctx, outbuf, + GRN_TEXT_VALUE(cache_value), + GRN_TEXT_LEN(cache_value)); + grn_cache_unref(ctx, cache_obj, cache_key, cache_key_size); + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_CACHE, + ":", "cache(%" GRN_FMT_LLD ")", + (long long int)GRN_TEXT_LEN(cache_value)); + return ctx->rc; + } + } + if (match_escalation_threshold_len) { + const char *end, *rest; + original_threshold = grn_ctx_get_match_escalation_threshold(ctx); + end = match_escalation_threshold + match_escalation_threshold_len; + threshold = grn_atoll(match_escalation_threshold, end, &rest); + if (end == rest) { + grn_ctx_set_match_escalation_threshold(ctx, threshold); + } + } + if ((table_ = grn_ctx_get(ctx, table, table_len))) { + // match_columns_ = grn_obj_column(ctx, table_, match_columns, match_columns_len); + if (filter_len && (filter[0] == '?') && + (ctx->impl->output.type == GRN_CONTENT_JSON)) { + ctx->rc = grn_ts_select(ctx, table_, filter + 1, filter_len - 1, + scorer, scorer_len, + sortby, sortby_len, + output_columns, output_columns_len, + offset, limit); + if (!ctx->rc && cacheable && cache_key_size <= GRN_CACHE_MAX_KEY_SIZE && + (!cache || cache_len != 2 || cache[0] != 'n' || cache[1] != 'o')) { + grn_cache_update(ctx, cache_obj, cache_key, cache_key_size, outbuf); + } + goto exit; + } + if (query_len || filter_len) { + grn_obj *v; + GRN_EXPR_CREATE_FOR_QUERY(ctx, table_, cond, v); + if (cond) { + if (match_columns_len) { + GRN_EXPR_CREATE_FOR_QUERY(ctx, table_, match_columns_, v); + if (match_columns_) { + grn_expr_parse(ctx, match_columns_, match_columns, match_columns_len, + NULL, GRN_OP_MATCH, GRN_OP_AND, + GRN_EXPR_SYNTAX_SCRIPT); + if (ctx->rc) { + goto exit; + } + } else { + /* todo */ + } + } + if (query_len) { + grn_expr_flags flags; + grn_obj query_expander_buf; + flags = GRN_EXPR_SYNTAX_QUERY; + if (query_flags_len) { + flags |= grn_parse_query_flags(ctx, query_flags, query_flags_len); + if (ctx->rc) { + goto exit; + } + } else { + flags |= GRN_EXPR_ALLOW_PRAGMA|GRN_EXPR_ALLOW_COLUMN; + } + GRN_TEXT_INIT(&query_expander_buf, 0); + if (query_expander_len) { + grn_rc rc; + rc = grn_proc_syntax_expand_query(ctx, + query, query_len, flags, + query_expander, query_expander_len, + &query_expander_buf); + 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); + goto exit; + } + } + grn_expr_parse(ctx, cond, query, query_len, + match_columns_, GRN_OP_MATCH, GRN_OP_AND, flags); + GRN_OBJ_FIN(ctx, &query_expander_buf); + if (!ctx->rc && filter_len) { + grn_expr_parse(ctx, cond, filter, filter_len, + match_columns_, GRN_OP_MATCH, GRN_OP_AND, + GRN_EXPR_SYNTAX_SCRIPT); + if (!ctx->rc) { grn_expr_append_op(ctx, cond, GRN_OP_AND, 2); } + } + } else { + grn_expr_parse(ctx, cond, filter, filter_len, + match_columns_, GRN_OP_MATCH, GRN_OP_AND, + GRN_EXPR_SYNTAX_SCRIPT); + } + cacheable *= ((grn_expr *)cond)->cacheable; + taintable += ((grn_expr *)cond)->taintable; + /* + grn_obj strbuf; + GRN_TEXT_INIT(&strbuf, 0); + grn_expr_inspect(ctx, &strbuf, cond); + GRN_TEXT_PUTC(ctx, &strbuf, '\0'); + GRN_LOG(ctx, GRN_LOG_NOTICE, "query=(%s)", GRN_TEXT_VALUE(&strbuf)); + GRN_OBJ_FIN(ctx, &strbuf); + */ + if (!ctx->rc) { res = grn_table_select(ctx, table_, cond, NULL, GRN_OP_OR); } + } else { + /* todo */ + ERRCLR(ctx); + } + } else { + res = table_; + } + nhits = res ? grn_table_size(ctx, res) : 0; + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "select(%d)", nhits); + + if (res) { + uint32_t ngkeys; + grn_table_sort_key *gkeys = NULL; + int result_size = 1; + if (!ctx->rc && n_drilldowns > 0) { + if (n_drilldowns == 1 && !drilldowns[0].label) { + gkeys = grn_table_sort_key_from_str(ctx, + drilldowns[0].keys, + drilldowns[0].keys_len, + res, &ngkeys); + if (gkeys) { + result_size += ngkeys; + } + } else { + result_size += 1; + } + } + + if (adjuster && adjuster_len) { + grn_obj *adjuster_; + grn_obj *v; + GRN_EXPR_CREATE_FOR_QUERY(ctx, table_, adjuster_, v); + if (adjuster_ && v) { + grn_rc rc; + rc = grn_expr_parse(ctx, adjuster_, adjuster, adjuster_len, NULL, + GRN_OP_MATCH, GRN_OP_ADJUST, + GRN_EXPR_SYNTAX_ADJUSTER); + if (rc) { + grn_obj_unlink(ctx, adjuster_); + goto exit; + } + cacheable *= ((grn_expr *)adjuster_)->cacheable; + taintable += ((grn_expr *)adjuster_)->taintable; + grn_select_apply_adjuster(ctx, table_, res, adjuster_); + grn_obj_unlink(ctx, adjuster_); + } + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "adjust(%d)", nhits); + } + + if (scorer && scorer_len) { + grn_obj *v; + GRN_EXPR_CREATE_FOR_QUERY(ctx, res, scorer_, v); + if (scorer_ && v) { + grn_table_cursor *tc; + grn_expr_parse(ctx, scorer_, scorer, scorer_len, NULL, GRN_OP_MATCH, GRN_OP_AND, + GRN_EXPR_SYNTAX_SCRIPT|GRN_EXPR_ALLOW_UPDATE); + cacheable *= ((grn_expr *)scorer_)->cacheable; + taintable += ((grn_expr *)scorer_)->taintable; + if ((tc = grn_table_cursor_open(ctx, res, NULL, 0, NULL, 0, 0, -1, 0))) { + grn_id id; + while ((id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL) { + GRN_RECORD_SET(ctx, v, id); + grn_expr_exec(ctx, scorer_, 0); + if (ctx->rc) { + break; + } + } + grn_table_cursor_close(ctx, tc); + } + grn_obj_unlink(ctx, scorer_); + } + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "score(%d)", nhits); + } + + GRN_OUTPUT_ARRAY_OPEN("RESULT", result_size); + + grn_normalize_offset_and_limit(ctx, nhits, &offset, &limit); + + if (sortby_len && + (keys = grn_table_sort_key_from_str(ctx, sortby, sortby_len, res, &nkeys))) { + if ((sorted = grn_table_create(ctx, NULL, 0, NULL, + GRN_OBJ_TABLE_NO_KEY, NULL, res))) { + grn_table_sort(ctx, res, offset, limit, sorted, keys, nkeys); + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "sort(%d)", limit); + grn_proc_select_output_columns(ctx, sorted, nhits, 0, limit, + output_columns, output_columns_len, + cond); + grn_obj_unlink(ctx, sorted); + } + grn_table_sort_key_close(ctx, keys, nkeys); + } else { + if (!ctx->rc) { + grn_proc_select_output_columns(ctx, res, nhits, offset, limit, + output_columns, output_columns_len, + cond); + } + } + GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE, + ":", "output(%d)", limit); + if (!ctx->rc) { + if (gkeys) { + drilldown_info *drilldown = &(drilldowns[0]); + grn_select_drilldown(ctx, res, gkeys, ngkeys, drilldown); + } else if (n_drilldowns > 0) { + grn_select_drilldowns(ctx, res, drilldowns, n_drilldowns, cond); + } + } + if (gkeys) { + grn_table_sort_key_close(ctx, gkeys, ngkeys); + } + if (res != table_) { grn_obj_unlink(ctx, res); } + } else { + GRN_OUTPUT_ARRAY_OPEN("RESULT", 0); + } + GRN_OUTPUT_ARRAY_CLOSE(); + if (!ctx->rc && cacheable && cache_key_size <= GRN_CACHE_MAX_KEY_SIZE + && (!cache || cache_len != 2 || *cache != 'n' || *(cache + 1) != 'o')) { + grn_cache_update(ctx, cache_obj, cache_key, cache_key_size, outbuf); + } + if (taintable) { grn_db_touch(ctx, DB_OBJ(table_)->db); } + grn_obj_unlink(ctx, table_); + } else { + ERR(GRN_INVALID_ARGUMENT, "invalid table name: <%.*s>", table_len, table); + } +exit : + if (match_escalation_threshold_len) { + grn_ctx_set_match_escalation_threshold(ctx, original_threshold); + } + if (match_columns_) { + grn_obj_unlink(ctx, match_columns_); + } + if (cond) { + grn_obj_unlink(ctx, cond); + } + /* GRN_LOG(ctx, GRN_LOG_NONE, "%d", ctx->seqno); */ + return ctx->rc; +} + +static void +proc_select_find_all_drilldown_labels(grn_ctx *ctx, grn_user_data *user_data, + grn_obj *labels) +{ + grn_obj *vars = GRN_PROC_GET_VARS(); + grn_table_cursor *cursor; + cursor = grn_table_cursor_open(ctx, vars, NULL, 0, NULL, 0, 0, -1, 0); + if (cursor) { + const char *prefix = "drilldown["; + int prefix_len = strlen(prefix); + const char *suffix = "].keys"; + int suffix_len = strlen(suffix); + 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 + suffix_len)) { + continue; + } + if (strncmp(prefix, name, prefix_len) != 0) { + continue; + } + if (strncmp(suffix, name + name_len - suffix_len, suffix_len) != 0) { + continue; + } + grn_vector_add_element(ctx, labels, + name + prefix_len, + name_len - prefix_len - suffix_len, + 0, GRN_ID_NIL); + } + grn_table_cursor_close(ctx, cursor); + } +} + +static grn_obj * +command_select(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) +{ +#define MAX_N_DRILLDOWNS 10 + const char *table; + size_t table_len; + const char *match_columns; + size_t match_columns_len; + const char *query; + size_t query_len; + const char *filter; + size_t filter_len; + const char *scorer; + size_t scorer_len; + const char *sortby; + size_t sortby_len; + const char *output_columns; + size_t output_columns_len; + int32_t offset; + int32_t limit; + grn_obj *drilldown; + drilldown_info drilldowns[MAX_N_DRILLDOWNS]; + unsigned int n_drilldowns = 0; + grn_obj drilldown_labels; + const char *cache; + size_t cache_len; + const char *match_escalation_threshold; + size_t match_escalation_threshold_len; + const char *query_flags; + size_t query_flags_len; + const char *query_expander; + size_t query_expander_len; + const char *adjuster; + size_t adjuster_len; + + table = grn_plugin_proc_get_var_string(ctx, user_data, + "table", -1, + &table_len); + match_columns = grn_plugin_proc_get_var_string(ctx, user_data, + "match_columns", -1, + &match_columns_len); + query = grn_plugin_proc_get_var_string(ctx, user_data, + "query", -1, + &query_len); + filter = grn_plugin_proc_get_var_string(ctx, user_data, + "filter", -1, + &filter_len); + scorer = grn_plugin_proc_get_var_string(ctx, user_data, + "scorer", -1, + &scorer_len); + sortby = grn_plugin_proc_get_var_string(ctx, user_data, + "sortby", -1, + &sortby_len); + output_columns = grn_plugin_proc_get_var_string(ctx, user_data, + "output_columns", -1, + &output_columns_len); + if (!output_columns) { + output_columns = GRN_SELECT_DEFAULT_OUTPUT_COLUMNS; + output_columns_len = strlen(GRN_SELECT_DEFAULT_OUTPUT_COLUMNS); + } + offset = grn_plugin_proc_get_var_int32(ctx, user_data, + "offset", -1, + 0); + limit = grn_plugin_proc_get_var_int32(ctx, user_data, + "limit", -1, + GRN_SELECT_DEFAULT_LIMIT); + + cache = grn_plugin_proc_get_var_string(ctx, user_data, + "cache", -1, + &cache_len); + match_escalation_threshold = + grn_plugin_proc_get_var_string(ctx, user_data, + "match_escalation_threshold", -1, + &match_escalation_threshold_len); + query_expander = grn_plugin_proc_get_var_string(ctx, user_data, + "query_expander", -1, + &query_expander_len); + if (!query_expander) { + query_expander = grn_plugin_proc_get_var_string(ctx, user_data, + "query_expansion", -1, + &query_expander_len); + } + query_flags = grn_plugin_proc_get_var_string(ctx, user_data, + "query_flags", -1, + &query_flags_len); + + adjuster = grn_plugin_proc_get_var_string(ctx, user_data, + "adjuster", -1, + &adjuster_len); + + GRN_TEXT_INIT(&drilldown_labels, GRN_OBJ_VECTOR); + drilldown = grn_plugin_proc_get_var(ctx, user_data, + "drilldown", -1); + if (GRN_TEXT_LEN(drilldown) > 0) { + drilldown_info *drilldown_info = &(drilldowns[0]); + drilldown_info->label = NULL; + drilldown_info->label_len = 0; + drilldown_info_fill(ctx, + drilldown_info, + drilldown, + grn_plugin_proc_get_var(ctx, user_data, + "drilldown_sortby", -1), + 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)); + n_drilldowns++; + } else { + unsigned int i; + proc_select_find_all_drilldown_labels(ctx, user_data, &drilldown_labels); + n_drilldowns = grn_vector_size(ctx, &drilldown_labels); + for (i = 0; i < n_drilldowns; i++) { + drilldown_info *drilldown = &(drilldowns[i]); + const char *label; + int label_len; + char key_name[GRN_TABLE_MAX_KEY_SIZE]; + grn_obj *keys; + grn_obj *sortby; + grn_obj *output_columns; + grn_obj *offset; + grn_obj *limit; + grn_obj *calc_types; + grn_obj *calc_target; + + label_len = grn_vector_get_element(ctx, &drilldown_labels, i, + &label, NULL, NULL); + drilldown->label = label; + drilldown->label_len = label_len; + +#define GET_VAR(name)\ + grn_snprintf(key_name, \ + GRN_TABLE_MAX_KEY_SIZE, \ + GRN_TABLE_MAX_KEY_SIZE, \ + "drilldown[%.*s]." # name, label_len, label); \ + name = grn_plugin_proc_get_var(ctx, user_data, key_name, -1); + + GET_VAR(keys); + GET_VAR(sortby); + GET_VAR(output_columns); + GET_VAR(offset); + GET_VAR(limit); + GET_VAR(calc_types); + GET_VAR(calc_target); + +#undef GET_VAR + + drilldown_info_fill(ctx, drilldown, + keys, sortby, output_columns, offset, limit, + calc_types, calc_target); + } + } + if (grn_select(ctx, + table, table_len, + match_columns, match_columns_len, + query, query_len, + filter, filter_len, + scorer, scorer_len, + sortby, sortby_len, + output_columns, output_columns_len, + offset, limit, + drilldowns, n_drilldowns, + cache, cache_len, + match_escalation_threshold, + match_escalation_threshold_len, + query_expander, query_expander_len, + query_flags, query_flags_len, + adjuster, adjuster_len)) { + } + GRN_OBJ_FIN(ctx, &drilldown_labels); +#undef MAX_N_DRILLDOWNS + + return NULL; +} + +#define N_VARS 23 +#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); + 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); + 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); +} + +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); +} Modified: lib/proc/sources.am (+1 -0) =================================================================== --- lib/proc/sources.am 2016-02-26 00:05:14 +0900 (a364382) +++ lib/proc/sources.am 2016-02-26 00:12:06 +0900 (81c68d2) @@ -5,6 +5,7 @@ libgrnproc_la_SOURCES = \ proc_lock.c \ proc_object.c \ proc_schema.c \ + proc_select.c \ proc_snippet.c \ proc_highlight.c \ proc_table.c