summaryrefslogtreecommitdiffstats
path: root/yql/essentials/sql
diff options
context:
space:
mode:
authorvitya-smirnov <[email protected]>2025-06-19 14:48:15 +0300
committervitya-smirnov <[email protected]>2025-06-19 15:26:22 +0300
commit460876175ab7e105d4b4513a308493fc73eeedb6 (patch)
treec0fcf5af3c56825d868f62e72da5ab1f30194324 /yql/essentials/sql
parent8d42f4f2ab6e419a05a55ba8d71dda34c446c22b (diff)
YQL-19747: Auto-close backtick on not folder
When table `folder/table` exists. On prefix ``` SELECT * FROM `folder/ ``` users want to accept ``` table` ``` rather than just `table`. This patch is about it. commit_hash:12d36cbf037db91f49136ab8e013b160a28b5b1b
Diffstat (limited to 'yql/essentials/sql')
-rw-r--r--yql/essentials/sql/v1/complete/analysis/local/cursor_token_context.cpp4
-rw-r--r--yql/essentials/sql/v1/complete/analysis/local/cursor_token_context.h1
-rw-r--r--yql/essentials/sql/v1/complete/analysis/local/local.cpp16
-rw-r--r--yql/essentials/sql/v1/complete/analysis/local/local.h8
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete.cpp7
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete_ut.cpp22
6 files changed, 39 insertions, 19 deletions
diff --git a/yql/essentials/sql/v1/complete/analysis/local/cursor_token_context.cpp b/yql/essentials/sql/v1/complete/analysis/local/cursor_token_context.cpp
index 445f863a255..0c091b6c4ee 100644
--- a/yql/essentials/sql/v1/complete/analysis/local/cursor_token_context.cpp
+++ b/yql/essentials/sql/v1/complete/analysis/local/cursor_token_context.cpp
@@ -67,6 +67,10 @@ namespace NSQLComplete {
Base->Name == "REAL";
}
+ size_t TRichParsedToken::End() const {
+ return Position + Base->Content.size();
+ }
+
TRichParsedToken TokenAt(const TCursorTokenContext& context, size_t index) {
return {
.Base = &context.Tokens.at(index),
diff --git a/yql/essentials/sql/v1/complete/analysis/local/cursor_token_context.h b/yql/essentials/sql/v1/complete/analysis/local/cursor_token_context.h
index 9db544b5234..987bfa90914 100644
--- a/yql/essentials/sql/v1/complete/analysis/local/cursor_token_context.h
+++ b/yql/essentials/sql/v1/complete/analysis/local/cursor_token_context.h
@@ -25,6 +25,7 @@ namespace NSQLComplete {
size_t Position = 0;
bool IsLiteral() const;
+ size_t End() const;
};
struct TCursorTokenContext {
diff --git a/yql/essentials/sql/v1/complete/analysis/local/local.cpp b/yql/essentials/sql/v1/complete/analysis/local/local.cpp
index 014ac61e803..3841f7276f8 100644
--- a/yql/essentials/sql/v1/complete/analysis/local/local.cpp
+++ b/yql/essentials/sql/v1/complete/analysis/local/local.cpp
@@ -86,6 +86,7 @@ namespace NSQLComplete {
TLocalSyntaxContext result;
+ result.IsQuoted = Quotation(input, context);
result.EditRange = EditRange(context);
result.EditRange.Begin += statement_position;
@@ -286,11 +287,6 @@ namespace NSQLComplete {
object.Path = *path;
}
- if (auto enclosing = context.Enclosing();
- enclosing.Defined() && enclosing->Base->Name == "ID_QUOTED") {
- object.IsQuoted = true;
- }
-
return object;
}
@@ -341,6 +337,16 @@ namespace NSQLComplete {
return AnyOf(candidates.Rules, RuleAdapted(IsLikelyBindingStack));
}
+ TLocalSyntaxContext::TQuotation Quotation(TCompletionInput input, const TCursorTokenContext& context) const {
+ TLocalSyntaxContext::TQuotation isQuoted;
+ if (auto enclosing = context.Enclosing();
+ enclosing.Defined() && enclosing->Base->Name == "ID_QUOTED") {
+ isQuoted.AtLhs = true;
+ isQuoted.AtRhs = enclosing->End() <= input.Text.size();
+ }
+ return isQuoted;
+ }
+
TEditRange EditRange(const TCursorTokenContext& context) const {
if (auto enclosing = context.Enclosing()) {
return EditRange(*enclosing, context.Cursor);
diff --git a/yql/essentials/sql/v1/complete/analysis/local/local.h b/yql/essentials/sql/v1/complete/analysis/local/local.h
index c5d73d52018..86de7d1632a 100644
--- a/yql/essentials/sql/v1/complete/analysis/local/local.h
+++ b/yql/essentials/sql/v1/complete/analysis/local/local.h
@@ -41,7 +41,6 @@ namespace NSQLComplete {
TString Cluster;
TString Path;
THashSet<EObjectKind> Kinds;
- bool IsQuoted = false;
bool HasCluster() const {
return !Cluster.empty();
@@ -52,6 +51,11 @@ namespace NSQLComplete {
TString Table;
};
+ struct TQuotation {
+ bool AtLhs = false;
+ bool AtRhs = false;
+ };
+
TKeywords Keywords;
TMaybe<TPragma> Pragma;
bool Type = false;
@@ -61,6 +65,8 @@ namespace NSQLComplete {
TMaybe<TCluster> Cluster;
TMaybe<TColumn> Column;
bool Binding = false;
+
+ TQuotation IsQuoted;
TEditRange EditRange;
};
diff --git a/yql/essentials/sql/v1/complete/sql_complete.cpp b/yql/essentials/sql/v1/complete/sql_complete.cpp
index 36aecf06b20..187756c6975 100644
--- a/yql/essentials/sql/v1/complete/sql_complete.cpp
+++ b/yql/essentials/sql/v1/complete/sql_complete.cpp
@@ -241,16 +241,19 @@ namespace NSQLComplete {
if constexpr (std::is_base_of_v<TFolderName, T>) {
name.Indentifier.append('/');
- if (!context.Object || !context.Object->IsQuoted) {
+ if (!context.IsQuoted.AtLhs) {
name.Indentifier.prepend('`');
}
return {ECandidateKind::FolderName, std::move(name.Indentifier)};
}
if constexpr (std::is_base_of_v<TTableName, T>) {
- if (!context.Object || !context.Object->IsQuoted) {
+ if (!context.IsQuoted.AtLhs) {
name.Indentifier.prepend('`');
}
+ if (!context.IsQuoted.AtRhs) {
+ name.Indentifier.append('`');
+ }
return {ECandidateKind::TableName, std::move(name.Indentifier)};
}
diff --git a/yql/essentials/sql/v1/complete/sql_complete_ut.cpp b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
index 1e3b2debdb1..90c2d8faeba 100644
--- a/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
+++ b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
@@ -213,7 +213,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
auto engine = MakeSqlCompletionEngineUT();
{
TVector<TCandidate> expected = {
- {TableName, "`maxim"},
+ {TableName, "`maxim`"},
{ClusterName, "example"},
{ClusterName, "saurus"},
{Keyword, "ANY"},
@@ -634,7 +634,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
}
{
TVector<TCandidate> expected = {
- {TableName, "meta"},
+ {TableName, "meta`"},
{FolderName, "service/"},
};
UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "SELECT * FROM `test/"), expected);
@@ -658,13 +658,13 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
}
{
TVector<TCandidate> expected = {
- {TableName, "`maxim"},
+ {TableName, "`maxim`"},
};
UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "SELECT * FROM yt:saurus."), expected);
}
{
TVector<TCandidate> expected = {
- {TableName, "`people"},
+ {TableName, "`people`"},
};
UNIT_ASSERT_VALUES_EQUAL(CompleteTop(1, engine, "SELECT * FROM example."), expected);
}
@@ -680,7 +680,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
auto engine = MakeSqlCompletionEngineUT();
{
TVector<TCandidate> expected = {
- {TableName, "`maxim"},
+ {TableName, "`maxim`"},
{ClusterName, "example"},
{ClusterName, "saurus"},
{Keyword, "ANY"},
@@ -689,14 +689,14 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
}
{
TVector<TCandidate> expected = {
- {TableName, "`people"},
+ {TableName, "`people`"},
{FolderName, "`yql/"},
};
UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "USE yt:saurus; SELECT * FROM example."), expected);
}
{
TVector<TCandidate> expected = {
- {TableName, "`maxim"},
+ {TableName, "`maxim`"},
{ClusterName, "example"},
{ClusterName, "saurus"},
{Keyword, "ANY"},
@@ -706,7 +706,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
{
TVector<TCandidate> expected = {
{BindingName, "$hello"},
- {TableName, "`maxim"},
+ {TableName, "`maxim`"},
{ClusterName, "example"},
{ClusterName, "saurus"},
{Keyword, "ANY"},
@@ -722,7 +722,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
{
TVector<TCandidate> expected = {
{BindingName, "$action"},
- {TableName, "`people"},
+ {TableName, "`people`"},
{FolderName, "`yql/"},
{ClusterName, "example"},
{ClusterName, "saurus"},
@@ -1401,8 +1401,8 @@ JOIN yt:$cluster_name.test;
auto petyaEngine = MakeSqlCompletionEngine(lexer, std::move(petyaService));
TVector<TCandidate> empty;
- TVector<TCandidate> aliceExpected = {{TableName, "`alice"}};
- TVector<TCandidate> petyaExpected = {{TableName, "`petya"}};
+ TVector<TCandidate> aliceExpected = {{TableName, "`alice`"}};
+ TVector<TCandidate> petyaExpected = {{TableName, "`petya`"}};
// Cache is empty
UNIT_ASSERT_VALUES_EQUAL(Complete(aliceEngine, "SELECT * FROM "), empty);