Kouhei Sutou 2019-04-17 10:17:28 +0900 (Wed, 17 Apr 2019) Revision: 532024b99d4a035444e26a2631a50eebc0b3393f https://github.com/groonga/groonga/commit/532024b99d4a035444e26a2631a50eebc0b3393f Message: io_flush: revert recursive=yes behavior and add recursive=dependent TODO: more test Added files: test/command/suite/io_flush/recursive/dependent/data_column/have_index.expected test/command/suite/io_flush/recursive/dependent/data_column/have_index.test test/command/suite/io_flush/recursive/dependent/data_column/no_index.expected test/command/suite/io_flush/recursive/dependent/data_column/no_index.test test/command/suite/io_flush/recursive/dependent/data_column/reference.expected test/command/suite/io_flush/recursive/dependent/data_column/reference.test test/command/suite/io_flush/recursive/dependent/index_column/source_column.expected test/command/suite/io_flush/recursive/dependent/index_column/source_column.test test/command/suite/io_flush/recursive/dependent/index_column/source_key.expected test/command/suite/io_flush/recursive/dependent/index_column/source_key.test test/command/suite/io_flush/recursive/dependent/index_column/sources.expected test/command/suite/io_flush/recursive/dependent/index_column/sources.test test/command/suite/io_flush/recursive/dependent/table/reference.expected test/command/suite/io_flush/recursive/dependent/table/reference.test Modified files: include/groonga/groonga.h lib/db.c lib/proc.c test/command/suite/io_flush/default.expected test/command/suite/io_flush/many_data.expected test/command/suite/io_flush/target_name/column.expected test/command/suite/io_flush/target_name/invalid.expected test/command/suite/io_flush/target_name/table.expected Modified: include/groonga/groonga.h (+1 -1) =================================================================== --- include/groonga/groonga.h 2019-04-15 18:00:02 +0900 (7ed36b718) +++ include/groonga/groonga.h 2019-04-17 10:17:28 +0900 (bf00cc8e5) @@ -779,7 +779,7 @@ GRN_API grn_rc grn_obj_clear_lock(grn_ctx *ctx, grn_obj *obj); GRN_API unsigned int grn_obj_is_locked(grn_ctx *ctx, grn_obj *obj); GRN_API grn_rc grn_obj_flush(grn_ctx *ctx, grn_obj *obj); GRN_API grn_rc grn_obj_flush_recursive(grn_ctx *ctx, grn_obj *obj); -GRN_API grn_rc grn_obj_flush_dependent(grn_ctx *ctx, grn_obj *obj); +GRN_API grn_rc grn_obj_flush_recursive_dependent(grn_ctx *ctx, grn_obj *obj); GRN_API int grn_obj_defrag(grn_ctx *ctx, grn_obj *obj, int threshold); GRN_API grn_obj *grn_obj_db(grn_ctx *ctx, grn_obj *obj); Modified: lib/db.c (+321 -113) =================================================================== --- lib/db.c 2019-04-15 18:00:02 +0900 (019e48263) +++ lib/db.c 2019-04-17 10:17:28 +0900 (a16d0dfee) @@ -12375,12 +12375,13 @@ grn_obj_flush(grn_ctx *ctx, grn_obj *obj) typedef struct { grn_hash *flushed; bool is_close_opened_object_mode; -} flush_recursive_data; + bool is_top_level; +} flush_recursive_dependent_data; static bool -grn_obj_flush_recursive_need_flush(grn_ctx *ctx, - flush_recursive_data *data, - grn_id id) +grn_obj_flush_recursive_dependent_need_flush(grn_ctx *ctx, + flush_recursive_dependent_data *data, + grn_id id) { int added = 0; grn_id flushed_id = grn_hash_add(ctx, @@ -12396,7 +12397,8 @@ grn_obj_flush_recursive_need_flush(grn_ctx *ctx, char message[GRN_CTX_MSGSIZE]; grn_strcpy(message, GRN_CTX_MSGSIZE, ctx->errbuf); ERR(rc, - "[io_flush] failed to register flushed ID: <%u>: %s", + "[io][flush][recursive][dependent] " + "failed to register flushed ID: <%u>: %s", id, message); return false; @@ -12405,17 +12407,17 @@ grn_obj_flush_recursive_need_flush(grn_ctx *ctx, } static void -grn_obj_flush_recursive_internal(grn_ctx *ctx, - grn_obj *obj, - flush_recursive_data *data); +grn_obj_flush_recursive_dependent_internal(grn_ctx *ctx, + grn_obj *obj, + flush_recursive_dependent_data *data); static void -grn_obj_flush_recursive_internal_db(grn_ctx *ctx, - grn_obj *db, - flush_recursive_data *data) +grn_obj_flush_recursive_dependent_internal_db(grn_ctx *ctx, + grn_obj *db, + flush_recursive_dependent_data *data) { GRN_TABLE_EACH_BEGIN(ctx, db, cursor, id) { - if (!grn_obj_flush_recursive_need_flush(ctx, data, id)) { + if (!grn_obj_flush_recursive_dependent_need_flush(ctx, data, id)) { if (ctx->rc == GRN_SUCCESS) { continue; } else { @@ -12429,7 +12431,8 @@ grn_obj_flush_recursive_internal_db(grn_ctx *ctx, grn_obj *object = grn_ctx_at(ctx, id); if (grn_obj_is_table(ctx, object)) { - grn_obj_flush_recursive_internal(ctx, object, data); + data->is_top_level = true; + grn_obj_flush_recursive_dependent_internal(ctx, object, data); } else { if (ctx->rc != GRN_SUCCESS) { ERRCLR(ctx); @@ -12452,19 +12455,22 @@ grn_obj_flush_recursive_internal_db(grn_ctx *ctx, } static void -grn_obj_flush_recursive_internal_table(grn_ctx *ctx, - grn_obj *table, - flush_recursive_data *data) +grn_obj_flush_recursive_dependent_internal_table(grn_ctx *ctx, + grn_obj *table, + flush_recursive_dependent_data *data) { + const bool is_top_level = data->is_top_level; + data->is_top_level = false; + if (grn_obj_is_lexicon(ctx, table)) { grn_id domain_id = table->header.domain; - if (grn_obj_flush_recursive_need_flush(ctx, data, domain_id)) { + if (grn_obj_flush_recursive_dependent_need_flush(ctx, data, domain_id)) { if (data->is_close_opened_object_mode) { grn_ctx_push_temporary_open_space(ctx); } grn_obj *domain = grn_ctx_at(ctx, domain_id); if (grn_obj_is_table(ctx, domain)) { - grn_obj_flush_recursive_internal(ctx, domain, data); + grn_obj_flush_recursive_dependent_internal(ctx, domain, data); } if (data->is_close_opened_object_mode) { grn_ctx_pop_temporary_open_space(ctx); @@ -12473,104 +12479,136 @@ grn_obj_flush_recursive_internal_table(grn_ctx *ctx, if (ctx->rc != GRN_SUCCESS) { return; } - } - grn_hash *columns = grn_hash_create(ctx, - NULL, - sizeof(grn_id), - 0, - GRN_OBJ_TABLE_HASH_KEY | GRN_HASH_TINY); - if (!columns) { - grn_rc rc = ctx->rc; - if (rc == GRN_SUCCESS) { - rc = GRN_NO_MEMORY_AVAILABLE; + for (grn_hook *hooks = DB_OBJ(table)->hooks[GRN_HOOK_INSERT]; + hooks; + hooks = hooks->next) { + grn_obj_default_set_value_hook_data *hook_data = + (grn_obj_default_set_value_hook_data *)GRN_NEXT_ADDR(hooks); + if (grn_obj_flush_recursive_dependent_need_flush(ctx, + data, + hook_data->target)) { + if (data->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + grn_obj *index = grn_ctx_at(ctx, hook_data->target); + if (index) { + grn_obj_flush_recursive_dependent_internal(ctx, index, data); + } + if (data->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + if (ctx->rc != GRN_SUCCESS) { + return; + } + } } - char message[GRN_CTX_MSGSIZE]; - grn_strcpy(message, GRN_CTX_MSGSIZE, ctx->errbuf); - char table_name[GRN_TABLE_MAX_KEY_SIZE]; - int table_name_size; - table_name_size = grn_obj_name(ctx, - table, - table_name, - GRN_TABLE_MAX_KEY_SIZE); - ERR(rc, - "[io_flush] failed to create internal hash table " - "to store columns: <%.*s>: %s", - table_name_size, table_name, - message); - return; } - if (grn_table_columns(ctx, table, "", 0, (grn_obj *)columns) > 0) { - GRN_HASH_EACH_BEGIN(ctx, columns, cursor, id) { - void *key; - if (grn_hash_cursor_get_key(ctx, cursor, &key) == 0) { - continue; + if (is_top_level) { + grn_hash *columns = grn_hash_create(ctx, + NULL, + sizeof(grn_id), + 0, + GRN_OBJ_TABLE_HASH_KEY | GRN_HASH_TINY); + if (!columns) { + grn_rc rc = ctx->rc; + if (rc == GRN_SUCCESS) { + rc = GRN_NO_MEMORY_AVAILABLE; } + char message[GRN_CTX_MSGSIZE]; + grn_strcpy(message, GRN_CTX_MSGSIZE, ctx->errbuf); + char table_name[GRN_TABLE_MAX_KEY_SIZE]; + int table_name_size; + table_name_size = grn_obj_name(ctx, + table, + table_name, + GRN_TABLE_MAX_KEY_SIZE); + ERR(rc, + "[io][flush][recursive][dependent] " + "failed to create internal hash table " + "to store columns: <%.*s>: %s", + table_name_size, table_name, + message); + return; + } - grn_id *column_id = key; - if (!grn_obj_flush_recursive_need_flush(ctx, data, *column_id)) { - if (ctx->rc == GRN_SUCCESS) { + if (grn_table_columns(ctx, table, "", 0, (grn_obj *)columns) > 0) { + GRN_HASH_EACH_BEGIN(ctx, columns, cursor, id) { + void *key; + if (grn_hash_cursor_get_key(ctx, cursor, &key) == 0) { continue; - } else { - break; } - } - if (data->is_close_opened_object_mode) { - grn_ctx_push_temporary_open_space(ctx); - } - grn_obj *column = grn_ctx_at(ctx, *column_id); - if (column) { - grn_obj_flush_recursive_internal(ctx, column, data); - } - if (data->is_close_opened_object_mode) { - grn_ctx_pop_temporary_open_space(ctx); - } + grn_id *column_id = key; + if (!grn_obj_flush_recursive_dependent_need_flush(ctx, data, *column_id)) { + if (ctx->rc == GRN_SUCCESS) { + continue; + } else { + break; + } + } - if (ctx->rc != GRN_SUCCESS) { - break; - } - } GRN_HASH_EACH_END(ctx, cursor); - } - grn_hash_close(ctx, columns); - if (ctx->rc != GRN_SUCCESS) { - return; + if (data->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + grn_obj *column = grn_ctx_at(ctx, *column_id); + if (column) { + grn_obj_flush_recursive_dependent_internal(ctx, column, data); + } + if (data->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + + if (ctx->rc != GRN_SUCCESS) { + break; + } + } GRN_HASH_EACH_END(ctx, cursor); + } + grn_hash_close(ctx, columns); + if (ctx->rc != GRN_SUCCESS) { + return; + } } grn_obj_flush(ctx, table); } static void -grn_obj_flush_recursive_internal_column_data(grn_ctx *ctx, - grn_obj *column, - flush_recursive_data *data) +grn_obj_flush_recursive_dependent_internal_column_data(grn_ctx *ctx, + grn_obj *column, + flush_recursive_dependent_data *data) { - grn_id table_id = column->header.domain; - if (grn_obj_flush_recursive_need_flush(ctx, data, table_id)) { - if (data->is_close_opened_object_mode) { - grn_ctx_push_temporary_open_space(ctx); - } - grn_obj *table = grn_ctx_at(ctx, table_id); - if (table) { - grn_obj_flush_recursive_internal(ctx, table, data); + const bool is_top_level = data->is_top_level; + data->is_top_level = false; + + if (is_top_level) { + const grn_id table_id = column->header.domain; + if (grn_obj_flush_recursive_dependent_need_flush(ctx, data, table_id)) { + if (data->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + grn_obj *table = grn_ctx_at(ctx, table_id); + if (table) { + grn_obj_flush_recursive_dependent_internal(ctx, table, data); + } + if (data->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } } - if (data->is_close_opened_object_mode) { - grn_ctx_pop_temporary_open_space(ctx); + if (ctx->rc != GRN_SUCCESS) { + return; } } - if (ctx->rc != GRN_SUCCESS) { - return; - } - grn_id range_id = grn_obj_get_range(ctx, column); - if (grn_obj_flush_recursive_need_flush(ctx, data, range_id)) { + const grn_id range_id = grn_obj_get_range(ctx, column); + if (grn_obj_flush_recursive_dependent_need_flush(ctx, data, range_id)) { if (data->is_close_opened_object_mode) { grn_ctx_push_temporary_open_space(ctx); } grn_obj *range = grn_ctx_at(ctx, range_id); if (grn_obj_is_table(ctx, range)) { - grn_obj_flush_recursive_internal(ctx, range, data); + grn_obj_flush_recursive_dependent_internal(ctx, range, data); } if (data->is_close_opened_object_mode) { grn_ctx_pop_temporary_open_space(ctx); @@ -12585,13 +12623,15 @@ grn_obj_flush_recursive_internal_column_data(grn_ctx *ctx, hooks = hooks->next) { grn_obj_default_set_value_hook_data *hook_data = (grn_obj_default_set_value_hook_data *)GRN_NEXT_ADDR(hooks); - if (grn_obj_flush_recursive_need_flush(ctx, data, hook_data->target)) { + if (grn_obj_flush_recursive_dependent_need_flush(ctx, + data, + hook_data->target)) { if (data->is_close_opened_object_mode) { grn_ctx_push_temporary_open_space(ctx); } grn_obj *index = grn_ctx_at(ctx, hook_data->target); if (index) { - grn_obj_flush_recursive_internal(ctx, index, data); + grn_obj_flush_recursive_dependent_internal(ctx, index, data); } if (data->is_close_opened_object_mode) { grn_ctx_pop_temporary_open_space(ctx); @@ -12606,18 +12646,20 @@ grn_obj_flush_recursive_internal_column_data(grn_ctx *ctx, } static void -grn_obj_flush_recursive_internal_column_index(grn_ctx *ctx, - grn_obj *column, - flush_recursive_data *data) +grn_obj_flush_recursive_dependent_internal_column_index(grn_ctx *ctx, + grn_obj *column, + flush_recursive_dependent_data *data) { + data->is_top_level = false; + grn_id lexicon_id = column->header.domain; - if (grn_obj_flush_recursive_need_flush(ctx, data, lexicon_id)) { + if (grn_obj_flush_recursive_dependent_need_flush(ctx, data, lexicon_id)) { if (data->is_close_opened_object_mode) { grn_ctx_push_temporary_open_space(ctx); } grn_obj *lexicon = grn_ctx_at(ctx, lexicon_id); if (lexicon) { - grn_obj_flush_recursive_internal(ctx, lexicon, data); + grn_obj_flush_recursive_dependent_internal(ctx, lexicon, data); } if (data->is_close_opened_object_mode) { grn_ctx_pop_temporary_open_space(ctx); @@ -12630,10 +12672,10 @@ grn_obj_flush_recursive_internal_column_index(grn_ctx *ctx, grn_obj source_ids; GRN_UINT32_INIT(&source_ids, GRN_OBJ_VECTOR); grn_obj_get_info(ctx, column, GRN_INFO_SOURCE, &source_ids); - size_t n = GRN_UINT32_VECTOR_SIZE(&source_ids); + const size_t n = GRN_UINT32_VECTOR_SIZE(&source_ids); for (size_t i = 0; i < n; i++) { grn_id source_id = GRN_UINT32_VALUE_AT(&source_ids, i); - if (!grn_obj_flush_recursive_need_flush(ctx, data, source_id)) { + if (!grn_obj_flush_recursive_dependent_need_flush(ctx, data, source_id)) { if (ctx->rc == GRN_SUCCESS) { continue; } else { @@ -12645,7 +12687,7 @@ grn_obj_flush_recursive_internal_column_index(grn_ctx *ctx, } grn_obj *source = grn_ctx_at(ctx, source_id); if (source) { - grn_obj_flush_recursive_internal(ctx, source, data); + grn_obj_flush_recursive_dependent_internal(ctx, source, data); } if (data->is_close_opened_object_mode) { grn_ctx_pop_temporary_open_space(ctx); @@ -12663,26 +12705,26 @@ grn_obj_flush_recursive_internal_column_index(grn_ctx *ctx, } static void -grn_obj_flush_recursive_internal(grn_ctx *ctx, - grn_obj *obj, - flush_recursive_data *data) +grn_obj_flush_recursive_dependent_internal(grn_ctx *ctx, + grn_obj *obj, + flush_recursive_dependent_data *data) { switch (obj->header.type) { case GRN_DB : - grn_obj_flush_recursive_internal_db(ctx, obj, data); + grn_obj_flush_recursive_dependent_internal_db(ctx, obj, data); break; case GRN_TABLE_NO_KEY : case GRN_TABLE_HASH_KEY : case GRN_TABLE_PAT_KEY : case GRN_TABLE_DAT_KEY : - grn_obj_flush_recursive_internal_table(ctx, obj, data); + grn_obj_flush_recursive_dependent_internal_table(ctx, obj, data); break; case GRN_COLUMN_FIX_SIZE : case GRN_COLUMN_VAR_SIZE : - grn_obj_flush_recursive_internal_column_data(ctx, obj, data); + grn_obj_flush_recursive_dependent_internal_column_data(ctx, obj, data); break; case GRN_COLUMN_INDEX : - grn_obj_flush_recursive_internal_column_index(ctx, obj, data); + grn_obj_flush_recursive_dependent_internal_column_index(ctx, obj, data); break; default : { @@ -12690,7 +12732,8 @@ grn_obj_flush_recursive_internal(grn_ctx *ctx, GRN_TEXT_INIT(&inspected, 0); grn_inspect(ctx, &inspected, obj); ERR(GRN_INVALID_ARGUMENT, - "[flush] object must be DB, table or column: <%.*s>", + "[io][flush][recursive][dependent] " + "object must be DB, table or column: <%.*s>", (int)GRN_TEXT_LEN(&inspected), GRN_TEXT_VALUE(&inspected)); GRN_OBJ_FIN(ctx, &inspected); @@ -12700,11 +12743,11 @@ grn_obj_flush_recursive_internal(grn_ctx *ctx, } grn_rc -grn_obj_flush_recursive(grn_ctx *ctx, grn_obj *obj) +grn_obj_flush_recursive_dependent(grn_ctx *ctx, grn_obj *obj) { GRN_API_ENTER; - flush_recursive_data data; + flush_recursive_dependent_data data; data.flushed = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY); if (!data.flushed) { @@ -12718,7 +12761,8 @@ grn_obj_flush_recursive(grn_ctx *ctx, grn_obj *obj) int name_size; name_size = grn_obj_name(ctx, obj, name, GRN_TABLE_MAX_KEY_SIZE); ERR(rc, - "[flush][recursive] failed to create an internal hash table " + "[io][flush][recursive][dependent] " + "failed to create an internal hash table " "to manage flushed objects: <%.*s>: %s", name_size, name, message); @@ -12728,8 +12772,9 @@ grn_obj_flush_recursive(grn_ctx *ctx, grn_obj *obj) data.is_close_opened_object_mode = (grn_thread_get_limit() == 1); grn_id id = grn_obj_id(ctx, obj); - if (grn_obj_flush_recursive_need_flush(ctx, &data, id)) { - grn_obj_flush_recursive_internal(ctx, obj, &data); + if (grn_obj_flush_recursive_dependent_need_flush(ctx, &data, id)) { + data.is_top_level = true; + grn_obj_flush_recursive_dependent_internal(ctx, obj, &data); } grn_hash_close(ctx, data.flushed); @@ -12737,6 +12782,169 @@ grn_obj_flush_recursive(grn_ctx *ctx, grn_obj *obj) GRN_API_RETURN(ctx->rc); } +typedef struct { + bool is_close_opened_object_mode; +} flush_recursive_data; + +static void +grn_obj_flush_recursive_internal(grn_ctx *ctx, + grn_obj *obj, + flush_recursive_data *data); + +static void +grn_obj_flush_recursive_internal_db(grn_ctx *ctx, + grn_obj *db, + flush_recursive_data *data) +{ + GRN_TABLE_EACH_BEGIN(ctx, db, cursor, id) { + if (data->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + + grn_obj *object = grn_ctx_at(ctx, id); + if (grn_obj_is_table(ctx, object)) { + grn_obj_flush_recursive_internal(ctx, object, data); + } else { + if (ctx->rc != GRN_SUCCESS) { + ERRCLR(ctx); + } + } + + if (data->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + + if (ctx->rc != GRN_SUCCESS) { + break; + } + } GRN_TABLE_EACH_END(ctx, cursor); + if (ctx->rc != GRN_SUCCESS) { + return; + } + + grn_obj_flush(ctx, db); +} + +static void +grn_obj_flush_recursive_internal_table(grn_ctx *ctx, + grn_obj *table, + flush_recursive_data *data) +{ + grn_hash *columns = grn_hash_create(ctx, + NULL, + sizeof(grn_id), + 0, + GRN_OBJ_TABLE_HASH_KEY | GRN_HASH_TINY); + if (!columns) { + grn_rc rc = ctx->rc; + if (rc == GRN_SUCCESS) { + rc = GRN_NO_MEMORY_AVAILABLE; + } + char message[GRN_CTX_MSGSIZE]; + grn_strcpy(message, GRN_CTX_MSGSIZE, ctx->errbuf); + char table_name[GRN_TABLE_MAX_KEY_SIZE]; + int table_name_size; + table_name_size = grn_obj_name(ctx, + table, + table_name, + GRN_TABLE_MAX_KEY_SIZE); + ERR(rc, + "[io][flush][recursive] " + "failed to create internal hash table " + "to store columns: <%.*s>: %s", + table_name_size, table_name, + message); + return; + } + + if (grn_table_columns(ctx, table, "", 0, (grn_obj *)columns) > 0) { + GRN_HASH_EACH_BEGIN(ctx, columns, cursor, id) { + void *key; + if (grn_hash_cursor_get_key(ctx, cursor, &key) == 0) { + continue; + } + + if (data->is_close_opened_object_mode) { + grn_ctx_push_temporary_open_space(ctx); + } + grn_id *column_id = key; + grn_obj *column = grn_ctx_at(ctx, *column_id); + if (column) { + grn_obj_flush_recursive_internal(ctx, column, data); + } + if (data->is_close_opened_object_mode) { + grn_ctx_pop_temporary_open_space(ctx); + } + + if (ctx->rc != GRN_SUCCESS) { + break; + } + } GRN_HASH_EACH_END(ctx, cursor); + } + grn_hash_close(ctx, columns); + if (ctx->rc != GRN_SUCCESS) { + return; + } + + grn_obj_flush(ctx, table); +} + +static void +grn_obj_flush_recursive_internal_column(grn_ctx *ctx, + grn_obj *column, + flush_recursive_data *data) +{ + grn_obj_flush(ctx, column); +} + +static void +grn_obj_flush_recursive_internal(grn_ctx *ctx, + grn_obj *obj, + flush_recursive_data *data) +{ + switch (obj->header.type) { + case GRN_DB : + grn_obj_flush_recursive_internal_db(ctx, obj, data); + break; + case GRN_TABLE_NO_KEY : + case GRN_TABLE_HASH_KEY : + case GRN_TABLE_PAT_KEY : + case GRN_TABLE_DAT_KEY : + grn_obj_flush_recursive_internal_table(ctx, obj, data); + break; + case GRN_COLUMN_FIX_SIZE : + case GRN_COLUMN_VAR_SIZE : + case GRN_COLUMN_INDEX : + grn_obj_flush_recursive_internal_column(ctx, obj, data); + break; + default : + { + grn_obj inspected; + GRN_TEXT_INIT(&inspected, 0); + grn_inspect(ctx, &inspected, obj); + ERR(GRN_INVALID_ARGUMENT, + "[io][flush][recursive] " + "object must be DB, table or column: <%.*s>", + (int)GRN_TEXT_LEN(&inspected), + GRN_TEXT_VALUE(&inspected)); + GRN_OBJ_FIN(ctx, &inspected); + } + break; + } +} + +grn_rc +grn_obj_flush_recursive(grn_ctx *ctx, grn_obj *obj) +{ + GRN_API_ENTER; + + flush_recursive_data data; + data.is_close_opened_object_mode = (grn_thread_get_limit() == 1); + grn_obj_flush_recursive_internal(ctx, obj, &data); + + GRN_API_RETURN(ctx->rc); +} + grn_obj * grn_obj_db(grn_ctx *ctx, grn_obj *obj) { Modified: lib/proc.c (+8 -5) =================================================================== --- lib/proc.c 2019-04-15 18:00:02 +0900 (776f39848) +++ lib/proc.c 2019-04-17 10:17:28 +0900 (3ce11d68f) @@ -3741,9 +3741,10 @@ proc_io_flush(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) return NULL; } - bool is_recursive = grn_plugin_proc_get_var_bool(ctx, user_data, + grn_raw_string recursive; + recursive.value = grn_plugin_proc_get_var_string(ctx, user_data, "recursive", -1, - GRN_TRUE); + &(recursive.length)); bool is_only_opened = grn_plugin_proc_get_var_bool(ctx, user_data, "only_opened", -1, GRN_FALSE); @@ -3767,10 +3768,12 @@ proc_io_flush(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data) rc = grn_obj_flush(ctx, target); } } else { - if (is_recursive) { - rc = grn_obj_flush_recursive(ctx, target); - } else { + if (GRN_RAW_STRING_EQUAL_CSTRING(recursive, "dependent")) { + rc = grn_obj_flush_recursive_dependent(ctx, target); + } else if (GRN_RAW_STRING_EQUAL_CSTRING(recursive, "no")) { rc = grn_obj_flush(ctx, target); + } else { + rc = grn_obj_flush_recursive(ctx, target); } } Modified: test/command/suite/io_flush/default.expected (+2 -2) =================================================================== --- test/command/suite/io_flush/default.expected 2019-04-15 18:00:02 +0900 (74e09674f) +++ test/command/suite/io_flush/default.expected 2019-04-17 10:17:28 +0900 (8a9630647) @@ -9,10 +9,10 @@ column_create Terms users_name COLUMN_INDEX|WITH_POSITION Users name io_flush [[0,0.0,0.0],true] #>io_flush -#:000000000000000 flush[Users] -#:000000000000000 flush[Users.name] #:000000000000000 flush[Terms.users_name] #:000000000000000 flush[Terms] +#:000000000000000 flush[Users.name] +#:000000000000000 flush[Users] #:000000000000000 flush[(anonymous:table:dat_key)] #:000000000000000 flush[(anonymous:column:var_size)] #:000000000000000 flush[(anonymous:table:hash_key)] Modified: test/command/suite/io_flush/many_data.expected (+0 -3) =================================================================== --- test/command/suite/io_flush/many_data.expected 2019-04-15 18:00:02 +0900 (f6296aa7a) +++ test/command/suite/io_flush/many_data.expected 2019-04-17 10:17:28 +0900 (609ed0ac0) @@ -9,9 +9,6 @@ column_create Lexicon sources_value COLUMN_INDEX Sources value io_flush Lexicon.sources_value [[0,0.0,0.0],true] #>io_flush --target_name "Lexicon.sources_value" -#:000000000000000 flush[Lexicon] -#:000000000000000 flush[Sources] -#:000000000000000 flush[Sources.value] #:000000000000000 flush[Lexicon.sources_value] #:000000000000000 flush[(anonymous:table:dat_key)] #:000000000000000 flush[(anonymous:column:var_size)] Added: test/command/suite/io_flush/recursive/dependent/data_column/have_index.expected (+25 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/io_flush/recursive/dependent/data_column/have_index.expected 2019-04-17 10:17:28 +0900 (f279133a0) @@ -0,0 +1,25 @@ +table_create Memos TABLE_NO_KEY +[[0,0.0,0.0],true] +column_create Memos content COLUMN_SCALAR Text +[[0,0.0,0.0],true] +column_create Memos timestamp COLUMN_SCALAR Time +[[0,0.0,0.0],true] +table_create Terms TABLE_PAT_KEY ShortText --default_tokenizer TokenBigram --default_normalizer NormalizerNFKC100 +[[0,0.0,0.0],true] +column_create Terms memos_key_index COLUMN_INDEX|WITH_POSITION Memos content +[[0,0.0,0.0],true] +column_create Terms is_stop_word COLUMN_SCALAR Bool +[[0,0.0,0.0],true] +io_flush Memos.content --recursive dependent +[[0,0.0,0.0],true] +#>io_flush --recursive "dependent" --target_name "Memos.content" +#:000000000000000 flush[Memos] +#:000000000000000 flush[Terms] +#:000000000000000 flush[Terms.memos_key_index] +#:000000000000000 flush[Memos.content] +#:000000000000000 flush[(anonymous:table:dat_key)] +#:000000000000000 flush[(anonymous:column:var_size)] +#:000000000000000 flush[(anonymous:table:hash_key)] +#:000000000000000 flush[(anonymous:column:var_size)] +#:000000000000000 flush[(DB)] +#<000000000000000 rc=0 Added: test/command/suite/io_flush/recursive/dependent/data_column/have_index.test (+13 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/io_flush/recursive/dependent/data_column/have_index.test 2019-04-17 10:17:28 +0900 (44c112202) @@ -0,0 +1,13 @@ +table_create Memos TABLE_NO_KEY +column_create Memos content COLUMN_SCALAR Text +column_create Memos timestamp COLUMN_SCALAR Time + +table_create Terms TABLE_PAT_KEY ShortText \ + --default_tokenizer TokenBigram \ + --default_normalizer NormalizerNFKC100 +column_create Terms memos_key_index COLUMN_INDEX|WITH_POSITION Memos content +column_create Terms is_stop_word COLUMN_SCALAR Bool + +#@collect-query-log true +io_flush Memos.content --recursive dependent +#@collect-query-log false Added: test/command/suite/io_flush/recursive/dependent/data_column/no_index.expected (+25 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/io_flush/recursive/dependent/data_column/no_index.expected 2019-04-17 10:17:28 +0900 (3f5393323) @@ -0,0 +1,25 @@ +table_create Memos TABLE_HASH_KEY ShortText +[[0,0.0,0.0],true] +column_create Memos content COLUMN_SCALAR Text +[[0,0.0,0.0],true] +column_create Memos timestamp COLUMN_SCALAR Time +[[0,0.0,0.0],true] +table_create Terms TABLE_PAT_KEY ShortText --default_tokenizer TokenBigram --default_normalizer NormalizerNFKC100 +[[0,0.0,0.0],true] +column_create Terms memos_key_index COLUMN_INDEX|WITH_POSITION Memos _key +[[0,0.0,0.0],true] +column_create Terms is_stop_word COLUMN_SCALAR Bool +[[0,0.0,0.0],true] +io_flush Memos.content --recursive dependent +[[0,0.0,0.0],true] +#>io_flush --recursive "dependent" --target_name "Memos.content" +#:000000000000000 flush[Terms] +#:000000000000000 flush[Terms.memos_key_index] +#:000000000000000 flush[Memos] +#:000000000000000 flush[Memos.content] +#:000000000000000 flush[(anonymous:table:dat_key)] +#:000000000000000 flush[(anonymous:column:var_size)] +#:000000000000000 flush[(anonymous:table:hash_key)] +#:000000000000000 flush[(anonymous:column:var_size)] +#:000000000000000 flush[(DB)] +#<000000000000000 rc=0 Added: test/command/suite/io_flush/recursive/dependent/data_column/no_index.test (+13 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/io_flush/recursive/dependent/data_column/no_index.test 2019-04-17 10:17:28 +0900 (7077ee8ab) @@ -0,0 +1,13 @@ +table_create Memos TABLE_HASH_KEY ShortText +column_create Memos content COLUMN_SCALAR Text +column_create Memos timestamp COLUMN_SCALAR Time + +table_create Terms TABLE_PAT_KEY ShortText \ + --default_tokenizer TokenBigram \ + --default_normalizer NormalizerNFKC100 +column_create Terms memos_key_index COLUMN_INDEX|WITH_POSITION Memos _key +column_create Terms is_stop_word COLUMN_SCALAR Bool + +#@collect-query-log true +io_flush Memos.content --recursive dependent +#@collect-query-log false Added: test/command/suite/io_flush/recursive/dependent/data_column/reference.expected (+39 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/io_flush/recursive/dependent/data_column/reference.expected 2019-04-17 10:17:28 +0900 (680a1a4ae) @@ -0,0 +1,39 @@ +table_create Users TABLE_HASH_KEY ShortText +[[0,0.0,0.0],true] +table_create Labels TABLE_HASH_KEY ShortText +[[0,0.0,0.0],true] +column_create Labels owner COLUMN_SCALR Users +[[[-22,0.0,0.0],"[column][create][flags] unknown flag: <COLUMN_SCALR>"],false] +#|e| [column][create][flags] unknown flag: <COLUMN_SCALR> +table_create Tags TABLE_HASH_KEY ShortText +[[0,0.0,0.0],true] +column_create Tags label COLUMN_SCALAR Labels +[[0,0.0,0.0],true] +table_create Memos TABLE_NO_KEY +[[0,0.0,0.0],true] +column_create Memos tags COLUMN_VECTOR Tags +[[0,0.0,0.0],true] +column_create Memos timestamp COLUMN_SCALAR Time +[[0,0.0,0.0],true] +table_create Terms TABLE_PAT_KEY ShortText --default_tokenizer TokenBigram --default_normalizer NormalizerNFKC100 +[[0,0.0,0.0],true] +column_create Terms tags_index COLUMN_INDEX|WITH_POSITION|WITH_SECTION Tags _key,label +[[0,0.0,0.0],true] +column_create Terms is_stop_word COLUMN_SCALAR Bool +[[0,0.0,0.0],true] +io_flush Memos.tags --recursive dependent +[[0,0.0,0.0],true] +#>io_flush --recursive "dependent" --target_name "Memos.tags" +#:000000000000000 flush[Memos] +#:000000000000000 flush[Terms] +#:000000000000000 flush[Labels] +#:000000000000000 flush[Tags.label] +#:000000000000000 flush[Terms.tags_index] +#:000000000000000 flush[Tags] +#:000000000000000 flush[Memos.tags] +#:000000000000000 flush[(anonymous:table:dat_key)] +#:000000000000000 flush[(anonymous:column:var_size)] +#:000000000000000 flush[(anonymous:table:hash_key)] +#:000000000000000 flush[(anonymous:column:var_size)] +#:000000000000000 flush[(DB)] +#<000000000000000 rc=0 Added: test/command/suite/io_flush/recursive/dependent/data_column/reference.test (+22 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/io_flush/recursive/dependent/data_column/reference.test 2019-04-17 10:17:28 +0900 (45f35fa0e) @@ -0,0 +1,22 @@ +table_create Users TABLE_HASH_KEY ShortText + +table_create Labels TABLE_HASH_KEY ShortText +column_create Labels owner COLUMN_SCALR Users + +table_create Tags TABLE_HASH_KEY ShortText +column_create Tags label COLUMN_SCALAR Labels + +table_create Memos TABLE_NO_KEY +column_create Memos tags COLUMN_VECTOR Tags +column_create Memos timestamp COLUMN_SCALAR Time + +table_create Terms TABLE_PAT_KEY ShortText \ + --default_tokenizer TokenBigram \ + --default_normalizer NormalizerNFKC100 +column_create Terms tags_index \ + COLUMN_INDEX|WITH_POSITION|WITH_SECTION Tags _key,label +column_create Terms is_stop_word COLUMN_SCALAR Bool + +#@collect-query-log true +io_flush Memos.tags --recursive dependent +#@collect-query-log false Added: test/command/suite/io_flush/recursive/dependent/index_column/source_column.expected (+22 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/io_flush/recursive/dependent/index_column/source_column.expected 2019-04-17 10:17:28 +0900 (356d281ba) @@ -0,0 +1,22 @@ +table_create Memos TABLE_HASH_KEY ShortText +[[0,0.0,0.0],true] +column_create Memos timestamp COLUMN_SCALAR Time +[[0,0.0,0.0],true] +table_create Timestamps TABLE_PAT_KEY Time +[[0,0.0,0.0],true] +column_create Timestamps memos_timestamp_index COLUMN_INDEX Memos timestamp +[[0,0.0,0.0],true] +column_create Timestamps is_special COLUMN_SCALAR Bool +[[0,0.0,0.0],true] +io_flush Timestamps.memos_timestamp_index --recursive dependent +[[0,0.0,0.0],true] +#>io_flush --recursive "dependent" --target_name "Timestamps.memos_timestamp_index" +#:000000000000000 flush[Timestamps] +#:000000000000000 flush[Memos.timestamp] +#:000000000000000 flush[Timestamps.memos_timestamp_index] +#:000000000000000 flush[(anonymous:table:dat_key)] +#:000000000000000 flush[(anonymous:column:var_size)] +#:000000000000000 flush[(anonymous:table:hash_key)] +#:000000000000000 flush[(anonymous:column:var_size)] +#:000000000000000 flush[(DB)] +#<000000000000000 rc=0 Added: test/command/suite/io_flush/recursive/dependent/index_column/source_column.test (+10 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/io_flush/recursive/dependent/index_column/source_column.test 2019-04-17 10:17:28 +0900 (940dd2010) @@ -0,0 +1,10 @@ +table_create Memos TABLE_HASH_KEY ShortText +column_create Memos timestamp COLUMN_SCALAR Time + +table_create Timestamps TABLE_PAT_KEY Time +column_create Timestamps memos_timestamp_index COLUMN_INDEX Memos timestamp +column_create Timestamps is_special COLUMN_SCALAR Bool + +#@collect-query-log true +io_flush Timestamps.memos_timestamp_index --recursive dependent +#@collect-query-log false Added: test/command/suite/io_flush/recursive/dependent/index_column/source_key.expected (+22 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/io_flush/recursive/dependent/index_column/source_key.expected 2019-04-17 10:17:28 +0900 (bfabfd088) @@ -0,0 +1,22 @@ +table_create Memos TABLE_HASH_KEY ShortText +[[0,0.0,0.0],true] +column_create Memos timestamp COLUMN_SCALAR Time +[[0,0.0,0.0],true] +table_create Terms TABLE_PAT_KEY ShortText --default_tokenizer TokenBigram --default_normalizer NormalizerNFKC100 +[[0,0.0,0.0],true] +column_create Terms memos_key_index COLUMN_INDEX|WITH_POSITION Memos _key +[[0,0.0,0.0],true] +column_create Terms is_stop_word COLUMN_SCALAR Bool +[[0,0.0,0.0],true] +io_flush Terms.memos_key_index --recursive dependent +[[0,0.0,0.0],true] +#>io_flush --recursive "dependent" --target_name "Terms.memos_key_index" +#:000000000000000 flush[Terms] +#:000000000000000 flush[Memos] +#:000000000000000 flush[Terms.memos_key_index] +#:000000000000000 flush[(anonymous:table:dat_key)] +#:000000000000000 flush[(anonymous:column:var_size)] +#:000000000000000 flush[(anonymous:table:hash_key)] +#:000000000000000 flush[(anonymous:column:var_size)] +#:000000000000000 flush[(DB)] +#<000000000000000 rc=0 Added: test/command/suite/io_flush/recursive/dependent/index_column/source_key.test (+12 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/io_flush/recursive/dependent/index_column/source_key.test 2019-04-17 10:17:28 +0900 (d8d40c7f6) @@ -0,0 +1,12 @@ +table_create Memos TABLE_HASH_KEY ShortText +column_create Memos timestamp COLUMN_SCALAR Time + +table_create Terms TABLE_PAT_KEY ShortText \ + --default_tokenizer TokenBigram \ + --default_normalizer NormalizerNFKC100 +column_create Terms memos_key_index COLUMN_INDEX|WITH_POSITION Memos _key +column_create Terms is_stop_word COLUMN_SCALAR Bool + +#@collect-query-log true +io_flush Terms.memos_key_index --recursive dependent +#@collect-query-log false Added: test/command/suite/io_flush/recursive/dependent/index_column/sources.expected (+28 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/io_flush/recursive/dependent/index_column/sources.expected 2019-04-17 10:17:28 +0900 (cbf21249f) @@ -0,0 +1,28 @@ +table_create Memos TABLE_HASH_KEY ShortText +[[0,0.0,0.0],true] +column_create Memos title COLUMN_SCALAR ShortText +[[0,0.0,0.0],true] +column_create Memos content COLUMN_SCALAR Text +[[0,0.0,0.0],true] +column_create Memos timestamp COLUMN_SCALAR Time +[[0,0.0,0.0],true] +table_create Terms TABLE_PAT_KEY ShortText --default_tokenizer TokenBigram --default_normalizer NormalizerNFKC100 +[[0,0.0,0.0],true] +column_create Terms memos_index COLUMN_INDEX|WITH_POSITION|WITH_SECTION Memos _key,title,content +[[0,0.0,0.0],true] +column_create Terms is_stop_word COLUMN_SCALAR Bool +[[0,0.0,0.0],true] +io_flush Terms.memos_index --recursive dependent +[[0,0.0,0.0],true] +#>io_flush --recursive "dependent" --target_name "Terms.memos_index" +#:000000000000000 flush[Terms] +#:000000000000000 flush[Memos] +#:000000000000000 flush[Memos.title] +#:000000000000000 flush[Memos.content] +#:000000000000000 flush[Terms.memos_index] +#:000000000000000 flush[(anonymous:table:dat_key)] +#:000000000000000 flush[(anonymous:column:var_size)] +#:000000000000000 flush[(anonymous:table:hash_key)] +#:000000000000000 flush[(anonymous:column:var_size)] +#:000000000000000 flush[(DB)] +#<000000000000000 rc=0 Added: test/command/suite/io_flush/recursive/dependent/index_column/sources.test (+15 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/io_flush/recursive/dependent/index_column/sources.test 2019-04-17 10:17:28 +0900 (d65f0db34) @@ -0,0 +1,15 @@ +table_create Memos TABLE_HASH_KEY ShortText +column_create Memos title COLUMN_SCALAR ShortText +column_create Memos content COLUMN_SCALAR Text +column_create Memos timestamp COLUMN_SCALAR Time + +table_create Terms TABLE_PAT_KEY ShortText \ + --default_tokenizer TokenBigram \ + --default_normalizer NormalizerNFKC100 +column_create Terms memos_index \ + COLUMN_INDEX|WITH_POSITION|WITH_SECTION Memos _key,title,content +column_create Terms is_stop_word COLUMN_SCALAR Bool + +#@collect-query-log true +io_flush Terms.memos_index --recursive dependent +#@collect-query-log false Added: test/command/suite/io_flush/recursive/dependent/table/reference.expected (+27 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/io_flush/recursive/dependent/table/reference.expected 2019-04-17 10:17:28 +0900 (707cbd7be) @@ -0,0 +1,27 @@ +table_create Tags TABLE_HASH_KEY ShortText +[[0,0.0,0.0],true] +column_create Tags label COLUMN_SCALAR ShortText +[[0,0.0,0.0],true] +table_create Titles TABLE_HASH_KEY ShortText +[[0,0.0,0.0],true] +column_create Titles tag COLUMN_SCALAR Tags +[[0,0.0,0.0],true] +table_create Memos TABLE_HASH_KEY Titles +[[0,0.0,0.0],true] +column_create Memos content COLUMN_SCALAR Text +[[0,0.0,0.0],true] +column_create Memos timestamp COLUMN_SCALAR Time +[[0,0.0,0.0],true] +io_flush Memos --recursive dependent +[[0,0.0,0.0],true] +#>io_flush --recursive "dependent" --target_name "Memos" +#:000000000000000 flush[Titles] +#:000000000000000 flush[Memos.content] +#:000000000000000 flush[Memos.timestamp] +#:000000000000000 flush[Memos] +#:000000000000000 flush[(anonymous:table:dat_key)] +#:000000000000000 flush[(anonymous:column:var_size)] +#:000000000000000 flush[(anonymous:table:hash_key)] +#:000000000000000 flush[(anonymous:column:var_size)] +#:000000000000000 flush[(DB)] +#<000000000000000 rc=0 Added: test/command/suite/io_flush/recursive/dependent/table/reference.test (+13 -0) 100644 =================================================================== --- /dev/null +++ test/command/suite/io_flush/recursive/dependent/table/reference.test 2019-04-17 10:17:28 +0900 (0a975a078) @@ -0,0 +1,13 @@ +table_create Tags TABLE_HASH_KEY ShortText +column_create Tags label COLUMN_SCALAR ShortText + +table_create Titles TABLE_HASH_KEY ShortText +column_create Titles tag COLUMN_SCALAR Tags + +table_create Memos TABLE_HASH_KEY Titles +column_create Memos content COLUMN_SCALAR Text +column_create Memos timestamp COLUMN_SCALAR Time + +#@collect-query-log true +io_flush Memos --recursive dependent +#@collect-query-log false Modified: test/command/suite/io_flush/target_name/column.expected (+0 -3) =================================================================== --- test/command/suite/io_flush/target_name/column.expected 2019-04-15 18:00:02 +0900 (cf57c25a2) +++ test/command/suite/io_flush/target_name/column.expected 2019-04-17 10:17:28 +0900 (4fab3e62e) @@ -9,9 +9,6 @@ column_create Terms users_name COLUMN_INDEX|WITH_POSITION Users name io_flush Users.name [[0,0.0,0.0],true] #>io_flush --target_name "Users.name" -#:000000000000000 flush[Users] -#:000000000000000 flush[Terms] -#:000000000000000 flush[Terms.users_name] #:000000000000000 flush[Users.name] #:000000000000000 flush[(anonymous:table:dat_key)] #:000000000000000 flush[(anonymous:column:var_size)] Modified: test/command/suite/io_flush/target_name/invalid.expected (+2 -2) =================================================================== --- test/command/suite/io_flush/target_name/invalid.expected 2019-04-15 18:00:02 +0900 (ed4fab584) +++ test/command/suite/io_flush/target_name/invalid.expected 2019-04-17 10:17:28 +0900 (760da6af6) @@ -6,8 +6,8 @@ io_flush TokenBigram 0.0, 0.0 ], - "[flush] object must be DB, table or column: <#<proc:tokenizer TokenBigram arguments:[$1, $2, $3]>>" + "[io][flush][recursive] object must be DB, table or column: <#<proc:tokenizer TokenBigram arguments:[$1, $2, $3]>>" ], false ] -#|e| [flush] object must be DB, table or column: <#<proc:tokenizer TokenBigram arguments:[$1, $2, $3]>> +#|e| [io][flush][recursive] object must be DB, table or column: <#<proc:tokenizer TokenBigram arguments:[$1, $2, $3]>> Modified: test/command/suite/io_flush/target_name/table.expected (+0 -2) =================================================================== --- test/command/suite/io_flush/target_name/table.expected 2019-04-15 18:00:02 +0900 (69109a1a5) +++ test/command/suite/io_flush/target_name/table.expected 2019-04-17 10:17:28 +0900 (c8e7db378) @@ -9,8 +9,6 @@ column_create Terms users_name COLUMN_INDEX|WITH_POSITION Users name io_flush Users [[0,0.0,0.0],true] #>io_flush --target_name "Users" -#:000000000000000 flush[Terms] -#:000000000000000 flush[Terms.users_name] #:000000000000000 flush[Users.name] #:000000000000000 flush[Users] #:000000000000000 flush[(anonymous:table:dat_key)] -------------- next part -------------- An HTML attachment was scrubbed... URL: <https://lists.osdn.me/mailman/archives/groonga-commit/attachments/20190417/d65b7b3b/attachment-0001.html>