Kouhei Sutou
null+****@clear*****
Wed May 18 00:22:11 JST 2016
Kouhei Sutou 2016-05-18 00:22:11 +0900 (Wed, 18 May 2016) New Revision: e19bfde7bcf9cee483dc9c4267afff847ce14042 https://github.com/groonga/groonga/commit/e19bfde7bcf9cee483dc9c4267afff847ce14042 Message: Support window function row_number() is the only available window function for now. Here is an usage: table_create Items TABLE_HASH_KEY ShortText column_create Items price COLUMN_SCALAR UInt32 load --table Items [ {"_key": "item1", "price": 666}, {"_key": "item2", "price": 999}, {"_key": "item3", "price": 777}, {"_key": "item4", "price": 111}, {"_key": "item5", "price": 333}, {"_key": "item6", "price": 222} ] select Items \ --column[nth_row].stage initial \ --column[nth_row].value 'row_number()' \ --column[nth_row].type UInt32 \ --column[nth_row].window.sort_keys -price \ --output_columns '_key, price, nth_row' #[ # [ # 0, # 0.0, # 0.0 # ], # [ # [ # [ # 6 # ], # [ # [ # "_key", # "ShortText" # ], # [ # "price", # "UInt32" # ], # [ # "nth_row", # "UInt32" # ] # ], # [ # "item1", # 666, # 3 # ], # [ # "item2", # 999, # 1 # ], # [ # "item3", # 777, # 2 # ], # [ # "item4", # 111, # 6 # ], # [ # "item5", # 333, # 4 # ], # [ # "item6", # 222, # 5 # ] # ] # ] #] New APIs: * GRN_WINDOW_FUNCTION_ERROR * GRN_PROC_WINDOW_FUNCTION * grn_obj_is_window_function_proc() * grn_window_direction * grn_window * grn_window_next() * grn_window_rewind() * grn_window_set_direction() * grn_window_get_table() * grn_window_definition * grn_window_function_func * grn_window_function_create() * grn_table_apply_window_function() Added files: include/groonga/window_function.h lib/window_function.c lib/window_functions.c test/command/suite/select/column/window_function/row_number/ascending.expected test/command/suite/select/column/window_function/row_number/ascending.test test/command/suite/select/column/window_function/row_number/descending.expected test/command/suite/select/column/window_function/row_number/descending.test Copied files: lib/grn_window_function.h (from include/groonga.h) lib/grn_window_functions.h (from include/groonga.h) Modified files: include/groonga.h include/groonga/Makefile.am include/groonga/groonga.h include/groonga/obj.h lib/db.c lib/expr.c lib/grn_db.h lib/mrb/mrb_ctx.c lib/mrb/mrb_error.c lib/mrb/scripts/context/rc.rb lib/obj.c lib/proc/proc_select.c lib/sources.am lib/util.c test/unit/core/test-object.c Modified: include/groonga.h (+1 -0) =================================================================== --- include/groonga.h 2016-05-18 00:16:33 +0900 (c24eff6) +++ include/groonga.h 2016-05-18 00:22:11 +0900 (b219bdc) @@ -42,5 +42,6 @@ #include "groonga/time.h" #include "groonga/type.h" #include "groonga/util.h" +#include "groonga/window_function.h" #include "groonga/windows.h" #include "groonga/windows_event_logger.h" Modified: include/groonga/Makefile.am (+1 -0) =================================================================== --- include/groonga/Makefile.am 2016-05-18 00:16:33 +0900 (f9ac5ff) +++ include/groonga/Makefile.am 2016-05-18 00:22:11 +0900 (e753a01) @@ -31,5 +31,6 @@ groonga_include_HEADERS = \ nfkc.h \ normalizer.h \ util.h \ + window_function.h \ windows.h \ windows_event_logger.h Modified: include/groonga/groonga.h (+4 -2) =================================================================== --- include/groonga/groonga.h 2016-05-18 00:16:33 +0900 (4ea4b63) +++ include/groonga/groonga.h 2016-05-18 00:22:11 +0900 (fdd15fe) @@ -125,7 +125,8 @@ typedef enum { GRN_COMMAND_ERROR = -74, GRN_PLUGIN_ERROR = -75, GRN_SCORER_ERROR = -76, - GRN_CANCEL = -77 + GRN_CANCEL = -77, + GRN_WINDOW_FUNCTION_ERROR = -78 } grn_rc; GRN_API grn_rc grn_init(void); @@ -507,7 +508,8 @@ typedef enum { GRN_PROC_HOOK, GRN_PROC_NORMALIZER, GRN_PROC_TOKEN_FILTER, - GRN_PROC_SCORER + GRN_PROC_SCORER, + GRN_PROC_WINDOW_FUNCTION } grn_proc_type; GRN_API grn_obj *grn_proc_create(grn_ctx *ctx, Modified: include/groonga/obj.h (+1 -0) =================================================================== --- include/groonga/obj.h 2016-05-18 00:16:33 +0900 (4539467) +++ include/groonga/obj.h 2016-05-18 00:22:11 +0900 (e811d87) @@ -46,6 +46,7 @@ GRN_API grn_bool grn_obj_is_selector_only_proc(grn_ctx *ctx, grn_obj *obj); GRN_API grn_bool grn_obj_is_normalizer_proc(grn_ctx *ctx, grn_obj *obj); GRN_API grn_bool grn_obj_is_token_filter_proc(grn_ctx *ctx, grn_obj *obj); GRN_API grn_bool grn_obj_is_scorer_proc(grn_ctx *ctx, grn_obj *obj); +GRN_API grn_bool grn_obj_is_window_function_proc(grn_ctx *ctx, grn_obj *obj); GRN_API grn_rc grn_obj_cast(grn_ctx *ctx, grn_obj *src, Added: include/groonga/window_function.h (+67 -0) 100644 =================================================================== --- /dev/null +++ include/groonga/window_function.h 2016-05-18 00:22:11 +0900 (df9be41) @@ -0,0 +1,67 @@ +/* + 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 as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + 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 +*/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + GRN_WINDOW_DIRECTION_ASCENDING, + GRN_WINDOW_DIRECTION_DESCENDING +} grn_window_direction; + +typedef struct _grn_window grn_window; + +GRN_API grn_id grn_window_next(grn_ctx *ctx, + grn_window *window); +GRN_API grn_rc grn_window_rewind(grn_ctx *ctx, + grn_window *window); +GRN_API grn_rc grn_window_set_direction(grn_ctx *ctx, + grn_window *window, + grn_window_direction direction); +GRN_API grn_obj *grn_window_get_table(grn_ctx *ctx, + grn_window *window); + +typedef struct _grn_window_definition { + grn_table_sort_key *sort_keys; + size_t n_sort_keys; +} grn_window_definition; + +typedef grn_rc grn_window_function_func(grn_ctx *ctx, + grn_obj *output_column, + grn_window *window, + grn_obj *args, + int n_args); + +GRN_API grn_obj *grn_window_function_create(grn_ctx *ctx, + const char *name, + int name_size, + grn_window_function_func func); + + +GRN_API grn_rc grn_table_apply_window_function(grn_ctx *ctx, + grn_obj *table, + grn_obj *output_column, + grn_window_definition *definition, + grn_obj *window_function_call); + +#ifdef __cplusplus +} +#endif Modified: lib/db.c (+3 -0) =================================================================== --- lib/db.c 2016-05-18 00:16:33 +0900 (3781ebf) +++ lib/db.c 2016-05-18 00:22:11 +0900 (b09cc83) @@ -36,6 +36,7 @@ #include "grn_report.h" #include "grn_util.h" #include "grn_cache.h" +#include "grn_window_functions.h" #include <string.h> typedef struct { @@ -421,6 +422,7 @@ grn_db_open(grn_ctx *ctx, const char *path) grn_db_init_builtin_normalizers(ctx); grn_db_init_builtin_scorers(ctx); grn_db_init_builtin_commands(ctx); + grn_db_init_builtin_window_functions(ctx); if (grn_table_size(ctx, (grn_obj *)s) > n_records) { grn_obj_flush(ctx, (grn_obj *)s); @@ -11847,6 +11849,7 @@ grn_db_init_builtin_types(grn_ctx *ctx) grn_obj_register(ctx, db, buf, 5); } grn_db_init_builtin_commands(ctx); + grn_db_init_builtin_window_functions(ctx); for (id = grn_db_curr_id(ctx, db) + 1; id < GRN_N_RESERVED_TYPES; id++) { grn_itoh(id, buf + 3, 2); grn_obj_register(ctx, db, buf, 5); Modified: lib/expr.c (+2 -1) =================================================================== --- lib/expr.c 2016-05-18 00:16:33 +0900 (e1c82dc) +++ lib/expr.c 2016-05-18 00:22:11 +0900 (4e78d9c) @@ -885,7 +885,8 @@ grn_expr_append_obj(grn_ctx *ctx, grn_obj *expr, grn_obj *obj, grn_operator op, goto exit; } if (!(grn_obj_is_function_proc(ctx, proc) || - grn_obj_is_scorer_proc(ctx, proc))) { + grn_obj_is_scorer_proc(ctx, proc) || + grn_obj_is_window_function_proc(ctx, proc))) { grn_obj buffer; GRN_TEXT_INIT(&buffer, 0); Modified: lib/grn_db.h (+1 -0) =================================================================== --- lib/grn_db.h 2016-05-18 00:16:33 +0900 (18fe337) +++ lib/grn_db.h 2016-05-18 00:22:11 +0900 (6748e37) @@ -185,6 +185,7 @@ struct _grn_proc { struct { grn_scorer_score_func *score; } scorer; + grn_window_function_func *window_function; } callbacks; void *user_data; Copied: lib/grn_window_function.h (+20 -26) 50% =================================================================== --- include/groonga.h 2016-05-18 00:16:33 +0900 (c24eff6) +++ lib/grn_window_function.h 2016-05-18 00:22:11 +0900 (71c71e9) @@ -1,5 +1,5 @@ /* - Copyright(C) 2014-2016 Brazil + 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 @@ -18,29 +18,23 @@ #pragma once -#include "groonga/portability.h" -#include "groonga/groonga.h" +struct _grn_window { + grn_obj *table; + grn_obj ids; + size_t n_ids; + ssize_t current_index; + grn_window_direction direction; + grn_table_sort_key *sort_keys; + size_t n_sort_keys; +}; -#include "groonga/array.h" -#include "groonga/config.h" -#include "groonga/dat.h" -#include "groonga/db.h" -#include "groonga/dump.h" -#include "groonga/expr.h" -#include "groonga/file_reader.h" -#include "groonga/geo.h" -#include "groonga/hash.h" -#include "groonga/ii.h" -#include "groonga/obj.h" -#include "groonga/operator.h" -#include "groonga/output.h" -#include "groonga/pat.h" -#include "groonga/request_canceler.h" -#include "groonga/request_timer.h" -#include "groonga/table.h" -#include "groonga/thread.h" -#include "groonga/time.h" -#include "groonga/type.h" -#include "groonga/util.h" -#include "groonga/windows.h" -#include "groonga/windows_event_logger.h" +grn_rc grn_window_init(grn_ctx *ctx, grn_window *window, grn_obj *table); +grn_rc grn_window_fin(grn_ctx *ctx, grn_window *window); +grn_rc grn_window_set_sort_keys(grn_ctx *ctx, + grn_window *window, + grn_table_sort_key *sort_keys, + size_t n_sort_keys); + +#ifdef __cplusplus +} +#endif Copied: lib/grn_window_functions.h (+6 -26) 50% =================================================================== --- include/groonga.h 2016-05-18 00:16:33 +0900 (c24eff6) +++ lib/grn_window_functions.h 2016-05-18 00:22:11 +0900 (a9d1e6f) @@ -1,5 +1,5 @@ /* - Copyright(C) 2014-2016 Brazil + 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 @@ -18,29 +18,9 @@ #pragma once -#include "groonga/portability.h" -#include "groonga/groonga.h" +grn_rc grn_db_init_builtin_window_functions(grn_ctx *ctx); -#include "groonga/array.h" -#include "groonga/config.h" -#include "groonga/dat.h" -#include "groonga/db.h" -#include "groonga/dump.h" -#include "groonga/expr.h" -#include "groonga/file_reader.h" -#include "groonga/geo.h" -#include "groonga/hash.h" -#include "groonga/ii.h" -#include "groonga/obj.h" -#include "groonga/operator.h" -#include "groonga/output.h" -#include "groonga/pat.h" -#include "groonga/request_canceler.h" -#include "groonga/request_timer.h" -#include "groonga/table.h" -#include "groonga/thread.h" -#include "groonga/time.h" -#include "groonga/type.h" -#include "groonga/util.h" -#include "groonga/windows.h" -#include "groonga/windows_event_logger.h" + +#ifdef __cplusplus +} +#endif Modified: lib/mrb/mrb_ctx.c (+6 -0) =================================================================== --- lib/mrb/mrb_ctx.c 2016-05-18 00:16:33 +0900 (180ace2) +++ lib/mrb/mrb_ctx.c 2016-05-18 00:22:11 +0900 (64be7e7) @@ -735,6 +735,12 @@ grn_mrb_ctx_check(mrb_state *mrb) "cancel: <%s>(%d)", ctx->errbuf, ctx->rc); break; + case GRN_WINDOW_FUNCTION_ERROR: + error_class = mrb_class_get_under(mrb, module, "WindowFunctionError"); + grn_snprintf(message, MESSAGE_SIZE, MESSAGE_SIZE, + "window function error: <%s>(%d)", + ctx->errbuf, ctx->rc); + break; } if (!error_class) { Modified: lib/mrb/mrb_error.c (+2 -0) =================================================================== --- lib/mrb/mrb_error.c 2016-05-18 00:16:33 +0900 (910e642) +++ lib/mrb/mrb_error.c 2016-05-18 00:22:11 +0900 (661910f) @@ -194,5 +194,7 @@ grn_mrb_error_init(grn_ctx *ctx) groonga_error_class); mrb_define_class_under(mrb, module, "Cancel", groonga_error_class); + mrb_define_class_under(mrb, module, "WindowFunctionError", + groonga_error_class); } #endif Modified: lib/mrb/scripts/context/rc.rb (+2 -0) =================================================================== --- lib/mrb/scripts/context/rc.rb 2016-05-18 00:16:33 +0900 (8ed182e) +++ lib/mrb/scripts/context/rc.rb 2016-05-18 00:22:11 +0900 (f2fecbe) @@ -186,6 +186,8 @@ module Groonga register(:scorer_error, -76, ScorerError) CANCEL = register(:cancel, -77, Cancel) + WINDOW_FUNCTION_ERROR = + register(:window_function_error, -78, WindowFunctionError) GroongaError.rc = UNKNOWN_ERROR end Modified: lib/obj.c (+13 -0) =================================================================== --- lib/obj.c 2016-05-18 00:16:33 +0900 (20ae871) +++ lib/obj.c 2016-05-18 00:22:11 +0900 (0f1e1ce) @@ -296,6 +296,19 @@ grn_obj_is_scorer_proc(grn_ctx *ctx, grn_obj *obj) return proc->type == GRN_PROC_SCORER; } +grn_bool +grn_obj_is_window_function_proc(grn_ctx *ctx, grn_obj *obj) +{ + grn_proc *proc; + + if (!grn_obj_is_proc(ctx, obj)) { + return GRN_FALSE; + } + + proc = (grn_proc *)obj; + return proc->type == GRN_PROC_WINDOW_FUNCTION; +} + static void grn_db_reindex(grn_ctx *ctx, grn_obj *db) { Modified: lib/proc/proc_select.c (+80 -92) =================================================================== --- lib/proc/proc_select.c 2016-05-18 00:16:33 +0900 (e53d824) +++ lib/proc/proc_select.c 2016-05-18 00:22:11 +0900 (2164670) @@ -62,7 +62,9 @@ typedef struct { grn_obj *type; grn_obj_flags flags; grn_select_string value; - grn_select_string sortby; + struct { + grn_select_string sort_keys; + } window; } grn_column_data; typedef struct { @@ -221,8 +223,8 @@ grn_column_data_init(grn_ctx *ctx, column->flags = GRN_OBJ_COLUMN_SCALAR; column->value.value = NULL; column->value.length = 0; - column->sortby.value = NULL; - column->sortby.length = 0; + column->window.sort_keys.value = NULL; + column->window.sort_keys.length = 0; return GRN_TRUE; } @@ -232,7 +234,7 @@ grn_column_data_fill(grn_ctx *ctx, grn_obj *type_raw, grn_obj *flags, grn_obj *value, - grn_obj *sortby) + grn_obj *window_sort_keys) { if (type_raw && GRN_TEXT_LEN(type_raw) > 0) { grn_obj *type; @@ -289,7 +291,7 @@ grn_column_data_fill(grn_ctx *ctx, } GRN_SELECT_FILL_STRING(column->value, value); - GRN_SELECT_FILL_STRING(column->sortby, sortby); + GRN_SELECT_FILL_STRING(column->window.sort_keys, window_sort_keys); return GRN_TRUE; } @@ -314,7 +316,9 @@ grn_column_data_collect(grn_ctx *ctx, grn_obj *type; grn_obj *flags; grn_obj *value; - grn_obj *sortby; + struct { + grn_obj *sort_keys; + } window; grn_hash_cursor_get_value(ctx, cursor, (void **)&column); @@ -332,12 +336,12 @@ grn_column_data_collect(grn_ctx *ctx, GET_VAR(type); GET_VAR(flags); GET_VAR(value); - GET_VAR(sortby); + GET_VAR(window.sort_keys); #undef GET_VAR grn_column_data_fill(ctx, column, - type, flags, value, sortby); + type, flags, value, window.sort_keys); } grn_hash_cursor_close(ctx, cursor); return GRN_TRUE; @@ -766,11 +770,6 @@ grn_select_apply_columns(grn_ctx *ctx, 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_table_cursor *table_cursor; - grn_id id; - grn_obj *target_table = table; grn_hash_cursor_get_value(ctx, columns_cursor, (void **)&column_data); @@ -792,50 +791,11 @@ grn_select_apply_columns(grn_ctx *ctx, break; } - if (column_data->sortby.length > 0) { - grn_table_sort_key *sort_keys; - uint32_t n_sort_keys; - sort_keys = grn_table_sort_key_from_str(ctx, - column_data->sortby.value, - column_data->sortby.length, - table, &n_sort_keys); - if (!sort_keys) { - grn_obj_close(ctx, column); - GRN_PLUGIN_ERROR(ctx, - GRN_INVALID_ARGUMENT, - "[select][column][%s][%.*s] failed to parse sort key: %s", - grn_column_stage_name(column_data->stage), - (int)(column_data->label.length), - column_data->label.value, - ctx->errbuf); - break; - } - - target_table = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_NO_KEY, - NULL, table); - if (!target_table) { - grn_obj_close(ctx, column); - grn_table_sort_key_close(ctx, sort_keys, n_sort_keys); - GRN_PLUGIN_ERROR(ctx, - GRN_INVALID_ARGUMENT, - "[select][column][%s][%.*s] failed to create sorted table: %s", - grn_column_stage_name(column_data->stage), - (int)(column_data->label.length), - column_data->label.value, - ctx->errbuf); - break; - } - grn_table_sort(ctx, table, 0, -1, - target_table, sort_keys, n_sort_keys); - grn_table_sort_key_close(ctx, sort_keys, n_sort_keys); - } - - GRN_EXPR_CREATE_FOR_QUERY(ctx, target_table, expression, record); + grn_obj *expression; + grn_obj *record; + GRN_EXPR_CREATE_FOR_QUERY(ctx, table, expression, record); if (!expression) { grn_obj_close(ctx, column); - if (column_data->sortby.length > 0) { - grn_obj_unlink(ctx, target_table); - } GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, "[select][column][%s][%.*s] " @@ -857,9 +817,6 @@ grn_select_apply_columns(grn_ctx *ctx, if (ctx->rc != GRN_SUCCESS) { grn_obj_close(ctx, expression); grn_obj_close(ctx, column); - if (column_data->sortby.length > 0) { - grn_obj_unlink(ctx, target_table); - } GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, "[select][column][%s][%.*s] " @@ -874,46 +831,77 @@ grn_select_apply_columns(grn_ctx *ctx, } grn_select_expression_set_condition(ctx, expression, condition); - table_cursor = grn_table_cursor_open(ctx, target_table, - NULL, 0, - NULL, 0, - 0, -1, GRN_CURSOR_BY_ID); - if (!table_cursor) { - grn_obj_close(ctx, expression); - grn_obj_close(ctx, column); - if (column_data->sortby.length > 0) { - grn_obj_unlink(ctx, target_table); + if (column_data->window.sort_keys.length > 0) { + grn_window_definition definition; + int n_sort_keys; + grn_rc rc; + + 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; } - GRN_PLUGIN_ERROR(ctx, - GRN_INVALID_ARGUMENT, - "[select][column][%s][%.*s] " - "failed to create cursor for getting records: %s", - grn_column_stage_name(column_data->stage), - (int)(column_data->label.length), - column_data->label.value, - ctx->errbuf); - break; - } - while ((id = grn_table_cursor_next(ctx, table_cursor)) != GRN_ID_NIL) { - grn_obj *value; - - GRN_RECORD_SET(ctx, record, id); - value = grn_expr_exec(ctx, expression, 0); - if (value) { - grn_id target_record_id = id; - if (column_data->sortby.length > 0) { - void *sorted_table_value; - grn_table_cursor_get_value(ctx, table_cursor, &sorted_table_value); - target_record_id = *((grn_id *)sorted_table_value); + rc = grn_table_apply_window_function(ctx, + table, + column, + &definition, + expression); + grn_table_sort_key_close(ctx, + definition.sort_keys, + definition.n_sort_keys); + if (rc != GRN_SUCCESS) { + grn_obj_close(ctx, expression); + grn_obj_close(ctx, column); + break; + } + } else { + grn_table_cursor *table_cursor; + grn_id id; + + table_cursor = grn_table_cursor_open(ctx, table, + NULL, 0, + NULL, 0, + 0, -1, GRN_CURSOR_BY_ID); + if (!table_cursor) { + grn_obj_close(ctx, expression); + grn_obj_close(ctx, column); + GRN_PLUGIN_ERROR(ctx, + GRN_INVALID_ARGUMENT, + "[select][column][%s][%.*s] " + "failed to create cursor for getting records: %s", + grn_column_stage_name(column_data->stage), + (int)(column_data->label.length), + column_data->label.value, + ctx->errbuf); + break; + } + + while ((id = grn_table_cursor_next(ctx, table_cursor)) != GRN_ID_NIL) { + grn_obj *value; + + GRN_RECORD_SET(ctx, record, id); + value = grn_expr_exec(ctx, expression, 0); + if (value) { + grn_obj_set_value(ctx, column, id, value, GRN_OBJ_SET); } - grn_obj_set_value(ctx, column, target_record_id, value, GRN_OBJ_SET); } } - if (column_data->sortby.length > 0) { - grn_obj_unlink(ctx, target_table); - } grn_obj_close(ctx, expression); } Modified: lib/sources.am (+5 -1) =================================================================== --- lib/sources.am 2016-05-18 00:16:33 +0900 (5c960d4) +++ lib/sources.am 2016-05-18 00:22:11 +0900 (c0ef10b) @@ -91,4 +91,8 @@ libgroonga_la_SOURCES = \ grn_util.h \ windows.c \ windows_event_logger.c \ - file_reader.c + file_reader.c \ + window_function.c \ + grn_window_function.h \ + window_functions.c \ + grn_window_functions.h Modified: lib/util.c (+3 -0) =================================================================== --- lib/util.c 2016-05-18 00:16:33 +0900 (ccfcc51) +++ lib/util.c 2016-05-18 00:22:11 +0900 (9483474) @@ -271,6 +271,9 @@ grn_proc_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj) case GRN_PROC_SCORER : GRN_TEXT_PUTS(ctx, buf, "scorer"); break; + case GRN_PROC_WINDOW_FUNCTION : + GRN_TEXT_PUTS(ctx, buf, "window-function"); + break; } GRN_TEXT_PUTS(ctx, buf, " "); Added: lib/window_function.c (+344 -0) 100644 =================================================================== --- /dev/null +++ lib/window_function.c 2016-05-18 00:22:11 +0900 (41e04bd) @@ -0,0 +1,344 @@ +/* -*- 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-1301 USA +*/ + +#include "grn_ctx.h" +#include "grn_db.h" +#include "grn_expr.h" +#include "grn_window_function.h" + +#include <string.h> + +grn_rc +grn_window_init(grn_ctx *ctx, grn_window *window, grn_obj *table) +{ + GRN_API_ENTER; + + window->table = table; + GRN_RECORD_INIT(&(window->ids), GRN_OBJ_VECTOR, grn_obj_id(ctx, table)); + window->n_ids = 0; + window->current_index = 0; + window->direction = GRN_WINDOW_DIRECTION_ASCENDING; + window->sort_keys = NULL; + window->n_sort_keys = 0; + + GRN_API_RETURN(GRN_SUCCESS); +} + +grn_rc +grn_window_fin(grn_ctx *ctx, grn_window *window) +{ + GRN_API_ENTER; + + GRN_OBJ_FIN(ctx, &(window->ids)); + + GRN_API_RETURN(GRN_SUCCESS); +} + +grn_id +grn_window_next(grn_ctx *ctx, grn_window *window) +{ + grn_id next_id; + + GRN_API_ENTER; + + if (!window) { + GRN_API_RETURN(GRN_ID_NIL); + } + + if (window->direction == GRN_WINDOW_DIRECTION_ASCENDING) { + if (window->current_index >= window->n_ids) { + GRN_API_RETURN(GRN_ID_NIL); + } + } else { + if (window->current_index < 0) { + GRN_API_RETURN(GRN_ID_NIL); + } + } + + next_id = GRN_RECORD_VALUE_AT(&(window->ids), window->current_index); + if (window->direction == GRN_WINDOW_DIRECTION_ASCENDING) { + window->current_index++; + } else { + window->current_index--; + } + + GRN_API_RETURN(next_id); +} + +grn_rc +grn_window_rewind(grn_ctx *ctx, grn_window *window) +{ + GRN_API_ENTER; + + if (!window) { + ERR(GRN_INVALID_ARGUMENT, "[window][rewind] window is NULL"); + GRN_API_RETURN(ctx->rc); + } + + if (window->direction == GRN_WINDOW_DIRECTION_ASCENDING) { + window->current_index = 0; + } else { + window->current_index = window->n_ids - 1; + } + + GRN_API_RETURN(GRN_SUCCESS); +} + +grn_obj * +grn_window_get_table(grn_ctx *ctx, grn_window *window) +{ + GRN_API_ENTER; + + if (!window) { + ERR(GRN_INVALID_ARGUMENT, "[window][rewind] window is NULL"); + GRN_API_RETURN(NULL); + } + + GRN_API_RETURN(window->table); +} + +grn_rc +grn_window_set_direction(grn_ctx *ctx, + grn_window *window, + grn_window_direction direction) +{ + GRN_API_ENTER; + + if (!window) { + ERR(GRN_INVALID_ARGUMENT, "[window][set][direction] window is NULL"); + GRN_API_RETURN(ctx->rc); + } + + switch (direction) { + case GRN_WINDOW_DIRECTION_ASCENDING : + window->direction = direction; + window->current_index = 0; + break; + case GRN_WINDOW_DIRECTION_DESCENDING : + window->direction = direction; + window->current_index = window->n_ids - 1; + break; + default : + ERR(GRN_INVALID_ARGUMENT, + "[window][set][direction] direction must be " + "GRN_WINDOW_DIRECTION_ASCENDING(%d) or " + "GRN_WINDOW_DIRECTION_DESCENDING(%d): %d", + GRN_WINDOW_DIRECTION_ASCENDING, + GRN_WINDOW_DIRECTION_DESCENDING, + direction); + GRN_API_RETURN(ctx->rc); + break; + } + + GRN_API_RETURN(GRN_SUCCESS); +} + +grn_rc +grn_window_set_sort_keys(grn_ctx *ctx, + grn_window *window, + grn_table_sort_key *sort_keys, + size_t n_sort_keys) +{ + grn_obj *sorted; + + GRN_API_ENTER; + + if (!window) { + ERR(GRN_INVALID_ARGUMENT, "[window][set][sort-keys] window is NULL"); + GRN_API_RETURN(ctx->rc); + } + + sorted = grn_table_create(ctx, + NULL, 0, NULL, + GRN_OBJ_TABLE_NO_KEY, + NULL, window->table); + if (!sorted) { + ERR(ctx->rc, + "[window][set][sort-keys] " + "failed to create a table to store sorted records: %s", + ctx->errbuf); + GRN_API_RETURN(ctx->rc); + } + + grn_table_sort(ctx, window->table, 0, -1, sorted, sort_keys, n_sort_keys); + if (ctx->rc != GRN_SUCCESS) { + ERR(ctx->rc, + "[window][set][sort-keys] " + "failed to sort: %s", + ctx->errbuf); + grn_obj_unlink(ctx, sorted); + GRN_API_RETURN(ctx->rc); + } + + GRN_BULK_REWIND(&(window->ids)); + GRN_TABLE_EACH_BEGIN(ctx, sorted, cursor, id) { + void *value; + grn_id record_id; + + grn_table_cursor_get_value(ctx, cursor, &value); + record_id = *((grn_id *)value); + GRN_RECORD_PUT(ctx, &(window->ids), record_id); + } GRN_TABLE_EACH_END(ctx, cursor); + + grn_obj_unlink(ctx, sorted); + + window->n_ids = GRN_BULK_VSIZE(&(window->ids)) / sizeof(grn_id); + + grn_window_set_direction(ctx, window, window->direction); + + window->sort_keys = sort_keys; + window->n_sort_keys = n_sort_keys; + + GRN_API_RETURN(GRN_SUCCESS); +} + +grn_obj * +grn_window_function_create(grn_ctx *ctx, + const char *name, + int name_size, + grn_window_function_func func) +{ + grn_obj *window_function = NULL; + + GRN_API_ENTER; + + if (name_size == -1) { + name_size = strlen(name); + } + + window_function = grn_proc_create(ctx, + name, + name_size, + GRN_PROC_WINDOW_FUNCTION, + NULL, NULL, NULL, 0, NULL); + if (!window_function) { + ERR(GRN_WINDOW_FUNCTION_ERROR, + "[window-function][%.*s] failed to create proc: %s", + name_size, name, + ctx->errbuf); + GRN_API_RETURN(NULL); + } + + { + grn_proc *proc = (grn_proc *)window_function; + proc->callbacks.window_function = func; + } + + GRN_API_RETURN(window_function); +} + +static grn_bool +grn_expr_is_window_function_call(grn_ctx *ctx, + grn_obj *window_function_call) +{ + grn_expr *expr = (grn_expr *)window_function_call; + grn_expr_code *func; + grn_expr_code *call; + + func = &(expr->codes[0]); + call = &(expr->codes[expr->codes_curr - 1]); + + if (func->op != GRN_OP_PUSH) { + return GRN_FALSE; + } + if (!grn_obj_is_window_function_proc(ctx, func->value)) { + return GRN_FALSE; + } + + if (call->op != GRN_OP_CALL) { + return GRN_FALSE; + } + if (call->nargs != (expr->codes_curr - 2)) { + return GRN_FALSE; + } + + return GRN_TRUE; +} + +static grn_rc +grn_expr_call_window_function(grn_ctx *ctx, + grn_obj *output_column, + grn_window *window, + grn_obj *window_function_call) +{ + grn_rc rc; + grn_expr *expr = (grn_expr *)window_function_call; + grn_proc *proc; + int32_t i, n; + grn_obj args; + + proc = (grn_proc *)(expr->codes[0].value); + + GRN_PTR_INIT(&args, GRN_OBJ_VECTOR, GRN_ID_NIL); + n = expr->codes_curr - 2; + for (i = 1; i < n; i++) { + /* TODO: Check op. */ + GRN_PTR_PUT(ctx, &args, expr->codes[i].value); + } + rc = proc->callbacks.window_function(ctx, + output_column, + window, + (grn_obj *)GRN_BULK_HEAD(&args), + GRN_BULK_VSIZE(&args) / sizeof(grn_obj *)); + GRN_OBJ_FIN(ctx, &args); + + return rc; +} + +grn_rc +grn_table_apply_window_function(grn_ctx *ctx, + grn_obj *table, + grn_obj *output_column, + grn_window_definition *definition, + grn_obj *window_function_call) +{ + grn_window window; + + GRN_API_ENTER; + + if (!table) { + ERR(GRN_INVALID_ARGUMENT, + "[table][apply][window-function] table is NULL"); + GRN_API_RETURN(ctx->rc); + } + + if (!grn_expr_is_window_function_call(ctx, window_function_call)) { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, window_function_call, &inspected); + ERR(GRN_INVALID_ARGUMENT, + "[table][apply][window-function] must be window function call: %.*s", + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + GRN_API_RETURN(ctx->rc); + } + + grn_window_init(ctx, &window, table); + grn_window_set_direction(ctx, &window, window.direction); + grn_window_set_sort_keys(ctx, &window, + definition->sort_keys, + definition->n_sort_keys); + grn_expr_call_window_function(ctx, + output_column, + &window, + window_function_call); + grn_window_fin(ctx, &window); + + GRN_API_RETURN(GRN_SUCCESS); +} Added: lib/window_functions.c (+51 -0) 100644 =================================================================== --- /dev/null +++ lib/window_functions.c 2016-05-18 00:22:11 +0900 (6ef478a) @@ -0,0 +1,51 @@ +/* -*- 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-1301 USA +*/ + +#include "grn_db.h" +#include "grn_window_functions.h" + +static grn_rc +window_function_row_number(grn_ctx *ctx, + grn_obj *output_column, + grn_window *window, + grn_obj *args, + int n_args) +{ + grn_id id; + uint32_t i = 1; + grn_obj value; + + GRN_UINT32_INIT(&value, 0); + while ((id = grn_window_next(ctx, window))) { + GRN_UINT32_SET(ctx, &value, i); + grn_obj_set_value(ctx, output_column, id, &value, GRN_OBJ_SET); + i++; + } + GRN_OBJ_FIN(ctx, &value); + + return GRN_SUCCESS; +} + +grn_rc +grn_db_init_builtin_window_functions(grn_ctx *ctx) +{ + grn_window_function_create(ctx, + "row_number", -1, + window_function_row_number); + return GRN_SUCCESS; +} Added: test/command/suite/select/column/window_function/row_number/ascending.expected (+73 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/select/column/window_function/row_number/ascending.expected 2016-05-18 00:22:11 +0900 (363631d) @@ -0,0 +1,73 @@ +table_create Items TABLE_HASH_KEY ShortText +[[0,0.0,0.0],true] +column_create Items price COLUMN_SCALAR UInt32 +[[0,0.0,0.0],true] +load --table Items +[ +{"_key": "item1", "price": 666}, +{"_key": "item2", "price": 999}, +{"_key": "item3", "price": 777}, +{"_key": "item4", "price": 111}, +{"_key": "item5", "price": 333}, +{"_key": "item6", "price": 222} +] +[[0,0.0,0.0],6] +select Items --column[nth_row].stage initial --column[nth_row].value 'row_number()' --column[nth_row].type UInt32 --column[nth_row].window.sort_keys price --output_columns '_key, price, nth_row' +[ + [ + 0, + 0.0, + 0.0 + ], + [ + [ + [ + 6 + ], + [ + [ + "_key", + "ShortText" + ], + [ + "price", + "UInt32" + ], + [ + "nth_row", + "UInt32" + ] + ], + [ + "item1", + 666, + 4 + ], + [ + "item2", + 999, + 6 + ], + [ + "item3", + 777, + 5 + ], + [ + "item4", + 111, + 1 + ], + [ + "item5", + 333, + 3 + ], + [ + "item6", + 222, + 2 + ] + ] + ] +] Added: test/command/suite/select/column/window_function/row_number/ascending.test (+19 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/select/column/window_function/row_number/ascending.test 2016-05-18 00:22:11 +0900 (98075ba) @@ -0,0 +1,19 @@ +table_create Items TABLE_HASH_KEY ShortText +column_create Items price COLUMN_SCALAR UInt32 + +load --table Items +[ +{"_key": "item1", "price": 666}, +{"_key": "item2", "price": 999}, +{"_key": "item3", "price": 777}, +{"_key": "item4", "price": 111}, +{"_key": "item5", "price": 333}, +{"_key": "item6", "price": 222} +] + +select Items \ + --column[nth_row].stage initial \ + --column[nth_row].value 'row_number()' \ + --column[nth_row].type UInt32 \ + --column[nth_row].window.sort_keys price \ + --output_columns '_key, price, nth_row' Added: test/command/suite/select/column/window_function/row_number/descending.expected (+73 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/select/column/window_function/row_number/descending.expected 2016-05-18 00:22:11 +0900 (f7209e5) @@ -0,0 +1,73 @@ +table_create Items TABLE_HASH_KEY ShortText +[[0,0.0,0.0],true] +column_create Items price COLUMN_SCALAR UInt32 +[[0,0.0,0.0],true] +load --table Items +[ +{"_key": "item1", "price": 666}, +{"_key": "item2", "price": 999}, +{"_key": "item3", "price": 777}, +{"_key": "item4", "price": 111}, +{"_key": "item5", "price": 333}, +{"_key": "item6", "price": 222} +] +[[0,0.0,0.0],6] +select Items --column[nth_row].stage initial --column[nth_row].value 'row_number()' --column[nth_row].type UInt32 --column[nth_row].window.sort_keys -price --output_columns '_key, price, nth_row' +[ + [ + 0, + 0.0, + 0.0 + ], + [ + [ + [ + 6 + ], + [ + [ + "_key", + "ShortText" + ], + [ + "price", + "UInt32" + ], + [ + "nth_row", + "UInt32" + ] + ], + [ + "item1", + 666, + 3 + ], + [ + "item2", + 999, + 1 + ], + [ + "item3", + 777, + 2 + ], + [ + "item4", + 111, + 6 + ], + [ + "item5", + 333, + 4 + ], + [ + "item6", + 222, + 5 + ] + ] + ] +] Added: test/command/suite/select/column/window_function/row_number/descending.test (+19 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/select/column/window_function/row_number/descending.test 2016-05-18 00:22:11 +0900 (071702f) @@ -0,0 +1,19 @@ +table_create Items TABLE_HASH_KEY ShortText +column_create Items price COLUMN_SCALAR UInt32 + +load --table Items +[ +{"_key": "item1", "price": 666}, +{"_key": "item2", "price": 999}, +{"_key": "item3", "price": 777}, +{"_key": "item4", "price": 111}, +{"_key": "item5", "price": 333}, +{"_key": "item6", "price": 222} +] + +select Items \ + --column[nth_row].stage initial \ + --column[nth_row].value 'row_number()' \ + --column[nth_row].type UInt32 \ + --column[nth_row].window.sort_keys -price \ + --output_columns '_key, price, nth_row' Modified: test/unit/core/test-object.c (+34 -0) =================================================================== --- test/unit/core/test-object.c 2016-05-18 00:16:33 +0900 (159b269) +++ test/unit/core/test-object.c 2016-05-18 00:22:11 +0900 (b716b9e) @@ -55,6 +55,8 @@ void data_is_token_filter_proc(void); void test_is_token_filter_proc(gconstpointer data); void data_is_scorer_proc(void); void test_is_scorer_proc(gconstpointer data); +void data_is_window_function_proc(void); +void test_is_window_function_proc(gconstpointer data); void data_type_to_string(void); void test_type_to_string(gconstpointer data); @@ -628,6 +630,38 @@ test_is_scorer_proc(gconstpointer data) } void +data_is_window_function_proc(void) +{ +#define ADD_DATUM(expected, name) \ + gcut_add_datum((expected ? \ + "window-function-proc - " name : \ + "not window-function-proc - " name), \ + "expected", G_TYPE_BOOLEAN, expected, \ + "name", G_TYPE_STRING, name, \ + NULL) + + ADD_DATUM(TRUE, "row_number"); + ADD_DATUM(FALSE, "geo_in_circle"); + +#undef ADD_DATUM +} + +void +test_is_window_function_proc(gconstpointer data) +{ + const gchar *name; + grn_obj *object; + + name = gcut_data_get_string(data, "name"); + object = grn_ctx_get(context, name, strlen(name)); + if (gcut_data_get_string(data, "expected")) { + cut_assert_true(grn_obj_is_window_function_proc(context, object)); + } else { + cut_assert_false(grn_obj_is_window_function_proc(context, object)); + } +} + +void data_type_to_string(void) { #define ADD_DATUM(expected, type) \ -------------- next part -------------- HTML����������������������������... ダウンロード