Kouhei Sutou
null+****@clear*****
Wed Feb 15 10:44:30 JST 2017
Kouhei Sutou 2017-02-15 10:44:30 +0900 (Wed, 15 Feb 2017) New Revision: a725e2b2907b1e5a3e0e96df2415db2b3848adcc https://github.com/groonga/groonga/commit/a725e2b2907b1e5a3e0e96df2415db2b3848adcc Message: Support index search for "!(column == 29)" It's evaluated as "all_records() &! (column == 29)". Added files: test/mruby/suite/query_optimizer/index/test_compare.rb test/mruby/suite/query_optimizer/no_index/test_compare.rb Modified files: lib/expr.c lib/mrb/scripts/scan_info_builder.rb test/command/suite/select/filter/unary_operation/not_equal.expected Modified: lib/expr.c (+29 -11) =================================================================== --- lib/expr.c 2017-02-15 10:37:00 +0900 (f612215) +++ lib/expr.c 2017-02-15 10:44:30 +0900 (2b6a433) @@ -4546,6 +4546,7 @@ grn_scan_info_build_full(grn_ctx *ctx, grn_obj *expr, int *n, grn_obj *var; scan_stat stat; int i, m = 0, o = 0; + int n_nots = 0; scan_info **sis, *si = NULL; grn_expr_code *c, *ce; grn_expr *e = (grn_expr *)expr; @@ -4686,6 +4687,7 @@ grn_scan_info_build_full(grn_ctx *ctx, grn_obj *expr, int *n, } break; case GRN_OP_NOT : + n_nots++; break; default : return NULL; @@ -4693,7 +4695,7 @@ grn_scan_info_build_full(grn_ctx *ctx, grn_obj *expr, int *n, } } if (stat || m != o + 1) { return NULL; } - if (!(sis = GRN_MALLOCN(scan_info *, m + m + o))) { return NULL; } + if (!(sis = GRN_MALLOCN(scan_info *, m + m + o + n_nots))) { return NULL; } for (i = 0, stat = SCAN_START, c = e->codes, ce = &e->codes[e->codes_curr]; c < ce; c++) { switch (c->op) { case GRN_OP_MATCH : @@ -4895,33 +4897,49 @@ grn_scan_info_build_full(grn_ctx *ctx, grn_obj *expr, int *n, switch (last_si->op) { case GRN_OP_LESS : last_si->op = GRN_OP_GREATER_EQUAL; + last_si->end++; break; case GRN_OP_LESS_EQUAL : last_si->op = GRN_OP_GREATER; + last_si->end++; break; case GRN_OP_GREATER : last_si->op = GRN_OP_LESS_EQUAL; + last_si->end++; break; case GRN_OP_GREATER_EQUAL : last_si->op = GRN_OP_LESS; - break; - case GRN_OP_EQUAL : - last_si->op = GRN_OP_NOT_EQUAL; + last_si->end++; break; case GRN_OP_NOT_EQUAL : last_si->op = GRN_OP_EQUAL; + last_si->end++; break; default : - { - int j; - for (j = 0; j < i; j++) { - SI_FREE(sis[j]); + if (i == 1 && GRN_BULK_VSIZE(&(last_si->index)) > 0) { + SI_ALLOC(si, 0, 0); + si->op = GRN_OP_CALL; + si->args[si->nargs++] = grn_ctx_get(ctx, "all_records", -1); + last_si->logical_op = GRN_OP_AND_NOT; + last_si->flags &= ~SCAN_PUSH; + sis[i] = sis[i - 1]; + sis[i - 1] = si; + i++; + } else { + if (last_si->op == GRN_OP_EQUAL) { + last_si->op = GRN_OP_NOT_EQUAL; + last_si->end++; + } else { + int j; + for (j = 0; j < i; j++) { + SI_FREE(sis[j]); + } + GRN_FREE(sis); + return NULL; } - GRN_FREE(sis); } - return NULL; + break; } - last_si->end++; } break; default : Modified: lib/mrb/scripts/scan_info_builder.rb (+28 -4) =================================================================== --- lib/mrb/scripts/scan_info_builder.rb 2017-02-15 10:37:00 +0900 (f58bfa2) +++ lib/mrb/scripts/scan_info_builder.rb 2017-02-15 10:44:30 +0900 (ec4e476) @@ -150,20 +150,33 @@ module Groonga case last_data.op when Operator::LESS last_data.op = Operator::GREATER_EQUAL + last_data.end += 1 when Operator::LESS_EQUAL last_data.op = Operator::GREATER + last_data.end += 1 when Operator::GREATER last_data.op = Operator::LESS_EQUAL + last_data.end += 1 when Operator::GREATER_EQUAL last_data.op = Operator::LESS - when Operator::EQUAL - last_data.op = Operator::NOT_EQUAL + last_data.end += 1 when Operator::NOT_EQUAL last_data.op = Operator::EQUAL + last_data.end += 1 else - return nil + if @data_list.size == 1 and not last_data.search_indexes.empty? + last_data.logical_op = Operator::AND_NOT + last_data.flags &= ~ScanInfo::Flags::PUSH + @data_list.unshift(create_all_match_data) + else + if last_data.op == Operator::EQUAL + last_data.op = Operator::NOT_EQUAL + last_data.end += 1 + else + return nil + end + end end - last_data.end += 1 end end @@ -393,6 +406,17 @@ module Groonga end end + def create_all_match_data + data = ScanInfoData.new(0) + data.end = 0 + data.flags = ScanInfo::Flags::PUSH + data.op = Operator::CALL + data.logical_op = Operator::OR + data.args = [Context.instance["all_records"]] + data.search_indexes = [] + data + end + def create_between_data(data, next_data) between_data = ScanInfoData.new(data.start) between_data.end = next_data.end + 1 Modified: test/command/suite/select/filter/unary_operation/not_equal.expected (+2 -0) =================================================================== --- test/command/suite/select/filter/unary_operation/not_equal.expected 2017-02-15 10:37:00 +0900 (891e677) +++ test/command/suite/select/filter/unary_operation/not_equal.expected 2017-02-15 10:44:30 +0900 (0155154) @@ -11,5 +11,7 @@ log_level --level info [[0,0.0,0.0],true] select Numbers --filter '!(_key == 29)' [[0,0.0,0.0],[[[2],[["_id","UInt32"],["_key","UInt32"]],[1,2],[3,2929]]]] +#|i| [table][select][index][selector][no-index][all_records] <Numbers> +#|i| [table][select][index][equal][accessor][key] <Numbers> log_level --level notice [[0,0.0,0.0],true] Added: test/mruby/suite/query_optimizer/index/test_compare.rb (+140 -0) 100644 =================================================================== --- /dev/null +++ test/mruby/suite/query_optimizer/index/test_compare.rb 2017-02-15 10:44:30 +0900 (82669b1) @@ -0,0 +1,140 @@ +class TestIndexCompare < QueryOptimizerTestCase + def setup + Groonga::Schema.define do |schema| + schema.create_table("Values") do |table| + table.int32("number") + end + + schema.create_table("Numbers", + :type => :patricia_trie, + :key_type => "Int32") do |table| + table.index("Values", "number") + end + end + + @values = Groonga["Values"] + setup_expression(@values) + end + + def teardown + teardown_expression + end + + def test_less + assert_equal(<<-DUMP, dump_plan("number < 29")) +[0] + op: <less> + logical_op: <or> + index: <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]> + query: <29> + expr: <0..2> + DUMP + end + + def test_not_less + assert_equal(<<-DUMP, dump_plan("!(number < 29)")) +[0] + op: <greater_equal> + logical_op: <or> + index: <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]> + query: <29> + expr: <0..3> + DUMP + end + + def test_not_less_equal + assert_equal(<<-DUMP, dump_plan("!(number <= 29)")) +[0] + op: <greater> + logical_op: <or> + index: <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]> + query: <29> + expr: <0..3> + DUMP + end + + def test_not_greater + assert_equal(<<-DUMP, dump_plan("!(number > 29)")) +[0] + op: <less_equal> + logical_op: <or> + index: <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]> + query: <29> + expr: <0..3> + DUMP + end + + def test_not_greater_equal + assert_equal(<<-DUMP, dump_plan("!(number >= 29)")) +[0] + op: <less> + logical_op: <or> + index: <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]> + query: <29> + expr: <0..3> + DUMP + end + + def test_not_equal + assert_equal(<<-DUMP, dump_plan("!(number == 29)")) +[0] + op: <call> + logical_op: <or> + args[0]: <#<proc:function all_records arguments:[]>> + expr: <0..0> +[1] + op: <equal> + logical_op: <and_not> + index: <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]> + query: <29> + expr: <0..2> + DUMP + end + + def test_not_not_equal + assert_equal(<<-DUMP, dump_plan("!(number != 29)")) +[0] + op: <equal> + logical_op: <or> + index: <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]> + query: <29> + expr: <0..3> + DUMP + end + + sub_test_case("multiple conditions") do + test("not at the first") do + assert_equal(<<-DUMP, dump_plan("!(number < 29) && _id == 29")) +[0] + op: <greater_equal> + logical_op: <or> + index: <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]> + query: <29> + expr: <0..3> +[1] + op: <equal> + logical_op: <and> + index: <[#<accessor _id(Values)>]> + query: <29> + expr: <4..6> + DUMP + end + + test("not at the last") do + assert_equal(<<-DUMP, dump_plan("_id == 29 && !(number < 29)")) +[0] + op: <equal> + logical_op: <or> + index: <[#<accessor _id(Values)>]> + query: <29> + expr: <0..2> +[1] + op: <greater_equal> + logical_op: <and> + index: <[#<column:index Numbers.Values_number range:Values sources:[Values.number] flags:NONE>]> + query: <29> + expr: <3..6> + DUMP + end + end +end Added: test/mruby/suite/query_optimizer/no_index/test_compare.rb (+129 -0) 100644 =================================================================== --- /dev/null +++ test/mruby/suite/query_optimizer/no_index/test_compare.rb 2017-02-15 10:44:30 +0900 (59185c5) @@ -0,0 +1,129 @@ +class TestNoIndexCompare < QueryOptimizerTestCase + def setup + Groonga::Schema.define do |schema| + schema.create_table("Values") do |table| + table.int32("number") + end + end + + @values = Groonga["Values"] + setup_expression(@values) + end + + def teardown + teardown_expression + end + + def test_less + assert_equal(<<-DUMP, dump_plan("number < 29")) +[0] + op: <less> + logical_op: <or> + index: <[]> + query: <29> + expr: <0..2> + DUMP + end + + def test_not_less + assert_equal(<<-DUMP, dump_plan("!(number < 29)")) +[0] + op: <greater_equal> + logical_op: <or> + index: <[]> + query: <29> + expr: <0..3> + DUMP + end + + def test_not_less_equal + assert_equal(<<-DUMP, dump_plan("!(number <= 29)")) +[0] + op: <greater> + logical_op: <or> + index: <[]> + query: <29> + expr: <0..3> + DUMP + end + + def test_not_greater + assert_equal(<<-DUMP, dump_plan("!(number > 29)")) +[0] + op: <less_equal> + logical_op: <or> + index: <[]> + query: <29> + expr: <0..3> + DUMP + end + + def test_not_greater_equal + assert_equal(<<-DUMP, dump_plan("!(number >= 29)")) +[0] + op: <less> + logical_op: <or> + index: <[]> + query: <29> + expr: <0..3> + DUMP + end + + def test_not_equal + assert_equal(<<-DUMP, dump_plan("!(number == 29)")) +[0] + op: <not_equal> + logical_op: <or> + index: <[]> + query: <29> + expr: <0..3> + DUMP + end + + def test_not_not_equal + assert_equal(<<-DUMP, dump_plan("!(number != 29)")) +[0] + op: <equal> + logical_op: <or> + index: <[]> + query: <29> + expr: <0..3> + DUMP + end + + sub_test_case("multiple conditions") do + test("not at the first") do + assert_equal(<<-DUMP, dump_plan("!(number < 29) && _id == 29")) +[0] + op: <greater_equal> + logical_op: <or> + index: <[]> + query: <29> + expr: <0..3> +[1] + op: <equal> + logical_op: <and> + index: <[#<accessor _id(Values)>]> + query: <29> + expr: <4..6> + DUMP + end + + test("not at the last") do + assert_equal(<<-DUMP, dump_plan("_id == 29 && !(number < 29)")) +[0] + op: <equal> + logical_op: <or> + index: <[#<accessor _id(Values)>]> + query: <29> + expr: <0..2> +[1] + op: <greater_equal> + logical_op: <and> + index: <[]> + query: <29> + expr: <3..6> + DUMP + end + end +end -------------- next part -------------- HTML����������������������������...下载