From 6a1d63eddbc152d08fa76449cdf60c33e7bd1213 Mon Sep 17 00:00:00 2001 From: Doodle <13706157+critical27@users.noreply.github.com> Date: Thu, 22 Dec 2022 16:31:31 +0800 Subject: [PATCH 1/3] add some truncated fixed string case (#5085) * add some truncated fixed string case * add test case about utf8 truncated in index Co-authored-by: hs.zhang <22708345+cangfengzhs@users.noreply.github.com> Co-authored-by: Sophie <84560950+Sophie-Xie@users.noreply.github.com> --- src/codec/Common.h | 30 + src/codec/RowWriterV2.cpp | 3 +- tests/tck/features/fetch/FetchEmpty.feature | 92 ++ tests/tck/features/index/Index.feature | 948 ++++++++++++++++++++ 4 files changed, 1072 insertions(+), 1 deletion(-) diff --git a/src/codec/Common.h b/src/codec/Common.h index 328231dd971..0e56686ff30 100644 --- a/src/codec/Common.h +++ b/src/codec/Common.h @@ -77,5 +77,35 @@ inline std::string toHexStr(folly::StringPiece str) { return buf; } +// If v is longer than maxLen, return a safepoint to be cut which contains legal utf-8 characters, +// and make sure the returned size is less than or equal to maxLen +inline size_t utf8CutSize(folly::StringPiece v, size_t maxLen) { + DCHECK_GT(v.size(), maxLen); + size_t len = 0; + size_t curLen = 0; // current length of utf-8 character + while (len < maxLen) { + auto tmp = static_cast(v[len]); + if (tmp >= 0xFC) { + curLen = 6; + } else if (tmp >= 0xF8) { + curLen = 5; + } else if (tmp >= 0xF0) { + curLen = 4; + } else if (tmp >= 0xE0) { + curLen = 3; + } else if (tmp >= 0xC0) { + curLen = 2; + } else { + curLen = 1; + } + if (len + curLen <= maxLen) { + len += curLen; + } else { + break; + } + } + return len; +} + } // namespace nebula #endif // CODEC_COMMON_H_ diff --git a/src/codec/RowWriterV2.cpp b/src/codec/RowWriterV2.cpp index 9e0818521e5..1368661ae53 100644 --- a/src/codec/RowWriterV2.cpp +++ b/src/codec/RowWriterV2.cpp @@ -7,6 +7,7 @@ #include +#include "codec/Common.h" #include "common/time/TimeUtils.h" #include "common/time/WallClock.h" #include "common/utils/DefaultValueContext.h" @@ -679,7 +680,7 @@ WriteResult RowWriterV2::write(ssize_t index, folly::StringPiece v) noexcept { case PropertyType::FIXED_STRING: { // In-place string. If the pass-in string is longer than the pre-defined // fixed length, the string will be truncated to the fixed length - size_t len = v.size() > field->size() ? field->size() : v.size(); + size_t len = v.size() > field->size() ? utf8CutSize(v, field->size()) : v.size(); strncpy(&buf_[offset], v.data(), len); if (len < field->size()) { memset(&buf_[offset + len], 0, field->size() - len); diff --git a/tests/tck/features/fetch/FetchEmpty.feature b/tests/tck/features/fetch/FetchEmpty.feature index d024e0b9c9a..6827f3f4d2f 100644 --- a/tests/tck/features/fetch/FetchEmpty.feature +++ b/tests/tck/features/fetch/FetchEmpty.feature @@ -80,3 +80,95 @@ Feature: Fetch prop on empty tag/edge | e | | [:zero_prop_edge "1"->"2" @0{}] | And drop the used space + + Scenario: Tag Fixed String Property + When executing query: + """ + CREATE TAG tag_with_fixed_string(col1 fixed_string(5)); + """ + And wait 5 seconds + When executing query: + """ + INSERT VERTEX tag_with_fixed_string(col1) + VALUES + "1": ("😀😀"), + "2": ("😂😂"), + "3": ("įžŠįžŠįžŠ"), + "4": ("🐏🐏🐏"); + """ + Then the execution should be successful + When executing query: + """ + FETCH PROP on tag_with_fixed_string "1" yield tag_with_fixed_string.col1 as col1 + """ + Then the result should be, in any order: + | col1 | + | "😀" | + When executing query: + """ + FETCH PROP on tag_with_fixed_string "2" yield tag_with_fixed_string.col1 as col1 + """ + Then the result should be, in any order: + | col1 | + | "😂" | + When executing query: + """ + FETCH PROP on tag_with_fixed_string "3" yield tag_with_fixed_string.col1 as col1 + """ + Then the result should be, in any order: + | col1 | + | "įžŠ" | + When executing query: + """ + FETCH PROP on tag_with_fixed_string "4" yield tag_with_fixed_string.col1 as col1 + """ + Then the result should be, in any order: + | col1 | + | "🐏" | + And drop the used space + + Scenario: Edge Fixed String Property + When executing query: + """ + CREATE EDGE edge_with_fixed_string(col1 fixed_string(5)); + """ + And wait 5 seconds + When executing query: + """ + INSERT EDGE edge_with_fixed_string(col1) + VALUES + "1" -> "1": ("😀😀"), + "2" -> "2": ("😂😂"), + "3" -> "3": ("įžŠįžŠįžŠ"), + "4" -> "4": ("🐏🐏🐏"); + """ + Then the execution should be successful + When executing query: + """ + FETCH PROP on edge_with_fixed_string "1" -> "1" yield edge_with_fixed_string.col1 as col1 + """ + Then the result should be, in any order: + | col1 | + | "😀" | + When executing query: + """ + FETCH PROP on edge_with_fixed_string "2" -> "2" yield edge_with_fixed_string.col1 as col1 + """ + Then the result should be, in any order: + | col1 | + | "😂" | + When executing query: + """ + FETCH PROP on edge_with_fixed_string "3" -> "3" yield edge_with_fixed_string.col1 as col1 + """ + Then the result should be, in any order: + | col1 | + | "įžŠ" | + When executing query: + """ + FETCH PROP on edge_with_fixed_string "4" -> "4" yield edge_with_fixed_string.col1 as col1 + """ + Then the result should be, in any order: + | col1 | + | "🐏" | + And drop the used space diff --git a/tests/tck/features/index/Index.feature b/tests/tck/features/index/Index.feature index 7c3c21913cb..cc05630a200 100644 --- a/tests/tck/features/index/Index.feature +++ b/tests/tck/features/index/Index.feature @@ -1197,3 +1197,951 @@ Feature: IndexTest_Vid_String | "1" | "2" | | "2" | "1" | Then drop the used space + + Scenario: IndexTest TagStringIndexWithTruncate + Given an empty graph + And create a space with following options: + | partition_num | 1 | + | replica_factor | 1 | + | vid_type | FIXED_STRING(30) | + | charset | utf8 | + | collate | utf8_bin | + And having executed: + """ + CREATE TAG t1(col1 string); + """ + When executing query: + """ + CREATE TAG INDEX ti1 ON t1(col1(5)); + """ + Then the execution should be successful + And wait 5 seconds + When executing query: + """ + INSERT VERTEX t1(col1) + VALUES + "1": ("aaa"), + "2": ("aaaaa"), + "3": ("aaaaaaa"), + "4": ("abc"), + "5": ("abcde"), + "6": ("abcdefg"); + """ + Then the execution should be successful + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "aaa" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "1" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "aaaaa" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "2" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "aaaaaaa" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "3" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "abc" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "4" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "abcde" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "5" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "abcdefg" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "6" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 > "aaaaa" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "3" | + | "4" | + | "5" | + | "6" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 >= "aaaaa" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "2" | + | "3" | + | "4" | + | "5" | + | "6" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 < "abcde" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "1" | + | "2" | + | "3" | + | "4" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 <= "abcde" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "1" | + | "2" | + | "3" | + | "4" | + | "5" | + When executing query: + """ + LOOKUP ON t1 WHERE "aaaaa" < t1.col1 AND t1.col1 < "abcde" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "3" | + | "4" | + When executing query: + """ + LOOKUP ON t1 WHERE "aaaaa" <= t1.col1 AND t1.col1 < "abcde" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "2" | + | "3" | + | "4" | + When executing query: + """ + LOOKUP ON t1 WHERE "aaaaa" < t1.col1 AND t1.col1 <= "abcde" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "3" | + | "4" | + | "5" | + When executing query: + """ + LOOKUP ON t1 WHERE "aaaaa" <= t1.col1 AND t1.col1 <= "abcde" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "2" | + | "3" | + | "4" | + | "5" | + Then drop the used space + + Scenario: IndexTest EdgeStringIndexWithTruncate + Given an empty graph + And create a space with following options: + | partition_num | 1 | + | replica_factor | 1 | + | vid_type | FIXED_STRING(30) | + | charset | utf8 | + | collate | utf8_bin | + And having executed: + """ + CREATE EDGE e1(col1 string); + """ + When executing query: + """ + CREATE EDGE INDEX ei1 ON e1(col1(5)); + """ + Then the execution should be successful + And wait 5 seconds + When executing query: + """ + INSERT EDGE e1(col1) + VALUES + "1" -> "1": ("aaa"), + "2" -> "2": ("aaaaa"), + "3" -> "3": ("aaaaaaa"), + "4" -> "4": ("abc"), + "5" -> "5": ("abcde"), + "6" -> "6": ("abcdefg"); + """ + Then the execution should be successful + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "aaa" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "1" | "1" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "aaaaa" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "2" | "2" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "aaaaaaa" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "3" | "3" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "abc" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "4" | "4" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "abcde" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "5" | "5" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "abcdefg" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "6" | "6" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 > "aaaaa" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "3" | "3" | + | "4" | "4" | + | "5" | "5" | + | "6" | "6" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 >= "aaaaa" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "2" | "2" | + | "3" | "3" | + | "4" | "4" | + | "5" | "5" | + | "6" | "6" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 < "abcde" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "1" | "1" | + | "2" | "2" | + | "3" | "3" | + | "4" | "4" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 <= "abcde" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "1" | "1" | + | "2" | "2" | + | "3" | "3" | + | "4" | "4" | + | "5" | "5" | + When executing query: + """ + LOOKUP ON e1 WHERE "aaaaa" < e1.col1 AND e1.col1 < "abcde" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "3" | "3" | + | "4" | "4" | + When executing query: + """ + LOOKUP ON e1 WHERE "aaaaa" <= e1.col1 AND e1.col1 < "abcde" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "2" | "2" | + | "3" | "3" | + | "4" | "4" | + When executing query: + """ + LOOKUP ON e1 WHERE "aaaaa" < e1.col1 AND e1.col1 <= "abcde" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "3" | "3" | + | "4" | "4" | + | "5" | "5" | + When executing query: + """ + LOOKUP ON e1 WHERE "aaaaa" <= e1.col1 AND e1.col1 <= "abcde" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "2" | "2" | + | "3" | "3" | + | "4" | "4" | + | "5" | "5" | + Then drop the used space + + Scenario: IndexTest TagStringIndexWithTruncateUTF8 + Given an empty graph + And create a space with following options: + | partition_num | 1 | + | replica_factor | 1 | + | vid_type | FIXED_STRING(10) | + | charset | utf8 | + | collate | utf8_bin | + And having executed: + """ + CREATE TAG t1(col1 string); + """ + When executing query: + """ + CREATE TAG INDEX ti1 ON t1(col1(5)); + """ + Then the execution should be successful + And wait 5 seconds + # Unicode of įžŠ is\u7f8a, 🐏 is \ud83d\udc0f + When executing query: + """ + INSERT VERTEX t1(col1) + VALUES + "1": ("įžŠ"), + "2": ("įžŠįžŠ"), + "3": ("įžŠįžŠįžŠ"), + "4": ("įžŠįžŠđŸ"), + "5": ("įžŠđŸ"), + "6": ("įžŠđŸįžŠ"), + "7": ("įžŠđŸđŸ"), + "8": ("🐏"), + "9": ("🐏įžŠ"), + "10": ("🐏įžŠįžŠ"), + "11": ("🐏įžŠđŸ"), + "12": ("🐏🐏"), + "13": ("🐏🐏įžŠ"), + "14": ("🐏🐏🐏"); + """ + Then the execution should be successful + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "įžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "1" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "įžŠįžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "2" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "įžŠįžŠįžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "3" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "įžŠđŸ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "5" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "🐏" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "8" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "🐏🐏įžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "13" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 > "🐏įžŠįžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "11" | + | "12" | + | "13" | + | "14" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 >= "🐏įžŠįžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "10" | + | "11" | + | "12" | + | "13" | + | "14" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 < "įžŠđŸ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "1" | + | "2" | + | "3" | + | "4" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 <= "įžŠđŸ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "1" | + | "2" | + | "3" | + | "4" | + | "5" | + When executing query: + """ + LOOKUP ON t1 WHERE "įžŠđŸįžŠ" < t1.col1 and t1.col1 < "🐏įžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "7" | + | "8" | + When executing query: + """ + LOOKUP ON t1 WHERE "įžŠđŸįžŠ" <= t1.col1 and t1.col1 < "🐏įžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "6" | + | "7" | + | "8" | + When executing query: + """ + LOOKUP ON t1 WHERE "įžŠđŸįžŠ" < t1.col1 and t1.col1 <= "🐏įžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "7" | + | "8" | + | "9" | + When executing query: + """ + LOOKUP ON t1 WHERE "įžŠđŸįžŠ" <= t1.col1 and t1.col1 <= "🐏įžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "6" | + | "7" | + | "8" | + | "9" | + Then drop the used space + + Scenario: IndexTest TagStringIndexWithoutTruncateUTF8AndCertain + Given an empty graph + And create a space with following options: + | partition_num | 1 | + | replica_factor | 1 | + | vid_type | FIXED_STRING(10) | + | charset | utf8 | + | collate | utf8_bin | + And having executed: + """ + CREATE TAG t1(col1 string); + """ + When executing query: + """ + CREATE TAG INDEX ti1 ON t1(col1(16)); + """ + Then the execution should be successful + And wait 5 seconds + # Unicode of įžŠ is\u7f8a, 🐏 is \ud83d\udc0f + When executing query: + """ + INSERT VERTEX t1(col1) + VALUES + "1": ("įžŠ"), + "2": ("įžŠįžŠ"), + "3": ("įžŠįžŠįžŠ"), + "4": ("įžŠįžŠđŸ"), + "5": ("įžŠđŸ"), + "6": ("įžŠđŸįžŠ"), + "7": ("įžŠđŸđŸ"), + "8": ("🐏"), + "9": ("🐏įžŠ"), + "10": ("🐏įžŠįžŠ"), + "11": ("🐏įžŠđŸ"), + "12": ("🐏🐏"), + "13": ("🐏🐏įžŠ"), + "14": ("🐏🐏🐏"); + """ + Then the execution should be successful + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "įžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "1" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "įžŠįžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "2" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "įžŠįžŠįžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "3" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "įžŠđŸ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "5" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "🐏" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "8" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 == "🐏🐏įžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "13" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 > "🐏įžŠįžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "11" | + | "12" | + | "13" | + | "14" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 >= "🐏įžŠįžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "10" | + | "11" | + | "12" | + | "13" | + | "14" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 < "įžŠđŸ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "1" | + | "2" | + | "3" | + | "4" | + When executing query: + """ + LOOKUP ON t1 WHERE t1.col1 <= "įžŠđŸ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "1" | + | "2" | + | "3" | + | "4" | + | "5" | + When executing query: + """ + LOOKUP ON t1 WHERE "įžŠđŸįžŠ" < t1.col1 and t1.col1 < "🐏įžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "7" | + | "8" | + When executing query: + """ + LOOKUP ON t1 WHERE "įžŠđŸįžŠ" <= t1.col1 and t1.col1 < "🐏įžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "6" | + | "7" | + | "8" | + When executing query: + """ + LOOKUP ON t1 WHERE "įžŠđŸįžŠ" < t1.col1 and t1.col1 <= "🐏įžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "7" | + | "8" | + | "9" | + When executing query: + """ + LOOKUP ON t1 WHERE "įžŠđŸįžŠ" <= t1.col1 and t1.col1 <= "🐏įžŠ" YIELD id(vertex) as id + """ + Then the result should be, in any order: + | id | + | "6" | + | "7" | + | "8" | + | "9" | + Then drop the used space + + Scenario: IndexTest EdgeStringIndexWithTruncateUTF8 + Given an empty graph + And create a space with following options: + | partition_num | 1 | + | replica_factor | 1 | + | vid_type | FIXED_STRING(10) | + | charset | utf8 | + | collate | utf8_bin | + And having executed: + """ + CREATE EDGE e1(col1 string); + """ + When executing query: + """ + CREATE EDGE INDEX ei1 ON e1(col1(5)); + """ + Then the execution should be successful + And wait 5 seconds + # Unicode of įžŠ is\u7f8a, 🐏 is \ud83d\udc0f + When executing query: + """ + INSERT EDGE e1(col1) + VALUES + "1" -> "1" : ("įžŠ"), + "2" -> "2" : ("įžŠįžŠ"), + "3" -> "3" : ("įžŠįžŠįžŠ"), + "4" -> "4" : ("įžŠįžŠđŸ"), + "5" -> "5" : ("įžŠđŸ"), + "6" -> "6" : ("įžŠđŸįžŠ"), + "7" -> "7" : ("įžŠđŸđŸ"), + "8" -> "8" : ("🐏"), + "9" -> "9" : ("🐏įžŠ"), + "10" -> "10": ("🐏įžŠįžŠ"), + "11" -> "11": ("🐏įžŠđŸ"), + "12" -> "12": ("🐏🐏"), + "13" -> "13": ("🐏🐏įžŠ"), + "14" -> "14": ("🐏🐏🐏"); + """ + Then the execution should be successful + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "įžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "1" | "1" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "įžŠįžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "2" | "2" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "įžŠįžŠįžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "3" | "3" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "įžŠđŸ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "5" | "5" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "🐏" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "8" | "8" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "🐏🐏įžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "13" | "13" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 > "🐏įžŠįžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "11" | "11" | + | "12" | "12" | + | "13" | "13" | + | "14" | "14" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 >= "🐏įžŠįžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "10" | "10" | + | "11" | "11" | + | "12" | "12" | + | "13" | "13" | + | "14" | "14" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 < "įžŠđŸ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "1" | "1" | + | "2" | "2" | + | "3" | "3" | + | "4" | "4" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 <= "įžŠđŸ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "1" | "1" | + | "2" | "2" | + | "3" | "3" | + | "4" | "4" | + | "5" | "5" | + When executing query: + """ + LOOKUP ON e1 WHERE "įžŠđŸįžŠ" < e1.col1 and e1.col1 < "🐏įžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "7" | "7" | + | "8" | "8" | + When executing query: + """ + LOOKUP ON e1 WHERE "įžŠđŸįžŠ" <= e1.col1 and e1.col1 < "🐏įžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "6" | "6" | + | "7" | "7" | + | "8" | "8" | + When executing query: + """ + LOOKUP ON e1 WHERE "įžŠđŸįžŠ" < e1.col1 and e1.col1 <= "🐏įžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "7" | "7" | + | "8" | "8" | + | "9" | "9" | + When executing query: + """ + LOOKUP ON e1 WHERE "įžŠđŸįžŠ" <= e1.col1 and e1.col1 <= "🐏įžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "6" | "6" | + | "7" | "7" | + | "8" | "8" | + | "9" | "9" | + Then drop the used space + + Scenario: IndexTest EdgeStringIndexWithoutTruncateUTF8AndCertain + Given an empty graph + And create a space with following options: + | partition_num | 1 | + | replica_factor | 1 | + | vid_type | FIXED_STRING(10) | + | charset | utf8 | + | collate | utf8_bin | + And having executed: + """ + CREATE EDGE e1(col1 string); + """ + When executing query: + """ + CREATE EDGE INDEX ei1 ON e1(col1(16)); + """ + Then the execution should be successful + And wait 5 seconds + # Unicode of įžŠ is\u7f8a, 🐏 is \ud83d\udc0f + When executing query: + """ + INSERT EDGE e1(col1) + VALUES + "1" -> "1" : ("įžŠ"), + "2" -> "2" : ("įžŠįžŠ"), + "3" -> "3" : ("įžŠįžŠįžŠ"), + "4" -> "4" : ("įžŠįžŠđŸ"), + "5" -> "5" : ("įžŠđŸ"), + "6" -> "6" : ("įžŠđŸįžŠ"), + "7" -> "7" : ("įžŠđŸđŸ"), + "8" -> "8" : ("🐏"), + "9" -> "9" : ("🐏įžŠ"), + "10" -> "10": ("🐏įžŠįžŠ"), + "11" -> "11": ("🐏įžŠđŸ"), + "12" -> "12": ("🐏🐏"), + "13" -> "13": ("🐏🐏įžŠ"), + "14" -> "14": ("🐏🐏🐏"); + """ + Then the execution should be successful + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "įžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "1" | "1" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "įžŠįžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "2" | "2" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "įžŠįžŠįžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "3" | "3" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "įžŠđŸ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "5" | "5" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "🐏" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "8" | "8" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 == "🐏🐏įžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "13" | "13" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 > "🐏įžŠįžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "11" | "11" | + | "12" | "12" | + | "13" | "13" | + | "14" | "14" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 >= "🐏įžŠįžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "10" | "10" | + | "11" | "11" | + | "12" | "12" | + | "13" | "13" | + | "14" | "14" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 < "įžŠđŸ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "1" | "1" | + | "2" | "2" | + | "3" | "3" | + | "4" | "4" | + When executing query: + """ + LOOKUP ON e1 WHERE e1.col1 <= "įžŠđŸ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "1" | "1" | + | "2" | "2" | + | "3" | "3" | + | "4" | "4" | + | "5" | "5" | + When executing query: + """ + LOOKUP ON e1 WHERE "įžŠđŸįžŠ" < e1.col1 and e1.col1 < "🐏įžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "7" | "7" | + | "8" | "8" | + When executing query: + """ + LOOKUP ON e1 WHERE "įžŠđŸįžŠ" <= e1.col1 and e1.col1 < "🐏įžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "6" | "6" | + | "7" | "7" | + | "8" | "8" | + When executing query: + """ + LOOKUP ON e1 WHERE "įžŠđŸįžŠ" < e1.col1 and e1.col1 <= "🐏įžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "7" | "7" | + | "8" | "8" | + | "9" | "9" | + When executing query: + """ + LOOKUP ON e1 WHERE "įžŠđŸįžŠ" <= e1.col1 and e1.col1 <= "🐏įžŠ" YIELD src(edge) as src, dst(edge) as dst + """ + Then the result should be, in any order: + | src | dst | + | "6" | "6" | + | "7" | "7" | + | "8" | "8" | + | "9" | "9" | + Then drop the used space From 011f2fa2f32668be4ba9f63179212097e95658d8 Mon Sep 17 00:00:00 2001 From: liwenhui-soul <38217397+liwenhui-soul@users.noreply.github.com> Date: Thu, 22 Dec 2022 18:09:31 +0800 Subject: [PATCH 2/3] could not add a host that is not a storage (#5024) Co-authored-by: Harris.Chu <1726587+HarrisChu@users.noreply.github.com> --- src/clients/meta/MetaClient.cpp | 2 + src/interface/common.thrift | 1 + .../processors/zone/AddHostsProcessor.cpp | 16 ++++ src/meta/test/ProcessorTest.cpp | 76 +++++++++++++++++++ 4 files changed, 95 insertions(+) diff --git a/src/clients/meta/MetaClient.cpp b/src/clients/meta/MetaClient.cpp index 1cec0afe970..cfd41d8365a 100644 --- a/src/clients/meta/MetaClient.cpp +++ b/src/clients/meta/MetaClient.cpp @@ -965,6 +965,8 @@ Status MetaClient::handleResponse(const RESP& resp) { return Status::Error("There are still space on the host"); case nebula::cpp2::ErrorCode::E_RELATED_FULLTEXT_INDEX_EXISTS: return Status::Error("Related fulltext index exists, please drop it first"); + case nebula::cpp2::ErrorCode::E_HOST_CAN_NOT_BE_ADDED: + return Status::Error("Could not add a host, which is not a storage and not expired either"); default: return Status::Error("Unknown error!"); } diff --git a/src/interface/common.thrift b/src/interface/common.thrift index e70a69c9a6d..47524736c24 100644 --- a/src/interface/common.thrift +++ b/src/interface/common.thrift @@ -374,6 +374,7 @@ enum ErrorCode { E_NO_VALID_HOST = -2026, // Lack of valid hosts E_CORRUPTED_BALANCE_PLAN = -2027, // A data balancing plan that has been corrupted E_NO_INVALID_BALANCE_PLAN = -2028, // No invalid balance plan + E_HOST_CAN_NOT_BE_ADDED = -2029, // the host can not be added for it's not a storage host // Authentication Failure diff --git a/src/meta/processors/zone/AddHostsProcessor.cpp b/src/meta/processors/zone/AddHostsProcessor.cpp index 102af6593d5..789578eb78e 100644 --- a/src/meta/processors/zone/AddHostsProcessor.cpp +++ b/src/meta/processors/zone/AddHostsProcessor.cpp @@ -50,6 +50,22 @@ void AddHostsProcessor::process(const cpp2::AddHostsReq& req) { spaceIter->next(); } for (auto& host : hosts) { + auto hostKey = MetaKeyUtils::hostKey(host.host, host.port); + auto hostRes = doGet(hostKey); + // check if the host is storage, if it is not storage, it should be found in host table not + // expired, so it can't be added + if (nebula::ok(hostRes)) { + auto now = time::WallClock::fastNowInMilliSec(); + auto hostInfo = HostInfo::decode(nebula::value(hostRes)); + if (hostInfo.role_ != cpp2::HostRole::STORAGE && + now - hostInfo.lastHBTimeInMilliSec_ < + FLAGS_heartbeat_interval_secs * FLAGS_expired_time_factor * 1000) { + LOG(ERROR) << "The host " << host << " is not storage"; + code = nebula::cpp2::ErrorCode::E_HOST_CAN_NOT_BE_ADDED; + break; + } + } + // Ensure that the node is not registered. auto machineKey = MetaKeyUtils::machineKey(host.host, host.port); if (machineExist(machineKey) == nebula::cpp2::ErrorCode::SUCCEEDED) { diff --git a/src/meta/test/ProcessorTest.cpp b/src/meta/test/ProcessorTest.cpp index 1b510745f43..e69e585f199 100644 --- a/src/meta/test/ProcessorTest.cpp +++ b/src/meta/test/ProcessorTest.cpp @@ -3064,6 +3064,82 @@ TEST(ProcessorTest, HostsTest) { } } +TEST(ProcessorTest, AddHostsError) { + fs::TempDir rootPath("/tmp/AddHostsError.XXXXXX"); + auto kv = MockCluster::initMetaKV(rootPath.path()); + // Attempt to register heartbeat + const ClusterID kClusterId = 10; + { + cpp2::HBReq req; + req.host_ref() = HostAddr("127.0.0.1", 8987); + req.cluster_id_ref() = kClusterId; + req.role_ref() = cpp2::HostRole::GRAPH; + auto* processor = HBProcessor::instance(kv.get(), nullptr, kClusterId); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(nebula::cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } + { + cpp2::HBReq req; + req.host_ref() = HostAddr("127.0.0.1", 8988); + req.cluster_id_ref() = kClusterId; + req.role_ref() = cpp2::HostRole::STORAGE_LISTENER; + auto* processor = HBProcessor::instance(kv.get(), nullptr, kClusterId); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(nebula::cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } + { + cpp2::AddHostsReq req; + std::vector hosts; + hosts.emplace_back("127.0.0.1", 8987); + req.hosts_ref() = std::move(hosts); + auto* processor = AddHostsProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(nebula::cpp2::ErrorCode::E_HOST_CAN_NOT_BE_ADDED, resp.get_code()); + } + { + cpp2::AddHostsReq req; + std::vector hosts; + hosts.emplace_back("127.0.0.1", 8988); + req.hosts_ref() = std::move(hosts); + auto* processor = AddHostsProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(nebula::cpp2::ErrorCode::E_HOST_CAN_NOT_BE_ADDED, resp.get_code()); + } + auto oldVal = FLAGS_heartbeat_interval_secs; + FLAGS_heartbeat_interval_secs = 0; + { + cpp2::AddHostsReq req; + std::vector hosts; + hosts.emplace_back("127.0.0.1", 8987); + req.hosts_ref() = std::move(hosts); + auto* processor = AddHostsProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(nebula::cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } + { + cpp2::AddHostsReq req; + std::vector hosts; + hosts.emplace_back("127.0.0.1", 8988); + req.hosts_ref() = std::move(hosts); + auto* processor = AddHostsProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(nebula::cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } + FLAGS_heartbeat_interval_secs = oldVal; +} + TEST(ProcessorTest, AddHostsIntoNewZoneTest) { fs::TempDir rootPath("/tmp/AddHostsIntoZoneTest.XXXXXX"); auto kv = MockCluster::initMetaKV(rootPath.path()); From b5df05ca59c0327798e597a8e4f5a66e932c49f0 Mon Sep 17 00:00:00 2001 From: "hs.zhang" <22708345+cangfengzhs@users.noreply.github.com> Date: Fri, 23 Dec 2022 09:53:47 +0800 Subject: [PATCH 3/3] allow multi fulltext index on a tag/edge (#5038) * allow multi fulltext index on a tag/edge * address some comment Co-authored-by: Doodle <13706157+critical27@users.noreply.github.com> --- src/clients/meta/MetaClient.cpp | 27 ++++++++-- src/clients/meta/MetaClient.h | 8 ++- src/common/meta/SchemaManager.h | 2 +- src/common/meta/ServerBasedSchemaManager.cpp | 6 +-- src/common/meta/ServerBasedSchemaManager.h | 2 +- src/graph/validator/LookupValidator.cpp | 13 ++--- src/graph/validator/test/MockSchemaManager.h | 4 +- .../listener/elasticsearch/ESListener.cpp | 52 ++++++++++--------- src/meta/processors/BaseProcessor-inl.h | 20 ++++--- src/meta/processors/BaseProcessor.h | 9 ++-- .../processors/schema/AlterEdgeProcessor.cpp | 2 +- .../processors/schema/AlterTagProcessor.cpp | 2 +- .../processors/schema/DropEdgeProcessor.cpp | 16 +++--- .../processors/schema/DropTagProcessor.cpp | 16 +++--- src/mock/AdHocSchemaManager.h | 4 +- 15 files changed, 105 insertions(+), 78 deletions(-) diff --git a/src/clients/meta/MetaClient.cpp b/src/clients/meta/MetaClient.cpp index cfd41d8365a..ea2a2864def 100644 --- a/src/clients/meta/MetaClient.cpp +++ b/src/clients/meta/MetaClient.cpp @@ -3458,8 +3458,8 @@ StatusOr> MetaClient::getFTIndexB return indexes; } -StatusOr> MetaClient::getFTIndexBySpaceSchemaFromCache( - GraphSpaceID spaceId, int32_t schemaId) { +StatusOr> MetaClient::getFTIndexFromCache( + GraphSpaceID spaceId, int32_t schemaId, const std::string& field) { if (!ready_) { return Status::Error("Not ready!"); } @@ -3469,13 +3469,34 @@ StatusOr> MetaClient::getFTIndexBySpaceSch auto id = it.second.get_depend_schema().getType() == nebula::cpp2::SchemaID::Type::edge_type ? it.second.get_depend_schema().get_edge_type() : it.second.get_depend_schema().get_tag_id(); - if (it.second.get_space_id() == spaceId && id == schemaId) { + // There will only be one field. However, in order to minimize changes, the IDL was not modified + auto f = it.second.fields()->front(); + if (it.second.get_space_id() == spaceId && id == schemaId && f == field) { return std::make_pair(it.first, it.second); } } return Status::IndexNotFound(); } +StatusOr> MetaClient::getFTIndexFromCache( + GraphSpaceID spaceId, int32_t schemaId) { + if (!ready_) { + return Status::Error("Not ready!"); + } + folly::rcu_reader guard; + const auto& metadata = *metadata_.load(); + std::unordered_map ret; + for (auto& it : metadata.fulltextIndexMap_) { + auto id = it.second.get_depend_schema().getType() == nebula::cpp2::SchemaID::Type::edge_type + ? it.second.get_depend_schema().get_edge_type() + : it.second.get_depend_schema().get_tag_id(); + if (it.second.get_space_id() == spaceId && id == schemaId) { + ret[it.first] = it.second; + } + } + return ret; +} + StatusOr MetaClient::getFTIndexByNameFromCache(GraphSpaceID spaceId, const std::string& name) { if (!ready_) { diff --git a/src/clients/meta/MetaClient.h b/src/clients/meta/MetaClient.h index 013c9d919d1..0d52b3e8a4a 100644 --- a/src/clients/meta/MetaClient.h +++ b/src/clients/meta/MetaClient.h @@ -480,8 +480,12 @@ class MetaClient : public BaseMetaClient { StatusOr> getFTIndexBySpaceFromCache( GraphSpaceID spaceId); - StatusOr> getFTIndexBySpaceSchemaFromCache( - GraphSpaceID spaceId, int32_t schemaId); + StatusOr> getFTIndexFromCache(GraphSpaceID spaceId, + int32_t schemaId, + const std::string& field); + + StatusOr> getFTIndexFromCache(GraphSpaceID spaceId, + int32_t schemaId); StatusOr getFTIndexByNameFromCache(GraphSpaceID spaceId, const std::string& name); diff --git a/src/common/meta/SchemaManager.h b/src/common/meta/SchemaManager.h index cdf7335633f..5e742ea0e20 100644 --- a/src/common/meta/SchemaManager.h +++ b/src/common/meta/SchemaManager.h @@ -91,7 +91,7 @@ class SchemaManager { StatusOr> getSchemaIDByName(GraphSpaceID space, folly::StringPiece schemaName); - virtual StatusOr> getFTIndex( + virtual StatusOr> getFTIndex( GraphSpaceID spaceId, int32_t schemaId) = 0; protected: diff --git a/src/common/meta/ServerBasedSchemaManager.cpp b/src/common/meta/ServerBasedSchemaManager.cpp index e86b8651c28..29b1b0f3f80 100644 --- a/src/common/meta/ServerBasedSchemaManager.cpp +++ b/src/common/meta/ServerBasedSchemaManager.cpp @@ -159,9 +159,9 @@ ServerBasedSchemaManager::getServiceClients(meta::cpp2::ExternalServiceType type return std::move(ret).value(); } -StatusOr> ServerBasedSchemaManager::getFTIndex( - GraphSpaceID spaceId, int32_t schemaId) { - auto ret = metaClient_->getFTIndexBySpaceSchemaFromCache(spaceId, schemaId); +StatusOr> +ServerBasedSchemaManager::getFTIndex(GraphSpaceID spaceId, int32_t schemaId) { + auto ret = metaClient_->getFTIndexFromCache(spaceId, schemaId); if (!ret.ok()) { return ret.status(); } diff --git a/src/common/meta/ServerBasedSchemaManager.h b/src/common/meta/ServerBasedSchemaManager.h index 75002effba1..2c5bcbee989 100644 --- a/src/common/meta/ServerBasedSchemaManager.h +++ b/src/common/meta/ServerBasedSchemaManager.h @@ -71,7 +71,7 @@ class ServerBasedSchemaManager : public SchemaManager { StatusOr> getServiceClients( cpp2::ExternalServiceType type) override; - StatusOr> getFTIndex( + StatusOr> getFTIndex( GraphSpaceID spaceId, int32_t schemaId) override; void init(MetaClient *client); diff --git a/src/graph/validator/LookupValidator.cpp b/src/graph/validator/LookupValidator.cpp index ea88bb0c2e3..af93c10a52d 100644 --- a/src/graph/validator/LookupValidator.cpp +++ b/src/graph/validator/LookupValidator.cpp @@ -501,19 +501,12 @@ StatusOr LookupValidator::checkConstExpr(Expression* expr, // Check does test search contains properties search in test search expression StatusOr LookupValidator::checkTSExpr(Expression* expr) { + auto tsExpr = static_cast(expr); + auto prop = tsExpr->arg()->prop(); auto metaClient = qctx_->getMetaClient(); - auto tsi = metaClient->getFTIndexBySpaceSchemaFromCache(spaceId(), schemaId()); + auto tsi = metaClient->getFTIndexFromCache(spaceId(), schemaId(), prop); NG_RETURN_IF_ERROR(tsi); auto tsName = tsi.value().first; - - auto ftFields = tsi.value().second.get_fields(); - auto tsExpr = static_cast(expr); - auto prop = tsExpr->arg()->prop(); - - auto iter = std::find(ftFields.begin(), ftFields.end(), prop); - if (iter == ftFields.end()) { - return Status::SemanticError("Column %s not found in %s", prop.c_str(), tsName.c_str()); - } return tsName; } diff --git a/src/graph/validator/test/MockSchemaManager.h b/src/graph/validator/test/MockSchemaManager.h index be2e904d80f..0152ba301dc 100644 --- a/src/graph/validator/test/MockSchemaManager.h +++ b/src/graph/validator/test/MockSchemaManager.h @@ -115,8 +115,8 @@ class MockSchemaManager final : public nebula::meta::SchemaManager { LOG(FATAL) << "Unimplemented."; } - StatusOr> getFTIndex(GraphSpaceID, - int32_t) override { + StatusOr> getFTIndex( + GraphSpaceID, int32_t) override { LOG(FATAL) << "Unimplemented"; return Status::Error("Unimplemented"); } diff --git a/src/kvstore/listener/elasticsearch/ESListener.cpp b/src/kvstore/listener/elasticsearch/ESListener.cpp index bf5d6d83eba..030bad6b93f 100644 --- a/src/kvstore/listener/elasticsearch/ESListener.cpp +++ b/src/kvstore/listener/elasticsearch/ESListener.cpp @@ -90,18 +90,20 @@ void ESListener::pickTagAndEdgeData(BatchLogType type, LOG(ERROR) << "get tag reader failed, tagID " << tagId; return; } - if (ftIndex.second.get_fields().size() > 1) { - LOG(ERROR) << "Only one field will create fulltext index"; - } - auto field = ftIndex.second.get_fields().front(); - auto v = reader->getValueByName(field); - if (v.type() != Value::Type::STRING) { - LOG(ERROR) << "Can't create fulltext index on type " << v.type(); + for (auto& index : ftIndex) { + if (index.second.get_fields().size() > 1) { + LOG(ERROR) << "Only one field will create fulltext index"; + } + auto field = index.second.get_fields().front(); + auto v = reader->getValueByName(field); + if (v.type() != Value::Type::STRING) { + LOG(ERROR) << "Can't create fulltext index on type " << v.type(); + } + std::string indexName = index.first; + std::string vid = NebulaKeyUtils::getVertexId(vIdLen_, key).toString(); + std::string text = std::move(v).getStr(); + callback(type, indexName, vid, "", "", 0, text); } - std::string indexName = ftIndex.first; - std::string vid = NebulaKeyUtils::getVertexId(vIdLen_, key).toString(); - std::string text = std::move(v).getStr(); - callback(type, indexName, vid, "", "", 0, text); } else if (nebula::NebulaKeyUtils::isEdge(vIdLen_, key)) { auto edgeType = NebulaKeyUtils::getEdgeType(vIdLen_, key); auto ftIndexRes = schemaMan_->getFTIndex(spaceId_, edgeType); @@ -114,20 +116,22 @@ void ESListener::pickTagAndEdgeData(BatchLogType type, LOG(ERROR) << "get edge reader failed, schema ID " << edgeType; return; } - if (ftIndex.second.get_fields().size() > 1) { - LOG(ERROR) << "Only one field will create fulltext index"; - } - auto field = ftIndex.second.get_fields().front(); - auto v = reader->getValueByName(field); - if (v.type() != Value::Type::STRING) { - LOG(ERROR) << "Can't create fulltext index on type " << v.type(); + for (auto& index : ftIndex) { + if (index.second.get_fields().size() > 1) { + LOG(ERROR) << "Only one field will create fulltext index"; + } + auto field = index.second.get_fields().front(); + auto v = reader->getValueByName(field); + if (v.type() != Value::Type::STRING) { + LOG(ERROR) << "Can't create fulltext index on type " << v.type(); + } + std::string indexName = index.first; + std::string src = NebulaKeyUtils::getSrcId(vIdLen_, key).toString(); + std::string dst = NebulaKeyUtils::getDstId(vIdLen_, key).toString(); + int64_t rank = NebulaKeyUtils::getRank(vIdLen_, key); + std::string text = std::move(v).getStr(); + callback(type, indexName, "", src, dst, rank, text); } - std::string indexName = ftIndex.first; - std::string src = NebulaKeyUtils::getSrcId(vIdLen_, key).toString(); - std::string dst = NebulaKeyUtils::getDstId(vIdLen_, key).toString(); - int64_t rank = NebulaKeyUtils::getRank(vIdLen_, key); - std::string text = std::move(v).getStr(); - callback(type, indexName, "", src, dst, rank, text); } } diff --git a/src/meta/processors/BaseProcessor-inl.h b/src/meta/processors/BaseProcessor-inl.h index 0a1f3fde65f..396b733da3b 100644 --- a/src/meta/processors/BaseProcessor-inl.h +++ b/src/meta/processors/BaseProcessor-inl.h @@ -411,8 +411,8 @@ ErrorOr> BaseProcessor -ErrorOr BaseProcessor::getFTIndex( - GraphSpaceID spaceId, int32_t tagOrEdge) { +ErrorOr> +BaseProcessor::getFTIndex(GraphSpaceID spaceId, int32_t tagOrEdge) { const auto& indexPrefix = MetaKeyUtils::fulltextIndexPrefix(); auto iterRet = doPrefix(indexPrefix); if (!nebula::ok(iterRet)) { @@ -422,18 +422,18 @@ ErrorOr BaseProcessor::getFTIndex( return retCode; } auto indexIter = nebula::value(iterRet).get(); - + std::unordered_map ret; while (indexIter->valid()) { auto index = MetaKeyUtils::parsefulltextIndex(indexIter->val()); auto id = index.get_depend_schema().getType() == nebula::cpp2::SchemaID::Type::edge_type ? index.get_depend_schema().get_edge_type() : index.get_depend_schema().get_tag_id(); if (spaceId == index.get_space_id() && tagOrEdge == id) { - return index; + ret[indexIter->key().toString()] = index; } indexIter->next(); } - return nebula::cpp2::ErrorCode::E_INDEX_NOT_FOUND; + return ret; } template @@ -464,14 +464,18 @@ nebula::cpp2::ErrorCode BaseProcessor::indexCheck( template nebula::cpp2::ErrorCode BaseProcessor::ftIndexCheck( - const std::vector& cols, const std::vector& alterItems) { + const std::unordered_map& ftIndices, + const std::vector& alterItems) { + std::set cols; + for (auto& [indexName, index] : ftIndices) { + cols.insert(index.fields_ref()->front()); + } for (const auto& item : alterItems) { if (*item.op_ref() == nebula::meta::cpp2::AlterSchemaOp::CHANGE || *item.op_ref() == nebula::meta::cpp2::AlterSchemaOp::DROP) { const auto& itemCols = item.get_schema().get_columns(); for (const auto& iCol : itemCols) { - auto it = - std::find_if(cols.begin(), cols.end(), [&](const auto& c) { return c == iCol.name; }); + auto it = cols.find(iCol.name); if (it != cols.end()) { LOG(INFO) << "fulltext index conflict"; return nebula::cpp2::ErrorCode::E_RELATED_FULLTEXT_INDEX_EXISTS; diff --git a/src/meta/processors/BaseProcessor.h b/src/meta/processors/BaseProcessor.h index 127520f4fe0..9f56934f955 100644 --- a/src/meta/processors/BaseProcessor.h +++ b/src/meta/processors/BaseProcessor.h @@ -352,8 +352,9 @@ class BaseProcessor { * @param alterItems * @return ErrorCode::E_RELATED_FULLTEXT_INDEX_EXISTS if contains */ - nebula::cpp2::ErrorCode ftIndexCheck(const std::vector& cols, - const std::vector& alterItems); + nebula::cpp2::ErrorCode ftIndexCheck( + const std::unordered_map& ftIndices, + const std::vector& alterItems); /** * @brief List all tag/edge index for given space and tag/edge id. @@ -374,8 +375,8 @@ class BaseProcessor { * @param tagOrEdge * @return ErrorOr */ - ErrorOr getFTIndex(GraphSpaceID spaceId, - int32_t tagOrEdge); + ErrorOr> getFTIndex( + GraphSpaceID spaceId, int32_t tagOrEdge); /** * @brief Check if index on given fields alredy exist. diff --git a/src/meta/processors/schema/AlterEdgeProcessor.cpp b/src/meta/processors/schema/AlterEdgeProcessor.cpp index 6e711302974..adfa26fa7be 100644 --- a/src/meta/processors/schema/AlterEdgeProcessor.cpp +++ b/src/meta/processors/schema/AlterEdgeProcessor.cpp @@ -103,7 +103,7 @@ void AlterEdgeProcessor::process(const cpp2::AlterEdgeReq& req) { auto ftIdxRet = getFTIndex(spaceId, edgeType); if (nebula::ok(ftIdxRet)) { auto fti = std::move(nebula::value(ftIdxRet)); - auto ftStatus = ftIndexCheck(fti.get_fields(), edgeItems); + auto ftStatus = ftIndexCheck(fti, edgeItems); if (ftStatus != nebula::cpp2::ErrorCode::SUCCEEDED) { handleErrorCode(ftStatus); onFinished(); diff --git a/src/meta/processors/schema/AlterTagProcessor.cpp b/src/meta/processors/schema/AlterTagProcessor.cpp index 92bb87ae0fe..2252b1955c3 100644 --- a/src/meta/processors/schema/AlterTagProcessor.cpp +++ b/src/meta/processors/schema/AlterTagProcessor.cpp @@ -100,7 +100,7 @@ void AlterTagProcessor::process(const cpp2::AlterTagReq& req) { auto ftIdxRet = getFTIndex(spaceId, tagId); if (nebula::ok(ftIdxRet)) { auto fti = std::move(nebula::value(ftIdxRet)); - auto ftStatus = ftIndexCheck(fti.get_fields(), tagItems); + auto ftStatus = ftIndexCheck(fti, tagItems); if (ftStatus != nebula::cpp2::ErrorCode::SUCCEEDED) { handleErrorCode(ftStatus); onFinished(); diff --git a/src/meta/processors/schema/DropEdgeProcessor.cpp b/src/meta/processors/schema/DropEdgeProcessor.cpp index 9255223ec59..0ef3c832a32 100644 --- a/src/meta/processors/schema/DropEdgeProcessor.cpp +++ b/src/meta/processors/schema/DropEdgeProcessor.cpp @@ -57,14 +57,14 @@ void DropEdgeProcessor::process(const cpp2::DropEdgeReq& req) { auto ftIdxRet = getFTIndex(spaceId, edgeType); if (nebula::ok(ftIdxRet)) { - LOG(INFO) << "Drop edge error, fulltext index conflict, " - << "please delete fulltext index first."; - handleErrorCode(nebula::cpp2::ErrorCode::E_RELATED_INDEX_EXISTS); - onFinished(); - return; - } - - if (nebula::error(ftIdxRet) != nebula::cpp2::ErrorCode::E_INDEX_NOT_FOUND) { + if (!nebula::value(ftIdxRet).empty()) { + LOG(INFO) << "Drop edge error, fulltext index conflict, " + << "please delete fulltext index first."; + handleErrorCode(nebula::cpp2::ErrorCode::E_RELATED_INDEX_EXISTS); + onFinished(); + return; + } + } else { handleErrorCode(nebula::error(ftIdxRet)); onFinished(); return; diff --git a/src/meta/processors/schema/DropTagProcessor.cpp b/src/meta/processors/schema/DropTagProcessor.cpp index 2f3c5c27cb7..8a18dbb901e 100644 --- a/src/meta/processors/schema/DropTagProcessor.cpp +++ b/src/meta/processors/schema/DropTagProcessor.cpp @@ -57,14 +57,14 @@ void DropTagProcessor::process(const cpp2::DropTagReq& req) { auto ftIdxRet = getFTIndex(spaceId, tagId); if (nebula::ok(ftIdxRet)) { - LOG(INFO) << "Drop tag error, fulltext index conflict, " - << "please delete fulltext index first."; - handleErrorCode(nebula::cpp2::ErrorCode::E_RELATED_INDEX_EXISTS); - onFinished(); - return; - } - - if (nebula::error(ftIdxRet) != nebula::cpp2::ErrorCode::E_INDEX_NOT_FOUND) { + if (!nebula::value(ftIdxRet).empty()) { + LOG(INFO) << "Drop tag error, fulltext index conflict, " + << "please delete fulltext index first."; + handleErrorCode(nebula::cpp2::ErrorCode::E_RELATED_INDEX_EXISTS); + onFinished(); + return; + } + } else { handleErrorCode(nebula::error(ftIdxRet)); onFinished(); return; diff --git a/src/mock/AdHocSchemaManager.h b/src/mock/AdHocSchemaManager.h index 07c9989e606..5a93f8e9ca8 100644 --- a/src/mock/AdHocSchemaManager.h +++ b/src/mock/AdHocSchemaManager.h @@ -103,8 +103,8 @@ class AdHocSchemaManager final : public nebula::meta::SchemaManager { void addServiceClient(const nebula::meta::cpp2::ServiceClient& client); - StatusOr> getFTIndex(GraphSpaceID, - int32_t) override { + StatusOr> getFTIndex( + GraphSpaceID, int32_t) override { LOG(FATAL) << "Unimplemented"; return Status::Error("Unimplemented"); }