summaryrefslogtreecommitdiffstats
path: root/yql/essentials/sql/v1/complete
diff options
context:
space:
mode:
authorvityaman <[email protected]>2025-06-02 13:23:33 +0300
committerrobot-piglet <[email protected]>2025-06-02 13:49:48 +0300
commitf8dd75681a31f8b883f1c69db243a1ded8bb8d94 (patch)
tree4175cf2925fdc77e7f45bb9f84b65cc06a1a6829 /yql/essentials/sql/v1/complete
parent70ff3797ac5c7fd31a1f092b4074dee5668fa0b1 (diff)
YQL-19747: Complete named expressions
- Introduced scoped name collection at global analysis - Added `BingingsNameService` constructed after global analysis and united with the base service. - Added dollar detection at local analysis. After this point design should be reexamined and module should be refactored. --- - Related to `YQL-19747` - Related to https://github.com/ydb-platform/ydb/issues/9056 - Related to https://github.com/vityaman/ydb/issues/57 --- Pull Request resolved: https://github.com/ytsaurus/ytsaurus/pull/1304 commit_hash:a7bf41703f0ee846e359407b53a5d62fc05928ea
Diffstat (limited to 'yql/essentials/sql/v1/complete')
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/evaluate.cpp21
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/global.cpp3
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/global.h2
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/global_ut.cpp72
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/named_node.cpp118
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/named_node.h15
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/narrowing_visitor.cpp33
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/narrowing_visitor.h23
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/parse_tree.cpp20
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/parse_tree.h4
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/use.cpp30
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/ut/ya.make7
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/ya.make8
-rw-r--r--yql/essentials/sql/v1/complete/name/service/binding/name_service.cpp56
-rw-r--r--yql/essentials/sql/v1/complete/name/service/binding/name_service.h12
-rw-r--r--yql/essentials/sql/v1/complete/name/service/binding/ya.make12
-rw-r--r--yql/essentials/sql/v1/complete/name/service/name_service.h23
-rw-r--r--yql/essentials/sql/v1/complete/name/service/ranking/dummy.cpp23
-rw-r--r--yql/essentials/sql/v1/complete/name/service/ranking/dummy.h9
-rw-r--r--yql/essentials/sql/v1/complete/name/service/ranking/ya.make1
-rw-r--r--yql/essentials/sql/v1/complete/name/service/static/name_index.cpp1
-rw-r--r--yql/essentials/sql/v1/complete/name/service/static/name_index.h20
-rw-r--r--yql/essentials/sql/v1/complete/name/service/static/name_service.cpp19
-rw-r--r--yql/essentials/sql/v1/complete/name/service/static/ya.make1
-rw-r--r--yql/essentials/sql/v1/complete/name/service/ya.make1
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete.cpp22
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete.h1
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete_ut.cpp41
-rw-r--r--yql/essentials/sql/v1/complete/syntax/local.cpp5
-rw-r--r--yql/essentials/sql/v1/complete/syntax/local.h1
-rw-r--r--yql/essentials/sql/v1/complete/syntax/parser_call_stack.cpp14
-rw-r--r--yql/essentials/sql/v1/complete/syntax/parser_call_stack.h2
-rw-r--r--yql/essentials/sql/v1/complete/ya.make4
33 files changed, 555 insertions, 69 deletions
diff --git a/yql/essentials/sql/v1/complete/analysis/global/evaluate.cpp b/yql/essentials/sql/v1/complete/analysis/global/evaluate.cpp
index b938eaf0631..f012c77583c 100644
--- a/yql/essentials/sql/v1/complete/analysis/global/evaluate.cpp
+++ b/yql/essentials/sql/v1/complete/analysis/global/evaluate.cpp
@@ -12,10 +12,15 @@ namespace NSQLComplete {
}
std::any visitBind_parameter(SQLv1::Bind_parameterContext* ctx) override {
- std::string id = GetId(ctx);
- if (const NYT::TNode* node = Env_->Parameters.FindPtr(id)) {
+ TMaybe<std::string> id = GetId(ctx);
+ if (id.Empty()) {
+ return defaultResult();
+ }
+
+ if (const NYT::TNode* node = Env_->Parameters.FindPtr(*id)) {
return *node;
}
+
return defaultResult();
}
@@ -24,18 +29,6 @@ namespace NSQLComplete {
}
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_;
};
diff --git a/yql/essentials/sql/v1/complete/analysis/global/global.cpp b/yql/essentials/sql/v1/complete/analysis/global/global.cpp
index 9acb3310fac..c5f33ae8297 100644
--- a/yql/essentials/sql/v1/complete/analysis/global/global.cpp
+++ b/yql/essentials/sql/v1/complete/analysis/global/global.cpp
@@ -1,5 +1,6 @@
#include "global.h"
+#include "named_node.h"
#include "parse_tree.h"
#include "use.h"
@@ -48,7 +49,9 @@ namespace NSQLComplete {
TGlobalContext ctx;
+ // TODO(YQL-19747): Add ~ParseContext(Tokens, ParseTree, CursorPosition)
ctx.Use = FindUseStatement(sqlQuery, &Tokens_, input.CursorPosition, env);
+ ctx.Names = CollectNamedNodes(sqlQuery, &Tokens_, input.CursorPosition);
return ctx;
}
diff --git a/yql/essentials/sql/v1/complete/analysis/global/global.h b/yql/essentials/sql/v1/complete/analysis/global/global.h
index 97c55738e45..2c30dbf1cf5 100644
--- a/yql/essentials/sql/v1/complete/analysis/global/global.h
+++ b/yql/essentials/sql/v1/complete/analysis/global/global.h
@@ -6,6 +6,7 @@
#include <util/generic/ptr.h>
#include <util/generic/maybe.h>
#include <util/generic/string.h>
+#include <util/generic/vector.h>
namespace NSQLComplete {
@@ -16,6 +17,7 @@ namespace NSQLComplete {
struct TGlobalContext {
TMaybe<TUseContext> Use;
+ TVector<TString> Names;
};
class IGlobalAnalysis {
diff --git a/yql/essentials/sql/v1/complete/analysis/global/global_ut.cpp b/yql/essentials/sql/v1/complete/analysis/global/global_ut.cpp
new file mode 100644
index 00000000000..643af6efc9c
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/analysis/global/global_ut.cpp
@@ -0,0 +1,72 @@
+#include "global.h"
+
+#include <library/cpp/testing/unittest/registar.h>
+
+using namespace NSQLComplete;
+
+Y_UNIT_TEST_SUITE(GlobalAnalysisTests) {
+
+ Y_UNIT_TEST(TopLevelNamesCollected) {
+ IGlobalAnalysis::TPtr global = MakeGlobalAnalysis();
+
+ TString query = R"(
+ DECLARE $cluster_name AS String;
+
+ IMPORT math SYMBOLS $sqrt, $pow;
+
+ $sqrt = 0;
+
+ DEFINE ACTION $hello_world($name, $suffix?) AS
+ $name = $name ?? ($suffix ?? "world");
+ SELECT "Hello, " || $name || "!";
+ END DEFINE;
+
+ $first, $second, $_ = AsTuple(1, 2, 3);
+ )";
+
+ TGlobalContext ctx = global->Analyze({query}, {});
+ Sort(ctx.Names);
+
+ TVector<TString> expected = {
+ "cluster_name",
+ "first",
+ "hello_world",
+ "pow",
+ "second",
+ "sqrt",
+ };
+ UNIT_ASSERT_VALUES_EQUAL(ctx.Names, expected);
+ }
+
+ Y_UNIT_TEST(LocalNamesCollected) {
+ IGlobalAnalysis::TPtr global = MakeGlobalAnalysis();
+
+ TString query = R"(
+ DEFINE ACTION $sum($x, $y) AS
+ $acc = 0;
+ EVALUATE FOR $i IN AsList($x, $y) DO BEGIN
+ $plus = ($a, $b) -> (#);
+ $acc = $plus($acc, $i);
+ END DO;
+ END DEFINE;
+ )";
+
+ TCompletionInput input = SharpedInput(query);
+
+ TGlobalContext ctx = global->Analyze(input, {});
+ Sort(ctx.Names);
+
+ TVector<TString> expected = {
+ "a",
+ "acc",
+ "b",
+ "i",
+ "plus",
+ "sum",
+ "x",
+ "y",
+ };
+ UNIT_ASSERT_VALUES_EQUAL(ctx.Names, expected);
+ }
+
+} // Y_UNIT_TEST_SUITE(GlobalAnalysisTests)
diff --git a/yql/essentials/sql/v1/complete/analysis/global/named_node.cpp b/yql/essentials/sql/v1/complete/analysis/global/named_node.cpp
new file mode 100644
index 00000000000..14b928c53e5
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/analysis/global/named_node.cpp
@@ -0,0 +1,118 @@
+#include "named_node.h"
+
+#include "narrowing_visitor.h"
+
+#include <library/cpp/iterator/iterate_keys.h>
+#include <library/cpp/iterator/iterate_values.h>
+
+#include <util/generic/hash_set.h>
+#include <util/generic/ptr.h>
+
+namespace NSQLComplete {
+
+ namespace {
+
+ class TVisitor: public TSQLv1NarrowingVisitor {
+ public:
+ TVisitor(
+ antlr4::TokenStream* tokens,
+ size_t cursorPosition,
+ THashSet<TString>* names)
+ : TSQLv1NarrowingVisitor(tokens, cursorPosition)
+ , Names_(names)
+ {
+ }
+
+ std::any visitSql_stmt_core(SQLv1::Sql_stmt_coreContext* ctx) override {
+ if (ctx->declare_stmt() ||
+ ctx->import_stmt() ||
+ ctx->define_action_or_subquery_stmt() ||
+ ctx->named_nodes_stmt() ||
+ IsEnclosing(ctx)) {
+ return visitChildren(ctx);
+ }
+ return {};
+ }
+
+ std::any visitDeclare_stmt(SQLv1::Declare_stmtContext* ctx) override {
+ VisitNullable(ctx->bind_parameter());
+ return {};
+ }
+
+ std::any visitImport_stmt(SQLv1::Import_stmtContext* ctx) override {
+ VisitNullable(ctx->named_bind_parameter_list());
+ return {};
+ }
+
+ std::any visitDefine_action_or_subquery_stmt(
+ SQLv1::Define_action_or_subquery_stmtContext* ctx) override {
+ VisitNullable(ctx->bind_parameter());
+ if (IsEnclosing(ctx)) {
+ return visitChildren(ctx);
+ }
+ return {};
+ }
+
+ std::any visitNamed_nodes_stmt(SQLv1::Named_nodes_stmtContext* ctx) override {
+ VisitNullable(ctx->bind_parameter_list());
+ if (IsEnclosing(ctx)) {
+ return visitChildren(ctx);
+ }
+ return {};
+ }
+
+ std::any visitFor_stmt(SQLv1::For_stmtContext* ctx) override {
+ VisitNullable(ctx->bind_parameter());
+ if (IsEnclosing(ctx)) {
+ return visitChildren(ctx);
+ }
+ return {};
+ }
+
+ std::any visitLambda(SQLv1::LambdaContext* ctx) override {
+ VisitNullable(ctx->smart_parenthesis());
+ if (IsEnclosing(ctx)) {
+ return visitChildren(ctx);
+ }
+ return {};
+ }
+
+ std::any visitNamed_bind_parameter(
+ SQLv1::Named_bind_parameterContext* ctx) override {
+ VisitNullable(ctx->bind_parameter(0));
+ return {};
+ }
+
+ std::any visitBind_parameter(SQLv1::Bind_parameterContext* ctx) override {
+ TMaybe<std::string> id = GetId(ctx);
+ if (id.Empty() || id == "_") {
+ return {};
+ }
+
+ Names_->emplace(std::move(*id));
+ return {};
+ }
+
+ private:
+ void VisitNullable(antlr4::tree::ParseTree* tree) {
+ if (tree == nullptr) {
+ return;
+ }
+ visit(tree);
+ }
+
+ THashSet<TString>* Names_;
+ };
+
+ } // namespace
+
+ TVector<TString> CollectNamedNodes(
+ SQLv1::Sql_queryContext* ctx,
+ antlr4::TokenStream* tokens,
+ size_t cursorPosition) {
+ THashSet<TString> names;
+ TVisitor(tokens, cursorPosition, &names).visit(ctx);
+ return TVector<TString>(begin(names), end(names));
+ }
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/analysis/global/named_node.h b/yql/essentials/sql/v1/complete/analysis/global/named_node.h
new file mode 100644
index 00000000000..119fbc4d5f6
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/analysis/global/named_node.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "parse_tree.h"
+
+#include <util/generic/string.h>
+#include <util/generic/vector.h>
+
+namespace NSQLComplete {
+
+ TVector<TString> CollectNamedNodes(
+ SQLv1::Sql_queryContext* ctx,
+ antlr4::TokenStream* tokens,
+ size_t cursorPosition);
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/analysis/global/narrowing_visitor.cpp b/yql/essentials/sql/v1/complete/analysis/global/narrowing_visitor.cpp
new file mode 100644
index 00000000000..eefb6e70f5e
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/analysis/global/narrowing_visitor.cpp
@@ -0,0 +1,33 @@
+#include "narrowing_visitor.h"
+
+namespace NSQLComplete {
+
+ TSQLv1NarrowingVisitor::TSQLv1NarrowingVisitor(antlr4::TokenStream* tokens, size_t cursorPosition)
+ : Tokens_(tokens)
+ , CursorPosition_(cursorPosition)
+ {
+ }
+
+ bool TSQLv1NarrowingVisitor::shouldVisitNextChild(antlr4::tree::ParseTree* node, const std::any& /*currentResult*/) {
+ return TextInterval(node).a < static_cast<ssize_t>(CursorPosition_);
+ }
+
+ bool TSQLv1NarrowingVisitor::IsEnclosing(antlr4::tree::ParseTree* tree) const {
+ return TextInterval(tree).properlyContains(CursorInterval());
+ }
+
+ antlr4::misc::Interval TSQLv1NarrowingVisitor::TextInterval(antlr4::tree::ParseTree* tree) const {
+ auto tokens = tree->getSourceInterval();
+ if (tokens.b == -1) {
+ tokens.b = tokens.a;
+ }
+ return antlr4::misc::Interval(
+ Tokens_->get(tokens.a)->getStartIndex(),
+ Tokens_->get(tokens.b)->getStopIndex());
+ }
+
+ antlr4::misc::Interval TSQLv1NarrowingVisitor::CursorInterval() const {
+ return antlr4::misc::Interval(CursorPosition_, CursorPosition_);
+ }
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/analysis/global/narrowing_visitor.h b/yql/essentials/sql/v1/complete/analysis/global/narrowing_visitor.h
new file mode 100644
index 00000000000..d5375c20322
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/analysis/global/narrowing_visitor.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "parse_tree.h"
+
+namespace NSQLComplete {
+
+ class TSQLv1NarrowingVisitor: public SQLv1Antlr4BaseVisitor {
+ public:
+ TSQLv1NarrowingVisitor(antlr4::TokenStream* tokens, size_t cursorPosition);
+
+ protected:
+ bool IsEnclosing(antlr4::tree::ParseTree* tree) const;
+ bool shouldVisitNextChild(antlr4::tree::ParseTree* node, const std::any& /*currentResult*/) override;
+
+ private:
+ antlr4::misc::Interval TextInterval(antlr4::tree::ParseTree* tree) const;
+ antlr4::misc::Interval CursorInterval() const;
+
+ antlr4::TokenStream* Tokens_;
+ size_t CursorPosition_;
+ };
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/analysis/global/parse_tree.cpp b/yql/essentials/sql/v1/complete/analysis/global/parse_tree.cpp
new file mode 100644
index 00000000000..2ed82752a01
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/analysis/global/parse_tree.cpp
@@ -0,0 +1,20 @@
+#include "parse_tree.h"
+
+#include <util/system/yassert.h>
+#include <util/generic/maybe.h>
+
+namespace NSQLComplete {
+
+ TMaybe<std::string> GetId(SQLv1::Bind_parameterContext* ctx) {
+ 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 {
+ return Nothing();
+ }
+ }
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/analysis/global/parse_tree.h b/yql/essentials/sql/v1/complete/analysis/global/parse_tree.h
index 25ca6d336cb..f194232aea5 100644
--- a/yql/essentials/sql/v1/complete/analysis/global/parse_tree.h
+++ b/yql/essentials/sql/v1/complete/analysis/global/parse_tree.h
@@ -1,5 +1,7 @@
#pragma once
+#include <util/generic/maybe.h>
+
#ifdef TOKEN_QUERY // Conflict with the winnt.h
#undef TOKEN_QUERY
#endif
@@ -15,4 +17,6 @@ namespace NSQLComplete {
using NALADefaultAntlr4::SQLv1Antlr4BaseVisitor;
+ TMaybe<std::string> GetId(SQLv1::Bind_parameterContext* ctx);
+
} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/analysis/global/use.cpp b/yql/essentials/sql/v1/complete/analysis/global/use.cpp
index 2fd7fab6614..717a35e4e8f 100644
--- a/yql/essentials/sql/v1/complete/analysis/global/use.cpp
+++ b/yql/essentials/sql/v1/complete/analysis/global/use.cpp
@@ -1,19 +1,19 @@
#include "use.h"
#include "evaluate.h"
+#include "narrowing_visitor.h"
namespace NSQLComplete {
namespace {
- class TVisitor: public SQLv1Antlr4BaseVisitor {
+ class TVisitor: public TSQLv1NarrowingVisitor {
public:
TVisitor(
antlr4::TokenStream* tokens,
size_t cursorPosition,
const TEnvironment* env)
- : Tokens_(tokens)
- , CursorPosition_(cursorPosition)
+ : TSQLv1NarrowingVisitor(tokens, cursorPosition)
, Env_(env)
{
}
@@ -61,29 +61,7 @@ namespace NSQLComplete {
return aggregate;
}
- bool shouldVisitNextChild(antlr4::tree::ParseTree* node, const std::any& /*currentResult*/) override {
- return TextInterval(node).a < static_cast<ssize_t>(CursorPosition_);
- }
-
private:
- bool IsEnclosing(antlr4::tree::ParseTree* tree) const {
- return TextInterval(tree).properlyContains(CursorInterval());
- }
-
- antlr4::misc::Interval TextInterval(antlr4::tree::ParseTree* tree) const {
- auto tokens = tree->getSourceInterval();
- if (tokens.b == -1) {
- tokens.b = tokens.a;
- }
- return antlr4::misc::Interval(
- Tokens_->get(tokens.a)->getStartIndex(),
- Tokens_->get(tokens.b)->getStopIndex());
- }
-
- antlr4::misc::Interval CursorInterval() const {
- 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);
@@ -102,8 +80,6 @@ namespace NSQLComplete {
return node.AsString();
}
- antlr4::TokenStream* Tokens_;
- size_t CursorPosition_;
const TEnvironment* Env_;
};
diff --git a/yql/essentials/sql/v1/complete/analysis/global/ut/ya.make b/yql/essentials/sql/v1/complete/analysis/global/ut/ya.make
new file mode 100644
index 00000000000..daeeee04b73
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/analysis/global/ut/ya.make
@@ -0,0 +1,7 @@
+UNITTEST_FOR(yql/essentials/sql/v1/complete/analysis/global)
+
+SRCS(
+ global_ut.cpp
+)
+
+END()
diff --git a/yql/essentials/sql/v1/complete/analysis/global/ya.make b/yql/essentials/sql/v1/complete/analysis/global/ya.make
index a8aa1eb7214..da6f4a17381 100644
--- a/yql/essentials/sql/v1/complete/analysis/global/ya.make
+++ b/yql/essentials/sql/v1/complete/analysis/global/ya.make
@@ -3,13 +3,21 @@ LIBRARY()
SRCS(
evaluate.cpp
global.cpp
+ named_node.cpp
+ narrowing_visitor.cpp
+ parse_tree.cpp
use.cpp
)
PEERDIR(
yql/essentials/sql/v1/complete/core
+ yql/essentials/sql/v1/complete/syntax
yql/essentials/parser/antlr_ast/gen/v1_antlr4
yql/essentials/parser/antlr_ast/gen/v1_ansi_antlr4
)
END()
+
+RECURSE_FOR_TESTS(
+ ut
+)
diff --git a/yql/essentials/sql/v1/complete/name/service/binding/name_service.cpp b/yql/essentials/sql/v1/complete/name/service/binding/name_service.cpp
new file mode 100644
index 00000000000..9de6380410b
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/name/service/binding/name_service.cpp
@@ -0,0 +1,56 @@
+#include "name_service.h"
+
+#include <yql/essentials/sql/v1/complete/name/service/static/name_index.h>
+
+namespace NSQLComplete {
+
+ namespace {
+
+ class TNameService: public INameService {
+ public:
+ explicit TNameService(TVector<TString> names)
+ : Index_(BuildNameIndex(std::move(names), NormalizeName))
+ {
+ }
+
+ NThreading::TFuture<TNameResponse> Lookup(TNameRequest request) const override {
+ if (request.Constraints.IsEmpty()) {
+ return NThreading::MakeFuture<TNameResponse>({});
+ }
+
+ TVector<TStringBuf> filtered =
+ FilteredByPrefix(request.Prefix, Index_, NormalizeName);
+ filtered.crop(request.Limit);
+
+ return NThreading::MakeFuture<TNameResponse>({
+ .RankedNames = Transform(filtered),
+ });
+ }
+
+ private:
+ static TVector<TGenericName> Transform(TVector<TStringBuf> names) {
+ TVector<TGenericName> generic;
+ generic.reserve(names.size());
+ for (TStringBuf name : names) {
+ generic.emplace_back(Transform(name));
+ }
+ return generic;
+ }
+
+ static TGenericName Transform(TStringBuf name) {
+ TBindingName unknown;
+ unknown.Indentifier = name;
+ return unknown;
+ }
+
+ TNameIndex Index_;
+ IRanking::TPtr Ranking_;
+ };
+
+ } // namespace
+
+ INameService::TPtr MakeBindingNameService(TVector<TString> names) {
+ return new TNameService(std::move(names));
+ }
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/name/service/binding/name_service.h b/yql/essentials/sql/v1/complete/name/service/binding/name_service.h
new file mode 100644
index 00000000000..8fdac1a8d7e
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/name/service/binding/name_service.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#pragma once
+
+#include <yql/essentials/sql/v1/complete/name/service/name_service.h>
+#include <yql/essentials/sql/v1/complete/name/service/ranking/ranking.h>
+
+namespace NSQLComplete {
+
+ INameService::TPtr MakeBindingNameService(TVector<TString> names);
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/name/service/binding/ya.make b/yql/essentials/sql/v1/complete/name/service/binding/ya.make
new file mode 100644
index 00000000000..54ba24fcff4
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/name/service/binding/ya.make
@@ -0,0 +1,12 @@
+LIBRARY()
+
+SRCS(
+ name_service.cpp
+)
+
+PEERDIR(
+ yql/essentials/sql/v1/complete/name/service
+ yql/essentials/sql/v1/complete/name/service/static
+)
+
+END()
diff --git a/yql/essentials/sql/v1/complete/name/service/name_service.h b/yql/essentials/sql/v1/complete/name/service/name_service.h
index cdcfc3a4982..bff94f9504c 100644
--- a/yql/essentials/sql/v1/complete/name/service/name_service.h
+++ b/yql/essentials/sql/v1/complete/name/service/name_service.h
@@ -58,6 +58,9 @@ namespace NSQLComplete {
struct TConstraints: TNamespaced {};
};
+ struct TBindingName: TIndentifier {
+ };
+
struct TUnkownName {
TString Content;
TString Type;
@@ -72,6 +75,7 @@ namespace NSQLComplete {
TFolderName,
TTableName,
TClusterName,
+ TBindingName,
TUnkownName>;
struct TNameConstraints {
@@ -82,6 +86,15 @@ namespace NSQLComplete {
TMaybe<TObjectNameConstraints> Object;
TMaybe<TClusterName::TConstraints> Cluster;
+ bool IsEmpty() const {
+ return !Pragma &&
+ !Type &&
+ !Function &&
+ !Hint &&
+ !Object &&
+ !Cluster;
+ }
+
TGenericName Qualified(TGenericName unqualified) const;
TGenericName Unqualified(TGenericName qualified) const;
TVector<TGenericName> Qualified(TVector<TGenericName> unqualified) const;
@@ -95,19 +108,13 @@ namespace NSQLComplete {
size_t Limit = 128;
bool IsEmpty() const {
- return Keywords.empty() &&
- !Constraints.Pragma &&
- !Constraints.Type &&
- !Constraints.Function &&
- !Constraints.Hint &&
- !Constraints.Object &&
- !Constraints.Cluster;
+ return Keywords.empty() && Constraints.IsEmpty();
}
};
struct TNameResponse {
TVector<TGenericName> RankedNames;
- TMaybe<size_t> NameHintLength;
+ TMaybe<size_t> NameHintLength = Nothing();
bool IsEmpty() const {
return RankedNames.empty();
diff --git a/yql/essentials/sql/v1/complete/name/service/ranking/dummy.cpp b/yql/essentials/sql/v1/complete/name/service/ranking/dummy.cpp
new file mode 100644
index 00000000000..c8dc320cc73
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/name/service/ranking/dummy.cpp
@@ -0,0 +1,23 @@
+#include "dummy.h"
+
+namespace NSQLComplete {
+
+ namespace {
+
+ class TRanking: public IRanking {
+ public:
+ void CropToSortedPrefix(
+ TVector<TGenericName>& names,
+ const TNameConstraints& /* constraints */,
+ size_t limit) const override {
+ names.crop(limit);
+ }
+ };
+
+ } // namespace
+
+ IRanking::TPtr MakeDummyRanking() {
+ return new TRanking();
+ }
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/name/service/ranking/dummy.h b/yql/essentials/sql/v1/complete/name/service/ranking/dummy.h
new file mode 100644
index 00000000000..f9f024fe7ab
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/name/service/ranking/dummy.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "ranking.h"
+
+namespace NSQLComplete {
+
+ IRanking::TPtr MakeDummyRanking();
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/name/service/ranking/ya.make b/yql/essentials/sql/v1/complete/name/service/ranking/ya.make
index 56e8e782128..e5464cf1f96 100644
--- a/yql/essentials/sql/v1/complete/name/service/ranking/ya.make
+++ b/yql/essentials/sql/v1/complete/name/service/ranking/ya.make
@@ -1,6 +1,7 @@
LIBRARY()
SRCS(
+ dummy.cpp
frequency.cpp
ranking.cpp
)
diff --git a/yql/essentials/sql/v1/complete/name/service/static/name_index.cpp b/yql/essentials/sql/v1/complete/name/service/static/name_index.cpp
new file mode 100644
index 00000000000..707c367385f
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/name/service/static/name_index.cpp
@@ -0,0 +1 @@
+#include "name_index.h"
diff --git a/yql/essentials/sql/v1/complete/name/service/static/name_index.h b/yql/essentials/sql/v1/complete/name/service/static/name_index.h
index 840a4a5fe44..271d7beca48 100644
--- a/yql/essentials/sql/v1/complete/name/service/static/name_index.h
+++ b/yql/essentials/sql/v1/complete/name/service/static/name_index.h
@@ -39,4 +39,24 @@ namespace NSQLComplete {
return index;
}
+ const TVector<TStringBuf> FilteredByPrefix(
+ const TString& prefix,
+ const TNameIndex& index Y_LIFETIME_BOUND,
+ auto normalize) {
+ TNameIndexEntry normalized = {
+ .Normalized = normalize(prefix),
+ .Original = "",
+ };
+
+ auto range = std::ranges::equal_range(
+ std::begin(index), std::end(index),
+ normalized, NameIndexCompareLimit(normalized.Normalized.size()));
+
+ TVector<TStringBuf> filtered;
+ for (const TNameIndexEntry& entry : range) {
+ filtered.emplace_back(TStringBuf(entry.Original));
+ }
+ return filtered;
+ }
+
} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/name/service/static/name_service.cpp b/yql/essentials/sql/v1/complete/name/service/static/name_service.cpp
index 976646dd7f4..fd6aad714ee 100644
--- a/yql/essentials/sql/v1/complete/name/service/static/name_service.cpp
+++ b/yql/essentials/sql/v1/complete/name/service/static/name_service.cpp
@@ -8,23 +8,6 @@
namespace NSQLComplete {
- const TVector<TStringBuf> FilteredByPrefix(const TString& prefix, const TNameIndex& index Y_LIFETIME_BOUND) {
- TNameIndexEntry normalized = {
- .Normalized = NormalizeName(prefix),
- .Original = "",
- };
-
- auto range = std::ranges::equal_range(
- std::begin(index), std::end(index),
- normalized, NameIndexCompareLimit(normalized.Normalized.size()));
-
- TVector<TStringBuf> filtered;
- for (const TNameIndexEntry& entry : range) {
- filtered.emplace_back(TStringBuf(entry.Original));
- }
- return filtered;
- }
-
const TVector<TStringBuf> FilteredByPrefix(
const TString& prefix,
const TVector<TString>& sorted Y_LIFETIME_BOUND) {
@@ -51,7 +34,7 @@ namespace NSQLComplete {
name.Indentifier = prefix;
name = std::get<T>(constraints.Qualified(std::move(name)));
- AppendAs<T>(out, FilteredByPrefix(name.Indentifier, index));
+ AppendAs<T>(out, FilteredByPrefix(name.Indentifier, index, NormalizeName));
out = constraints.Unqualified(std::move(out));
}
diff --git a/yql/essentials/sql/v1/complete/name/service/static/ya.make b/yql/essentials/sql/v1/complete/name/service/static/ya.make
index 317ef4ed84a..e1211e223e0 100644
--- a/yql/essentials/sql/v1/complete/name/service/static/ya.make
+++ b/yql/essentials/sql/v1/complete/name/service/static/ya.make
@@ -1,6 +1,7 @@
LIBRARY()
SRCS(
+ name_index.cpp
name_service.cpp
name_set_json.cpp
name_set.cpp
diff --git a/yql/essentials/sql/v1/complete/name/service/ya.make b/yql/essentials/sql/v1/complete/name/service/ya.make
index ec4de4d5e10..2f8118f882d 100644
--- a/yql/essentials/sql/v1/complete/name/service/ya.make
+++ b/yql/essentials/sql/v1/complete/name/service/ya.make
@@ -12,6 +12,7 @@ PEERDIR(
END()
RECURSE(
+ binding
cluster
ranking
schema
diff --git a/yql/essentials/sql/v1/complete/sql_complete.cpp b/yql/essentials/sql/v1/complete/sql_complete.cpp
index 807786512e4..eb7610640a1 100644
--- a/yql/essentials/sql/v1/complete/sql_complete.cpp
+++ b/yql/essentials/sql/v1/complete/sql_complete.cpp
@@ -2,7 +2,10 @@
#include <yql/essentials/sql/v1/complete/syntax/grammar.h>
#include <yql/essentials/sql/v1/complete/text/word.h>
+#include <yql/essentials/sql/v1/complete/name/service/ranking/dummy.h>
+#include <yql/essentials/sql/v1/complete/name/service/binding/name_service.h>
#include <yql/essentials/sql/v1/complete/name/service/static/name_service.h>
+#include <yql/essentials/sql/v1/complete/name/service/union/name_service.h>
#include <yql/essentials/sql/v1/complete/syntax/local.h>
#include <yql/essentials/sql/v1/complete/syntax/format.h>
#include <yql/essentials/sql/v1/complete/analysis/global/global.h>
@@ -52,7 +55,14 @@ namespace NSQLComplete {
});
}
- return Names_->Lookup(std::move(request))
+ TVector<INameService::TPtr> children;
+ children.emplace_back(MakeBindingNameService(std::move(global.Names)));
+ if (!context.Binding) {
+ children.emplace_back(Names_);
+ }
+
+ return MakeUnionNameService(std::move(children), MakeDummyRanking())
+ ->Lookup(std::move(request))
.Apply([this, input, context = std::move(context)](auto f) {
return ToCompletion(input, context, f.ExtractValue());
});
@@ -202,6 +212,13 @@ namespace NSQLComplete {
return {ECandidateKind::ClusterName, std::move(name.Indentifier)};
}
+ if constexpr (std::is_base_of_v<TBindingName, T>) {
+ if (!context.Binding) {
+ name.Indentifier.prepend('$');
+ }
+ return {ECandidateKind::BindingName, std::move(name.Indentifier)};
+ }
+
if constexpr (std::is_base_of_v<TUnkownName, T>) {
return {ECandidateKind::UnknownName, std::move(name.Content)};
}
@@ -290,6 +307,9 @@ void Out<NSQLComplete::ECandidateKind>(IOutputStream& out, NSQLComplete::ECandid
case NSQLComplete::ECandidateKind::ClusterName:
out << "ClusterName";
break;
+ case NSQLComplete::ECandidateKind::BindingName:
+ out << "BindingName";
+ break;
case NSQLComplete::ECandidateKind::UnknownName:
out << "UnknownName";
break;
diff --git a/yql/essentials/sql/v1/complete/sql_complete.h b/yql/essentials/sql/v1/complete/sql_complete.h
index e5dffcb8d27..6174f913254 100644
--- a/yql/essentials/sql/v1/complete/sql_complete.h
+++ b/yql/essentials/sql/v1/complete/sql_complete.h
@@ -27,6 +27,7 @@ namespace NSQLComplete {
FolderName,
TableName,
ClusterName,
+ BindingName,
UnknownName,
};
diff --git a/yql/essentials/sql/v1/complete/sql_complete_ut.cpp b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
index 7460924b48f..cc39fa875cc 100644
--- a/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
+++ b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
@@ -39,6 +39,7 @@ public:
};
Y_UNIT_TEST_SUITE(SqlCompleteTests) {
+ using ECandidateKind::BindingName;
using ECandidateKind::ClusterName;
using ECandidateKind::FolderName;
using ECandidateKind::FunctionName;
@@ -679,6 +680,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
}
{
TVector<TCandidate> expected = {
+ {BindingName, "$hello"},
{TableName, "`maxim"},
{ClusterName, "example"},
{ClusterName, "saurus"},
@@ -694,6 +696,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
}
{
TVector<TCandidate> expected = {
+ {BindingName, "$action"},
{TableName, "`people"},
{FolderName, "`yql/"},
{ClusterName, "example"},
@@ -983,6 +986,44 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
UNIT_ASSERT_GE(Complete(engine, "SELECT 1;").size(), 35);
}
+ Y_UNIT_TEST(Bindings) {
+ auto engine = MakeSqlCompletionEngineUT();
+
+ TString query = R"(
+ $=0;
+ $a=0;
+ $abac=0;
+ SELECT
+ )";
+
+ {
+ TVector<TCandidate> expected = {
+ {BindingName, "$a"},
+ {BindingName, "$abac"},
+ };
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, query), expected);
+ }
+ {
+ TVector<TCandidate> expected = {
+ {BindingName, "$abac"},
+ };
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(1, engine, query + "ab"), expected);
+ }
+ {
+ TVector<TCandidate> expected = {
+ {BindingName, "a"},
+ {BindingName, "abac"},
+ };
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, query + "$"), expected);
+ }
+ {
+ TVector<TCandidate> expected = {
+ {BindingName, "abac"},
+ };
+ UNIT_ASSERT_VALUES_EQUAL(Complete(engine, query + "$ab"), expected);
+ }
+ }
+
Y_UNIT_TEST(Typing) {
const auto queryUtf16 = TUtf16String::FromUtf8(
"SELECT \n"
diff --git a/yql/essentials/sql/v1/complete/syntax/local.cpp b/yql/essentials/sql/v1/complete/syntax/local.cpp
index 772f5b78dd5..8e956dc66db 100644
--- a/yql/essentials/sql/v1/complete/syntax/local.cpp
+++ b/yql/essentials/sql/v1/complete/syntax/local.cpp
@@ -101,6 +101,7 @@ namespace NSQLComplete {
result.Hint = HintMatch(candidates);
result.Object = ObjectMatch(context, candidates);
result.Cluster = ClusterMatch(context, candidates);
+ result.Binding = BindingMatch(candidates);
return result;
}
@@ -292,6 +293,10 @@ namespace NSQLComplete {
return cluster;
}
+ bool BindingMatch(const TC3Candidates& candidates) const {
+ return AnyOf(candidates.Rules, RuleAdapted(IsLikelyBindingStack));
+ }
+
TEditRange EditRange(const TCursorTokenContext& context) const {
if (auto enclosing = context.Enclosing()) {
return EditRange(*enclosing, context.Cursor);
diff --git a/yql/essentials/sql/v1/complete/syntax/local.h b/yql/essentials/sql/v1/complete/syntax/local.h
index 0017afa0684..da44969c746 100644
--- a/yql/essentials/sql/v1/complete/syntax/local.h
+++ b/yql/essentials/sql/v1/complete/syntax/local.h
@@ -55,6 +55,7 @@ namespace NSQLComplete {
TMaybe<THint> Hint;
TMaybe<TObject> Object;
TMaybe<TCluster> Cluster;
+ bool Binding = false;
TEditRange EditRange;
};
diff --git a/yql/essentials/sql/v1/complete/syntax/parser_call_stack.cpp b/yql/essentials/sql/v1/complete/syntax/parser_call_stack.cpp
index 03e07181a0d..efde1ea16b6 100644
--- a/yql/essentials/sql/v1/complete/syntax/parser_call_stack.cpp
+++ b/yql/essentials/sql/v1/complete/syntax/parser_call_stack.cpp
@@ -73,7 +73,11 @@ namespace NSQLComplete {
RULE(Named_expr_list),
RULE(Named_expr),
RULE(Expr)}, stack) &&
- EndsWith({RULE(Atom_expr), RULE(An_id_or_type)}, stack));
+ (EndsWith({RULE(Atom_expr),
+ RULE(An_id_or_type)}, stack) ||
+ EndsWith({RULE(Atom_expr),
+ RULE(Bind_parameter),
+ RULE(An_id_or_type)}, stack)));
}
bool IsLikelyFunctionStack(const TParserCallStack& stack) {
@@ -81,6 +85,10 @@ namespace NSQLComplete {
EndsWith({RULE(Unary_casual_subexpr),
RULE(Atom_expr),
RULE(An_id_or_type)}, stack) ||
+ EndsWith({RULE(Unary_casual_subexpr),
+ RULE(Atom_expr),
+ RULE(Bind_parameter),
+ RULE(An_id_or_type)}, stack) ||
EndsWith({RULE(Atom_expr), RULE(Id_or_type)}, stack);
}
@@ -109,6 +117,10 @@ namespace NSQLComplete {
return Contains({RULE(Cluster_expr)}, stack);
}
+ bool IsLikelyBindingStack(const TParserCallStack& stack) {
+ return EndsWith({RULE(Bind_parameter), RULE(An_id_or_type)}, stack);
+ }
+
TMaybe<EStatementKind> StatementKindOf(const TParserCallStack& stack) {
for (TRuleId rule : std::ranges::views::reverse(stack)) {
if (rule == RULE(Process_core) || rule == RULE(Reduce_core) || rule == RULE(Select_core)) {
diff --git a/yql/essentials/sql/v1/complete/syntax/parser_call_stack.h b/yql/essentials/sql/v1/complete/syntax/parser_call_stack.h
index d44b824a05e..c8daf9114fe 100644
--- a/yql/essentials/sql/v1/complete/syntax/parser_call_stack.h
+++ b/yql/essentials/sql/v1/complete/syntax/parser_call_stack.h
@@ -21,6 +21,8 @@ namespace NSQLComplete {
bool IsLikelyClusterStack(const TParserCallStack& stack);
+ bool IsLikelyBindingStack(const TParserCallStack& stack);
+
TMaybe<EStatementKind> StatementKindOf(const TParserCallStack& stack);
std::unordered_set<TRuleId> GetC3PreferredRules();
diff --git a/yql/essentials/sql/v1/complete/ya.make b/yql/essentials/sql/v1/complete/ya.make
index e2a7d7db5f9..aeec31d506a 100644
--- a/yql/essentials/sql/v1/complete/ya.make
+++ b/yql/essentials/sql/v1/complete/ya.make
@@ -12,6 +12,10 @@ PEERDIR(
yql/essentials/sql/v1/complete/syntax
yql/essentials/sql/v1/complete/analysis/global
yql/essentials/sql/v1/complete/text
+ # TODO(YQL-19747): split /name/service/ranking interface and implementation
+ # TODO(YQL-19747): extract NameIndex
+ yql/essentials/sql/v1/complete/name/service/ranking
+ yql/essentials/sql/v1/complete/name/service/binding
# TODO(YQL-19747): add it to YDB CLI PEERDIR
yql/essentials/sql/v1/complete/name/service/static
)