diff options
| author | vityaman <[email protected]> | 2025-05-20 13:09:37 +0300 |
|---|---|---|
| committer | robot-piglet <[email protected]> | 2025-05-20 13:23:38 +0300 |
| commit | ae913bccb4fd8a8ba5f536c8afe622e2ad229fbc (patch) | |
| tree | 4145cf25a40302e0888e7174d288f4ed00919738 | |
| parent | 16630d4dfad772e0108e694f76f922e486cd6439 (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
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 {}; } |
