diff options
author | vityaman <[email protected]> | 2025-05-27 11:56:16 +0300 |
---|---|---|
committer | robot-piglet <[email protected]> | 2025-05-27 12:09:17 +0300 |
commit | 04c43eb2d8297ffe7b7b7a5b4ca48980d01fca96 (patch) | |
tree | 4230ea5e52fbdbfd3068624004cdc054e1952268 | |
parent | 844ea8aaa5c094d9210b9535d06d98cc5bb56afa (diff) |
YQL-19747: Support immediate string named expressions
---
- Related to `YQL-19747`
- Related to https://github.com/vityaman/ydb/issues/50
---
Pull Request resolved: https://github.com/ytsaurus/ytsaurus/pull/1292
commit_hash:a6d543bc18b2f9239bf0b33afd8497a7919fe4b3
13 files changed, 180 insertions, 22 deletions
diff --git a/yql/essentials/sql/v1/complete/analysis/global/evaluate.cpp b/yql/essentials/sql/v1/complete/analysis/global/evaluate.cpp new file mode 100644 index 00000000000..b938eaf0631 --- /dev/null +++ b/yql/essentials/sql/v1/complete/analysis/global/evaluate.cpp @@ -0,0 +1,52 @@ +#include "evaluate.h" + +namespace NSQLComplete { + + namespace { + + class TVisitor: public SQLv1Antlr4BaseVisitor { + public: + explicit TVisitor(const TEnvironment* env) + : Env_(env) + { + } + + std::any visitBind_parameter(SQLv1::Bind_parameterContext* ctx) override { + std::string id = GetId(ctx); + if (const NYT::TNode* node = Env_->Parameters.FindPtr(id)) { + return *node; + } + return defaultResult(); + } + + std::any defaultResult() override { + return NYT::TNode(); + } + + private: + std::string GetId(SQLv1::Bind_parameterContext* ctx) const { + if (auto* x = ctx->an_id_or_type()) { + return x->getText(); + } else if (auto* x = ctx->TOKEN_TRUE()) { + return x->getText(); + } else if (auto* x = ctx->TOKEN_FALSE()) { + return x->getText(); + } else { + Y_ABORT("You should change implementation according grammar changes"); + } + } + + const TEnvironment* Env_; + }; + + NYT::TNode EvaluateG(antlr4::ParserRuleContext* ctx, const TEnvironment& env) { + return std::any_cast<NYT::TNode>(TVisitor(&env).visit(ctx)); + } + + } // namespace + + NYT::TNode Evaluate(SQLv1::Bind_parameterContext* ctx, const TEnvironment& env) { + return EvaluateG(ctx, env); + } + +} // namespace NSQLComplete diff --git a/yql/essentials/sql/v1/complete/analysis/global/evaluate.h b/yql/essentials/sql/v1/complete/analysis/global/evaluate.h new file mode 100644 index 00000000000..03cbcdc798b --- /dev/null +++ b/yql/essentials/sql/v1/complete/analysis/global/evaluate.h @@ -0,0 +1,11 @@ +#pragma once + +#include "parse_tree.h" + +#include <yql/essentials/sql/v1/complete/core/environment.h> + +namespace NSQLComplete { + + NYT::TNode Evaluate(SQLv1::Bind_parameterContext* ctx, const TEnvironment& env); + +} // namespace NSQLComplete diff --git a/yql/essentials/sql/v1/complete/analysis/global/global.cpp b/yql/essentials/sql/v1/complete/analysis/global/global.cpp index a216b03de1a..9acb3310fac 100644 --- a/yql/essentials/sql/v1/complete/analysis/global/global.cpp +++ b/yql/essentials/sql/v1/complete/analysis/global/global.cpp @@ -42,13 +42,13 @@ namespace NSQLComplete { Parser_.setErrorHandler(std::make_shared<TErrorStrategy>()); } - TGlobalContext Analyze(TCompletionInput input) override { + TGlobalContext Analyze(TCompletionInput input, TEnvironment env) override { SQLv1::Sql_queryContext* sqlQuery = Parse(input.Text); Y_ENSURE(sqlQuery); TGlobalContext ctx; - ctx.Use = FindUseStatement(sqlQuery, &Tokens_, input.CursorPosition); + ctx.Use = FindUseStatement(sqlQuery, &Tokens_, input.CursorPosition, env); return ctx; } @@ -70,9 +70,9 @@ namespace NSQLComplete { class TGlobalAnalysis: public IGlobalAnalysis { public: - TGlobalContext Analyze(TCompletionInput input) override { + TGlobalContext Analyze(TCompletionInput input, TEnvironment env) override { const bool isAnsiLexer = IsAnsiQuery(TString(input.Text)); - return GetSpecialized(isAnsiLexer).Analyze(std::move(input)); + return GetSpecialized(isAnsiLexer).Analyze(std::move(input), std::move(env)); } private: diff --git a/yql/essentials/sql/v1/complete/analysis/global/global.h b/yql/essentials/sql/v1/complete/analysis/global/global.h index a5249bc3a48..97c55738e45 100644 --- a/yql/essentials/sql/v1/complete/analysis/global/global.h +++ b/yql/essentials/sql/v1/complete/analysis/global/global.h @@ -1,6 +1,7 @@ #pragma once #include <yql/essentials/sql/v1/complete/core/input.h> +#include <yql/essentials/sql/v1/complete/core/environment.h> #include <util/generic/ptr.h> #include <util/generic/maybe.h> @@ -22,7 +23,7 @@ namespace NSQLComplete { using TPtr = THolder<IGlobalAnalysis>; virtual ~IGlobalAnalysis() = default; - virtual TGlobalContext Analyze(TCompletionInput input) = 0; + virtual TGlobalContext Analyze(TCompletionInput input, TEnvironment env) = 0; }; IGlobalAnalysis::TPtr MakeGlobalAnalysis(); diff --git a/yql/essentials/sql/v1/complete/analysis/global/use.cpp b/yql/essentials/sql/v1/complete/analysis/global/use.cpp index da7dc6a5751..2fd7fab6614 100644 --- a/yql/essentials/sql/v1/complete/analysis/global/use.cpp +++ b/yql/essentials/sql/v1/complete/analysis/global/use.cpp @@ -1,14 +1,20 @@ #include "use.h" +#include "evaluate.h" + namespace NSQLComplete { namespace { class TVisitor: public SQLv1Antlr4BaseVisitor { public: - TVisitor(antlr4::TokenStream* tokens, size_t cursorPosition) + TVisitor( + antlr4::TokenStream* tokens, + size_t cursorPosition, + const TEnvironment* env) : Tokens_(tokens) , CursorPosition_(cursorPosition) + , Env_(env) { } @@ -33,7 +39,9 @@ namespace NSQLComplete { } if (SQLv1::Pure_column_or_namedContext* ctx = expr->pure_column_or_named()) { - cluster = ctx->getText(); + if (auto id = GetId(ctx)) { + cluster = std::move(*id); + } } if (cluster.empty()) { @@ -76,8 +84,27 @@ namespace NSQLComplete { return antlr4::misc::Interval(CursorPosition_, CursorPosition_); } + TMaybe<TString> GetId(SQLv1::Pure_column_or_namedContext* ctx) const { + if (auto* x = ctx->bind_parameter()) { + return GetId(x); + } else if (auto* x = ctx->an_id()) { + return x->getText(); + } else { + Y_ABORT("You should change implementation according grammar changes"); + } + } + + TMaybe<TString> GetId(SQLv1::Bind_parameterContext* ctx) const { + NYT::TNode node = Evaluate(ctx, *Env_); + if (!node.HasValue() || !node.IsString()) { + return Nothing(); + } + return node.AsString(); + } + antlr4::TokenStream* Tokens_; size_t CursorPosition_; + const TEnvironment* Env_; }; } // namespace @@ -85,8 +112,9 @@ namespace NSQLComplete { TMaybe<TUseContext> FindUseStatement( SQLv1::Sql_queryContext* ctx, antlr4::TokenStream* tokens, - size_t cursorPosition) { - std::any result = TVisitor(tokens, cursorPosition).visit(ctx); + size_t cursorPosition, + const TEnvironment& env) { + std::any result = TVisitor(tokens, cursorPosition, &env).visit(ctx); if (!result.has_value()) { return Nothing(); } diff --git a/yql/essentials/sql/v1/complete/analysis/global/use.h b/yql/essentials/sql/v1/complete/analysis/global/use.h index 54f3557fd62..0cdb9b15469 100644 --- a/yql/essentials/sql/v1/complete/analysis/global/use.h +++ b/yql/essentials/sql/v1/complete/analysis/global/use.h @@ -12,6 +12,7 @@ namespace NSQLComplete { TMaybe<TUseContext> FindUseStatement( SQLv1::Sql_queryContext* ctx, antlr4::TokenStream* tokens, - size_t cursorPosition); + size_t cursorPosition, + const TEnvironment& env); } // namespace NSQLComplete diff --git a/yql/essentials/sql/v1/complete/analysis/global/ya.make b/yql/essentials/sql/v1/complete/analysis/global/ya.make index a28d99f94c2..a8aa1eb7214 100644 --- a/yql/essentials/sql/v1/complete/analysis/global/ya.make +++ b/yql/essentials/sql/v1/complete/analysis/global/ya.make @@ -1,6 +1,7 @@ LIBRARY() SRCS( + evaluate.cpp global.cpp use.cpp ) diff --git a/yql/essentials/sql/v1/complete/core/environment.cpp b/yql/essentials/sql/v1/complete/core/environment.cpp new file mode 100644 index 00000000000..89f179533be --- /dev/null +++ b/yql/essentials/sql/v1/complete/core/environment.cpp @@ -0,0 +1 @@ +#include "environment.h" diff --git a/yql/essentials/sql/v1/complete/core/environment.h b/yql/essentials/sql/v1/complete/core/environment.h new file mode 100644 index 00000000000..0f0299a9353 --- /dev/null +++ b/yql/essentials/sql/v1/complete/core/environment.h @@ -0,0 +1,16 @@ +#pragma once + +#include <library/cpp/yson/node/node.h> + +#include <util/generic/string.h> +#include <util/generic/hash.h> + +namespace NSQLComplete { + + struct TEnvironment { + // Given `{ "$x": "{ "Data": "foo" }" }`, + // it will contain `{ "$x": "foo" }` + THashMap<TString, NYT::TNode> Parameters; + }; + +} // namespace NSQLComplete diff --git a/yql/essentials/sql/v1/complete/core/ya.make b/yql/essentials/sql/v1/complete/core/ya.make index 8bc457f8f95..599810301ca 100644 --- a/yql/essentials/sql/v1/complete/core/ya.make +++ b/yql/essentials/sql/v1/complete/core/ya.make @@ -1,7 +1,12 @@ LIBRARY() SRCS( + environment.cpp input.cpp ) +PEERDIR( + library/cpp/yson/node +) + END() diff --git a/yql/essentials/sql/v1/complete/sql_complete.cpp b/yql/essentials/sql/v1/complete/sql_complete.cpp index 2e155ff6f3b..22fc6b31062 100644 --- a/yql/essentials/sql/v1/complete/sql_complete.cpp +++ b/yql/essentials/sql/v1/complete/sql_complete.cpp @@ -26,10 +26,10 @@ namespace NSQLComplete { } TCompletion Complete(TCompletionInput input) override { - return CompleteAsync(std::move(input)).ExtractValueSync(); + return CompleteAsync(std::move(input), {}).ExtractValueSync(); } - virtual NThreading::TFuture<TCompletion> CompleteAsync(TCompletionInput input) override { + virtual NThreading::TFuture<TCompletion> CompleteAsync(TCompletionInput input, TEnvironment env) override { if ( input.CursorPosition < input.Text.length() && IsUTF8ContinuationByte(input.Text.at(input.CursorPosition)) || @@ -42,7 +42,7 @@ namespace NSQLComplete { TLocalSyntaxContext context = SyntaxAnalysis_->Analyze(input); auto keywords = context.Keywords; - TGlobalContext global = GlobalAnalysis_->Analyze(input); + TGlobalContext global = GlobalAnalysis_->Analyze(input, std::move(env)); TNameRequest request = NameRequestFrom(input, context, global); if (request.IsEmpty()) { diff --git a/yql/essentials/sql/v1/complete/sql_complete.h b/yql/essentials/sql/v1/complete/sql_complete.h index a600efcdac9..f72a228f62a 100644 --- a/yql/essentials/sql/v1/complete/sql_complete.h +++ b/yql/essentials/sql/v1/complete/sql_complete.h @@ -1,6 +1,7 @@ #pragma once #include <yql/essentials/sql/v1/complete/core/input.h> +#include <yql/essentials/sql/v1/complete/core/environment.h> #include <yql/essentials/sql/v1/complete/name/service/name_service.h> #include <yql/essentials/sql/v1/lexer/lexer.h> @@ -52,7 +53,8 @@ namespace NSQLComplete { virtual ~ISqlCompletionEngine() = default; virtual TCompletion Complete(TCompletionInput input) = 0; // TODO(YQL-19747): migrate YDB CLI to CompleteAsync - virtual NThreading::TFuture<TCompletion> CompleteAsync(TCompletionInput input) = 0; + virtual NThreading::TFuture<TCompletion> + CompleteAsync(TCompletionInput input, TEnvironment env = {}) = 0; }; using TLexerSupplier = std::function<NSQLTranslation::ILexer::TPtr(bool ansi)>; diff --git a/yql/essentials/sql/v1/complete/sql_complete_ut.cpp b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp index dc5690853f5..60a77a422d9 100644 --- a/yql/essentials/sql/v1/complete/sql_complete_ut.cpp +++ b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp @@ -127,8 +127,8 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) { return MakeSqlCompletionEngine(std::move(lexer), std::move(service)); } - TVector<TCandidate> Complete(ISqlCompletionEngine::TPtr& engine, TString sharped) { - return engine->CompleteAsync(SharpedInput(sharped)).GetValueSync().Candidates; + TVector<TCandidate> Complete(ISqlCompletionEngine::TPtr& engine, TString sharped, TEnvironment env = {}) { + return engine->CompleteAsync(SharpedInput(sharped), std::move(env)).GetValueSync().Candidates; } TVector<TCandidate> CompleteTop(size_t limit, ISqlCompletionEngine::TPtr& engine, TString sharped) { @@ -196,6 +196,41 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) { UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "USE "), expected); } + Y_UNIT_TEST(UseClusterResultion) { + auto engine = MakeSqlCompletionEngineUT(); + { + TVector<TCandidate> expected = { + {TableName, "`maxim`"}, + {ClusterName, "example"}, + {ClusterName, "yt:saurus"}, + {Keyword, "ANY"}, + }; + UNIT_ASSERT_VALUES_EQUAL( + Complete( + engine, + "USE yt:$cluster_name; SELECT * FROM ", + {.Parameters = {{"cluster_name", "saurus"}}}), + expected); + } + { + TVector<TCandidate> expected = { + {FolderName, "`.sys/`"}, + {FolderName, "`local/`"}, + {FolderName, "`prod/`"}, + {FolderName, "`test/`"}, + {ClusterName, "example"}, + {ClusterName, "yt:saurus"}, + {Keyword, "ANY"}, + }; + UNIT_ASSERT_VALUES_EQUAL( + Complete( + engine, + "USE yt:$cluster_name; SELECT * FROM ", + {.Parameters = {}}), + expected); + } + } + Y_UNIT_TEST(Alter) { TVector<TCandidate> expected = { {Keyword, "ASYNC REPLICATION"}, @@ -981,12 +1016,17 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) { } Y_UNIT_TEST(Tabbing) { - TString query = - "SELECT \n" - " 123467, \"Hello, {name}! 编码\"}, \n" - " (1 + (5 * 1 / 0)), MIN(identifier), \n" - " Bool(field), Math::Sin(var) \n" - "FROM `local/test/space/table` JOIN test;"; + TString query = R"( +USE example; + +SELECT + 123467, \"Hello, {name}! 编码\"}, + (1 + (5 * 1 / 0)), MIN(identifier), + Bool(field), Math::Sin(var) +FROM `local/test/space/table` +JOIN yt:$cluster_name.test; +)"; + query += query + ";"; query += query + ";"; |