summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvityaman <[email protected]>2025-05-20 13:09:37 +0300
committerrobot-piglet <[email protected]>2025-05-20 13:23:38 +0300
commitae913bccb4fd8a8ba5f536c8afe622e2ad229fbc (patch)
tree4145cf25a40302e0888e7174d288f4ed00919738
parent16630d4dfad772e0108e694f76f922e486cd6439 (diff)
YQL-19747: Recover from unclosed ID_QUOTED
The simplest solution actually is an SQL injection. --- - Related to `YQL-19747` - Related to https://github.com/ydb-platform/ydb/issues/9056 - Related to https://github.com/ydb-platform/ydb/pull/18146 - Related to https://github.com/vityaman/ydb/issues/46 --- Pull Request resolved: https://github.com/ytsaurus/ytsaurus/pull/1283 commit_hash:7043e7cdd1e0d378926783d31cf5f8e055393aaa
-rw-r--r--yql/essentials/sql/v1/complete/core/input.h5
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete_ut.cpp27
-rw-r--r--yql/essentials/sql/v1/complete/syntax/cursor_token_context.cpp9
-rw-r--r--yql/essentials/sql/v1/complete/syntax/cursor_token_context.h2
-rw-r--r--yql/essentials/sql/v1/complete/syntax/local.cpp11
5 files changed, 47 insertions, 7 deletions
diff --git a/yql/essentials/sql/v1/complete/core/input.h b/yql/essentials/sql/v1/complete/core/input.h
index d852736cb44..917ae5388c1 100644
--- a/yql/essentials/sql/v1/complete/core/input.h
+++ b/yql/essentials/sql/v1/complete/core/input.h
@@ -9,6 +9,11 @@ namespace NSQLComplete {
size_t CursorPosition = Text.length();
};
+ struct TMaterializedInput {
+ TString Text;
+ size_t CursorPosition = Text.length();
+ };
+
TCompletionInput SharpedInput(TString& text Y_LIFETIME_BOUND);
} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/sql_complete_ut.cpp b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
index 00c5f3427f6..d3a39d1c503 100644
--- a/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
+++ b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
@@ -508,7 +508,12 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
UNIT_ASSERT_VALUES_EQUAL(actual.CompletedToken.Content, "pr");
}
{
- TVector<TCandidate> expected = {};
+ TVector<TCandidate> expected = {
+ {FolderName, ".sys/"},
+ {FolderName, "local/"},
+ {FolderName, "prod/"},
+ {FolderName, "test/"},
+ };
UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "SELECT * FROM `#"), expected);
}
{
@@ -564,6 +569,26 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
}
}
+ Y_UNIT_TEST(SelectFromUnclosedIdQuoted) {
+ auto engine = MakeSqlCompletionEngineUT();
+ {
+ TVector<TCandidate> expected = {
+ {FolderName, ".sys/"},
+ {FolderName, "local/"},
+ {FolderName, "prod/"},
+ {FolderName, "test/"},
+ };
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "SELECT * FROM `#"), expected);
+ }
+ {
+ TVector<TCandidate> expected = {
+ {TableName, "meta"},
+ {FolderName, "service/"},
+ };
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "SELECT * FROM `test/"), expected);
+ }
+ }
+
Y_UNIT_TEST(SelectFromCluster) {
auto engine = MakeSqlCompletionEngineUT();
{
diff --git a/yql/essentials/sql/v1/complete/syntax/cursor_token_context.cpp b/yql/essentials/sql/v1/complete/syntax/cursor_token_context.cpp
index 33aef36847a..445f863a255 100644
--- a/yql/essentials/sql/v1/complete/syntax/cursor_token_context.cpp
+++ b/yql/essentials/sql/v1/complete/syntax/cursor_token_context.cpp
@@ -112,13 +112,13 @@ namespace NSQLComplete {
bool GetStatement(
ILexer::TPtr& lexer,
- TCompletionInput input,
+ const TMaterializedInput& input,
TCompletionInput& output,
size_t& output_position) {
TVector<TString> statements;
NYql::TIssues issues;
if (!NSQLTranslationV1::SplitQueryToStatements(
- TString(input.Text) + ";", lexer,
+ input.Text, lexer,
statements, issues, /* file = */ "",
/* areBlankSkipped = */ false)) {
return false;
@@ -129,7 +129,7 @@ namespace NSQLComplete {
for (const auto& statement : statements) {
if (input.CursorPosition < cursor + statement.size()) {
output = {
- .Text = input.Text.SubStr(cursor, statement.size()),
+ .Text = TStringBuf(input.Text).SubStr(cursor, statement.size()),
.CursorPosition = input.CursorPosition - cursor,
};
return true;
@@ -137,7 +137,8 @@ namespace NSQLComplete {
cursor += statement.size();
}
- output = input;
+ output.Text = TStringBuf(input.Text);
+ output.CursorPosition = input.CursorPosition;
return true;
}
diff --git a/yql/essentials/sql/v1/complete/syntax/cursor_token_context.h b/yql/essentials/sql/v1/complete/syntax/cursor_token_context.h
index 35d22231e35..9db544b5234 100644
--- a/yql/essentials/sql/v1/complete/syntax/cursor_token_context.h
+++ b/yql/essentials/sql/v1/complete/syntax/cursor_token_context.h
@@ -38,7 +38,7 @@ namespace NSQLComplete {
bool GetStatement(
ILexer::TPtr& lexer,
- TCompletionInput input,
+ const TMaterializedInput& input,
TCompletionInput& output,
size_t& output_position);
diff --git a/yql/essentials/sql/v1/complete/syntax/local.cpp b/yql/essentials/sql/v1/complete/syntax/local.cpp
index d461cd41523..a9803feae85 100644
--- a/yql/essentials/sql/v1/complete/syntax/local.cpp
+++ b/yql/essentials/sql/v1/complete/syntax/local.cpp
@@ -57,9 +57,18 @@ namespace NSQLComplete {
}
TLocalSyntaxContext Analyze(TCompletionInput input) override {
+ TMaterializedInput materialized = {
+ .Text = TString(input.Text),
+ .CursorPosition = input.CursorPosition,
+ };
+
+ // - ";" is for a correct stetement split
+ // - "-- `" is for a ilformed ID_QUOTED recovery
+ materialized.Text += "; -- `";
+
TCompletionInput statement;
size_t statement_position;
- if (!GetStatement(Lexer_, input, statement, statement_position)) {
+ if (!GetStatement(Lexer_, materialized, statement, statement_position)) {
return {};
}