null+****@clear*****
null+****@clear*****
2011年 9月 25日 (日) 20:01:30 JST
Kouhei Sutou 2011-09-25 11:01:30 +0000 (Sun, 25 Sep 2011) New Revision: 98ef30703a5f6d3c546851bc83f3a300a30afb0d Log: [wrapper] support fast order and limit. Added files: test/sql/groonga_wrapper/r/order_limit_performance.result test/sql/groonga_wrapper/t/order_limit_performance.test Modified files: ha_mroonga.cc ha_mroonga.h mrn_sys.h Modified: ha_mroonga.cc (+187 -15) =================================================================== --- ha_mroonga.cc 2011-09-25 07:28:45 +0000 (a0084bc) +++ ha_mroonga.cc 2011-09-25 11:01:30 +0000 (ee33579) @@ -1095,6 +1095,7 @@ static void mrn_wrapper_ft_close_search(FT_INFO *handler) MRN_DBUG_ENTER_FUNCTION(); st_mrn_ft_info *info = (st_mrn_ft_info *)handler; grn_obj_unlink(info->ctx, info->result); + grn_obj_unlink(info->ctx, info->sorted_result); grn_obj_unlink(info->ctx, info->score_column); grn_obj_unlink(info->ctx, &(info->key)); grn_obj_unlink(info->ctx, &(info->score)); @@ -1202,6 +1203,7 @@ ha_mroonga::ha_mroonga(handlerton *hton, TABLE_SHARE *share) result0 = NULL; result_geo = NULL; score_column = NULL; + key_accessor = NULL; share = NULL; is_clone = FALSE; wrap_handler = NULL; @@ -4764,12 +4766,18 @@ int ha_mroonga::wrapper_ft_init() { MRN_DBUG_ENTER_METHOD(); int error; - cursor = grn_table_cursor_open(ctx, matched_record_keys, NULL, 0, NULL, 0, 0, - -1, 0); - if (ctx->rc) - { - error = ER_ERROR_ON_READ; - my_message(error, ctx->errbuf, MYF(0)); + if (!cursor) { + cursor = grn_table_cursor_open(ctx, matched_record_keys, NULL, 0, NULL, 0, 0, + -1, 0); + if (ctx->rc) + { + error = ER_ERROR_ON_READ; + my_message(error, ctx->errbuf, MYF(0)); + } else { + key_accessor = grn_obj_column(ctx, matched_record_keys, + MRN_COLUMN_NAME_KEY, + strlen(MRN_COLUMN_NAME_KEY)); + } } DBUG_RETURN(error); } @@ -4818,6 +4826,9 @@ void ha_mroonga::ft_end() FT_INFO *ha_mroonga::wrapper_ft_init_ext(uint flags, uint key_nr, String *key) { MRN_DBUG_ENTER_METHOD(); + + clear_cursor(); + struct st_mrn_ft_info *info = new st_mrn_ft_info(); info->please = &mrn_wrapper_ft_vft; info->mroonga = this; @@ -4885,7 +4896,29 @@ FT_INFO *ha_mroonga::wrapper_ft_init_ext(uint flags, uint key_nr, String *key) grn_obj_unlink(info->ctx, expression); grn_obj_unlink(info->ctx, match_columns); - merge_matched_record_keys(info->result); + grn_table_sort_key *sort_keys = NULL; + int n_sort_keys = 0; + longlong limit = -1; + check_fast_order_limit(&sort_keys, &n_sort_keys, &limit, info->score_column); + if (fast_order_limit) { + info->sorted_result = grn_table_create(ctx, NULL, + 0, NULL, + GRN_OBJ_TABLE_NO_KEY, NULL, + info->result); + grn_table_sort(ctx, info->result, 0, limit, info->sorted_result, + sort_keys, n_sort_keys); + cursor = grn_table_cursor_open(ctx, info->sorted_result, + NULL, 0, NULL, 0, + 0, -1, 0); + key_accessor = grn_obj_column(ctx, info->sorted_result, + MRN_COLUMN_NAME_KEY, + strlen(MRN_COLUMN_NAME_KEY)); + } else { + merge_matched_record_keys(info->result); + } + if (sort_keys) { + free(sort_keys); + } DBUG_RETURN((FT_INFO *)info); } @@ -4999,15 +5032,11 @@ int ha_mroonga::wrapper_ft_read(uchar *buf) record_id = grn_table_cursor_next(ctx, cursor); if (record_id == GRN_ID_NIL) { error = HA_ERR_END_OF_FILE; - grn_table_cursor_close(ctx, cursor); - cursor = NULL; + clear_cursor(); break; } else { - grn_id relation_record_id; - grn_table_get_key(ctx, matched_record_keys, record_id, - &relation_record_id, sizeof(grn_id)); - grn_table_get_key(ctx, grn_table, relation_record_id, - GRN_TEXT_VALUE(&pkey), table->key_info->key_length); + GRN_BULK_REWIND(&pkey); + grn_obj_get_value(ctx, key_accessor, record_id, &pkey); MRN_SET_WRAP_SHARE_KEY(share, table->s); MRN_SET_WRAP_TABLE_KEY(this, table); #ifdef MRN_HANDLER_HAVE_HA_INDEX_READ_IDX_MAP @@ -5193,9 +5222,13 @@ void ha_mroonga::push_warning_unsupported_spatial_index_search(enum ha_rkey_func search_name); } -void ha_mroonga::clear_search_result() +void ha_mroonga::clear_cursor() { MRN_DBUG_ENTER_METHOD(); + if (key_accessor) { + grn_obj_unlink(ctx, key_accessor); + key_accessor = NULL; + } if (cursor) { grn_table_cursor_close(ctx, cursor); cursor = NULL; @@ -5204,6 +5237,13 @@ void ha_mroonga::clear_search_result() grn_table_cursor_close(ctx, index_table_cursor); index_table_cursor = NULL; } + DBUG_VOID_RETURN; +} + +void ha_mroonga::clear_search_result() +{ + MRN_DBUG_ENTER_METHOD(); + clear_cursor(); if (result0) { grn_obj_unlink(ctx, result0); result0 = NULL; @@ -5516,6 +5556,138 @@ void ha_mroonga::check_fast_order_limit(grn_table_sort_key **sort_keys, DBUG_VOID_RETURN; } +void ha_mroonga::check_fast_order_limit(grn_table_sort_key **sort_keys, + int *n_sort_keys, + longlong *limit, + grn_obj *score_column) +{ + MRN_DBUG_ENTER_METHOD(); + st_select_lex *select_lex = table->pos_in_table_list->select_lex; + + if ( + thd_sql_command(ha_thd()) == SQLCOM_SELECT && + !select_lex->with_sum_func && + !select_lex->group_list.elements && + !select_lex->having && + select_lex->table_list.elements == 1 && + select_lex->order_list.elements && + select_lex->explicit_limit && + select_lex->select_limit && + select_lex->select_limit->val_int() > 0 + ) { + if (select_lex->offset_limit) { + *limit = select_lex->offset_limit->val_int(); + } else { + *limit = 0; + } + *limit += select_lex->select_limit->val_int(); + if (*limit > (longlong)INT_MAX) { + DBUG_PRINT("info", ("mroonga fast_order_limit = FALSE")); + fast_order_limit = FALSE; + DBUG_VOID_RETURN; + } + Item *info = (Item *)select_lex->item_list.first_node()->info; + Item *where; + where = select_lex->where; + if (!where || + where->type() != Item::FUNC_ITEM || + ((Item_func *)where)->functype() != Item_func::FT_FUNC) { + DBUG_PRINT("info", ("mroonga fast_order_limit = FALSE")); + fast_order_limit = FALSE; + DBUG_VOID_RETURN; + } + Item_func *match_against = (Item_func *)where; + where = where->next; + if (!where || + where->type() != Item::STRING_ITEM) { + DBUG_PRINT("info", ("mroonga fast_order_limit = FALSE")); + fast_order_limit = FALSE; + DBUG_VOID_RETURN; + } + where = where->next; + if (!where || + where->type() != Item::FIELD_ITEM) { + DBUG_PRINT("info", ("mroonga fast_order_limit = FALSE")); + fast_order_limit = FALSE; + DBUG_VOID_RETURN; + } + for (where = where->next; where; where = where->next) { + if (grn_columns && where->type() == Item::FIELD_ITEM) + continue; + if (where->type() == Item::FUNC_ITEM && match_against->eq(where, true)) { + for (int i = 0; i < match_against->arg_count; i++) { + where = where->next; + } + continue; + } + if (where == info) + continue; + break; + } + if (where && (where != info || !where->eq(info, true))) { + DBUG_PRINT("info", ("mroonga fast_order_limit = FALSE")); + fast_order_limit = FALSE; + DBUG_VOID_RETURN; + } + *n_sort_keys = select_lex->order_list.elements; + *sort_keys = (grn_table_sort_key *)malloc(sizeof(grn_table_sort_key) * + *n_sort_keys); + ORDER *order; + int i, col_field_index = -1; + for (order = (ORDER *) select_lex->order_list.first, i = 0; order; + order = order->next, i++) { + Item *item = *order->item; + if (grn_columns && item->type() == Item::FIELD_ITEM) + { + Field *field = ((Item_field *) (*order->item))->field; + const char *column_name = field->field_name; + int column_name_size = strlen(column_name); + + if (strncmp(MRN_COLUMN_NAME_ID, column_name, column_name_size) == 0) { + (*sort_keys)[i].key = grn_obj_column(ctx, grn_table, + column_name, column_name_size); + } else if (strncmp(MRN_COLUMN_NAME_SCORE, + column_name, column_name_size) == 0) { + (*sort_keys)[i].key = score_column; + } else { + (*sort_keys)[i].key = grn_columns[field->field_index]; + col_field_index = field->field_index; + } + } else if (match_against->eq(item, true)) { + (*sort_keys)[i].key = score_column; + } else { + DBUG_PRINT("info", ("mroonga fast_order_limit = FALSE")); + fast_order_limit = FALSE; + DBUG_VOID_RETURN; + } + (*sort_keys)[i].offset = 0; + if (MRN_ORDER_IS_ASC(order)) + { + (*sort_keys)[i].flags = GRN_TABLE_SORT_ASC; + } else { + (*sort_keys)[i].flags = GRN_TABLE_SORT_DESC; + } + } + grn_obj *index; + if (i == 1 && col_field_index >= 0 && + grn_column_index(ctx, grn_columns[col_field_index], GRN_OP_LESS, + &index, 1, NULL)) { + DBUG_PRINT("info", ("mroonga fast_order_limit_with_index = TRUE")); + fast_order_limit_with_index = TRUE; + } else { + DBUG_PRINT("info", ("mroonga fast_order_limit_with_index = FALSE")); + fast_order_limit_with_index = FALSE; + } + DBUG_PRINT("info", ("mroonga fast_order_limit = TRUE")); + fast_order_limit = TRUE; + mrn_fast_order_limit++; + DBUG_VOID_RETURN; + } + DBUG_PRINT("info", ("mroonga fast_order_limit = FALSE")); + fast_order_limit = FALSE; + DBUG_VOID_RETURN; +} + void ha_mroonga::store_fields_from_primary_table(uchar *buf, grn_id record_id) { MRN_DBUG_ENTER_METHOD(); Modified: ha_mroonga.h (+6 -1) =================================================================== --- ha_mroonga.h 2011-09-25 07:28:45 +0000 (3c1d906) +++ ha_mroonga.h 2011-09-25 11:01:30 +0000 (266252b) @@ -1,4 +1,4 @@ -/* +/* Copyright(C) 2010 Tetsuro IKEDA Copyright(C) 2010-2011 Kentoku SHIBA Copyright(C) 2011 Kouhei Sutou <kou****@clear*****> @@ -86,6 +86,7 @@ struct st_mrn_ft_info grn_ctx *ctx; grn_obj *table; grn_obj *result; + grn_obj *sorted_result; grn_obj *score_column; grn_obj key; grn_obj score; @@ -134,6 +135,7 @@ private: grn_table_cursor *index_table_cursor; grn_id record_id; grn_obj *score_column; + grn_obj *key_accessor; st_mrn_ft_info mrn_ft_info; grn_obj *matched_record_keys; @@ -330,6 +332,7 @@ protected: private: void push_warning_unsupported_spatial_index_search(enum ha_rkey_function flag); + void clear_cursor(); void clear_search_result(); grn_obj *find_tokenizer(const char *name, int name_length); int storage_get_next_record(uchar *buf); @@ -342,6 +345,8 @@ private: void check_count_skip(key_part_map start_key_part_map, key_part_map end_key_part_map, bool fulltext); void check_fast_order_limit(grn_table_sort_key **sort_keys, int *n_sort_keys); + void check_fast_order_limit(grn_table_sort_key **sort_keys, int *n_sort_keys, + longlong *limit, grn_obj *score_column); void store_fields_from_primary_table(uchar *buf, grn_id record_id); void set_pk_bitmap(); int wrapper_create(const char *name, TABLE *table, Modified: mrn_sys.h (+1 -0) =================================================================== --- mrn_sys.h 2011-09-25 07:28:45 +0000 (b9112b9) +++ mrn_sys.h 2011-09-25 11:01:30 +0000 (3e8bf26) @@ -35,6 +35,7 @@ #define MRN_HASH_SUFFIX "_hash" #define MRN_PAT_SUFFIX "_pat" #define MRN_COLUMN_NAME_ID "_id" +#define MRN_COLUMN_NAME_KEY "_key" #define MRN_COLUMN_NAME_SCORE "_score" #ifndef MRN_TOKENIZER_DEFAULT # define MRN_TOKENIZER_DEFAULT "TokenBigram" Added: test/sql/groonga_wrapper/r/order_limit_performance.result (+66 -0) 100644 =================================================================== --- /dev/null +++ test/sql/groonga_wrapper/r/order_limit_performance.result 2011-09-25 11:01:30 +0000 (63971d2) @@ -0,0 +1,66 @@ +drop table if exists t1; +create table t1 ( +c1 int primary key, +c2 int, +c3 text, +key idx1(c2), +fulltext index ft(c3) +) comment = 'engine "innodb"'; +insert into t1 values(1,10,"aa ii uu ee oo"); +insert into t1 values(2,20,"ka ki ku ke ko"); +insert into t1 values(3,30,"ii si ii se ii"); +insert into t1 values(4,40,"ta ti tu te to"); +insert into t1 values(5,50,"aa ii uu ii oo"); +select *, match(c3) against("ii") from t1 +where match(c3) against("ii") order by c1 desc limit 1; +c1 c2 c3 match(c3) against("ii") +5 50 aa ii uu ii oo 2 +show status like 'groonga_fast_order_limit'; +Variable_name Value +groonga_fast_order_limit 0 +select *, match(c3) against("ii") from t1 +where match(c3) against("ii") order by c1 limit 1; +c1 c2 c3 match(c3) against("ii") +1 10 aa ii uu ee oo 1 +show status like 'groonga_fast_order_limit'; +Variable_name Value +groonga_fast_order_limit 0 +select c3, match(c3) against("ii") from t1 +where match(c3) against("ii") order by match(c3) against("ii") desc; +c3 match(c3) against("ii") +ii si ii se ii 3 +aa ii uu ii oo 2 +aa ii uu ee oo 1 +show status like 'groonga_fast_order_limit'; +Variable_name Value +groonga_fast_order_limit 0 +select c3, match(c3) against("ii") from t1 +where match(c3) against("ii") order by match(c3) against("ii") desc limit 1, 1; +c3 match(c3) against("ii") +aa ii uu ii oo 2 +show status like 'groonga_fast_order_limit'; +Variable_name Value +groonga_fast_order_limit 1 +select c3, match(c3) against("ii") from t1 +where match(c3) against("ii") order by match(c3) against("ii"); +c3 match(c3) against("ii") +aa ii uu ee oo 1 +aa ii uu ii oo 2 +ii si ii se ii 3 +show status like 'groonga_fast_order_limit'; +Variable_name Value +groonga_fast_order_limit 1 +select c3, match(c3) against("ii") from t1 +where match(c3) against("ii") order by match(c3) against("ii") limit 1; +c3 match(c3) against("ii") +aa ii uu ee oo 1 +show status like 'groonga_fast_order_limit'; +Variable_name Value +groonga_fast_order_limit 2 +select count(*) from t1 where match(c3) against("ii") limit 1; +count(*) +3 +show status like 'groonga_fast_order_limit'; +Variable_name Value +groonga_fast_order_limit 2 +drop table t1; Added: test/sql/groonga_wrapper/t/order_limit_performance.test (+66 -0) 100644 =================================================================== --- /dev/null +++ test/sql/groonga_wrapper/t/order_limit_performance.test 2011-09-25 11:01:30 +0000 (739f910) @@ -0,0 +1,66 @@ +# Copyright(C) 2010 Kentoku SHIBA +# Copyright(C) 2011 Kouhei Sutou <kou****@clear*****> +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +--source suite/groonga_include/groonga_init.inc + +--disable_warnings +drop table if exists t1; +--enable_warnings + +create table t1 ( + c1 int primary key, + c2 int, + c3 text, + key idx1(c2), + fulltext index ft(c3) +) comment = 'engine "innodb"'; +insert into t1 values(1,10,"aa ii uu ee oo"); +insert into t1 values(2,20,"ka ki ku ke ko"); +insert into t1 values(3,30,"ii si ii se ii"); +insert into t1 values(4,40,"ta ti tu te to"); +insert into t1 values(5,50,"aa ii uu ii oo"); + +select *, match(c3) against("ii") from t1 + where match(c3) against("ii") order by c1 desc limit 1; +show status like 'groonga_fast_order_limit'; + +select *, match(c3) against("ii") from t1 + where match(c3) against("ii") order by c1 limit 1; +show status like 'groonga_fast_order_limit'; + +select c3, match(c3) against("ii") from t1 + where match(c3) against("ii") order by match(c3) against("ii") desc; +show status like 'groonga_fast_order_limit'; + +select c3, match(c3) against("ii") from t1 + where match(c3) against("ii") order by match(c3) against("ii") desc limit 1, 1; +show status like 'groonga_fast_order_limit'; + +select c3, match(c3) against("ii") from t1 + where match(c3) against("ii") order by match(c3) against("ii"); +show status like 'groonga_fast_order_limit'; + +select c3, match(c3) against("ii") from t1 + where match(c3) against("ii") order by match(c3) against("ii") limit 1; +show status like 'groonga_fast_order_limit'; + +select count(*) from t1 where match(c3) against("ii") limit 1; +show status like 'groonga_fast_order_limit'; + +drop table t1; + +--source suite/groonga_include/groonga_deinit.inc