summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvitya-smirnov <[email protected]>2025-08-26 16:00:33 +0300
committervitya-smirnov <[email protected]>2025-08-26 16:25:59 +0300
commitb142a8ffb787fc94482c7504ff7fb0ed820a4a28 (patch)
tree3542d763492a14cc6bd279870ed6932bd560e5d1
parent6c7504ede1e3ce47b5da317e81256c47b75c79e4 (diff)
YQL-19747: Replicate unambiguous columns and fix ranking
This is an improvement for column suggestions. Even when user made a source aliased, he still is able to reference a column with both unqualified and qualified name, so we should provide both candidates, but with unqualified more relevant. As this patch required non trivial `NameResponse` modifications at the `CompletionEngine`, ranking should be used properly, so a way to pass it to the engine was introduced. commit_hash:1f2b90e2f6fbb7d33d9fc8479f43349a7f08c320
-rw-r--r--yql/essentials/sql/v1/complete/name/service/column/replicating.cpp85
-rw-r--r--yql/essentials/sql/v1/complete/name/service/column/replicating.h10
-rw-r--r--yql/essentials/sql/v1/complete/name/service/column/ya.make1
-rw-r--r--yql/essentials/sql/v1/complete/name/service/name_service.h1
-rw-r--r--yql/essentials/sql/v1/complete/name/service/ranking/ranking.cpp19
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete.cpp18
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete.h4
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete_ut.cpp165
8 files changed, 263 insertions, 40 deletions
diff --git a/yql/essentials/sql/v1/complete/name/service/column/replicating.cpp b/yql/essentials/sql/v1/complete/name/service/column/replicating.cpp
new file mode 100644
index 00000000000..ef2d39969d5
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/name/service/column/replicating.cpp
@@ -0,0 +1,85 @@
+#include "replicating.h"
+
+#include <yql/essentials/sql/v1/complete/name/object/simple/static/schema.h>
+
+#include <library/cpp/case_insensitive_string/case_insensitive_string.h>
+
+#include <util/generic/hash.h>
+
+namespace NSQLComplete {
+
+ namespace {
+
+ class TNameService: public INameService {
+ public:
+ TNameService(INameService::TPtr origin, IRanking::TPtr ranking)
+ : Origin_(std::move(origin))
+ , Ranking_(std::move(ranking))
+ {
+ }
+
+ NThreading::TFuture<TNameResponse> Lookup(const TNameRequest& request) const final {
+ NThreading::TFuture<TNameResponse> response = Origin_->Lookup(request);
+
+ if (!request.Constraints.Column || request.Constraints.Column->TableAlias) {
+ return response;
+ }
+
+ return std::move(response).Apply([request, ranking = Ranking_](auto f) -> TNameResponse {
+ TNameResponse response = f.ExtractValue();
+
+ TVector<TGenericName> replicatable = ReplicatableColumns(response.RankedNames, request.Prefix);
+ std::ranges::move(std::move(replicatable), std::back_inserter(response.RankedNames));
+
+ ranking->CropToSortedPrefix(response.RankedNames, request.Constraints, request.Limit);
+
+ return response;
+ });
+ }
+
+ private:
+ static TVector<TGenericName> ReplicatableColumns(const TVector<TGenericName>& names, TStringBuf prefix) {
+ THashMap<TString, size_t> references;
+ for (const TGenericName& name : names) {
+ if (!std::holds_alternative<TColumnName>(name)) {
+ continue;
+ }
+
+ const TColumnName& column = std::get<TColumnName>(name);
+ if (column.TableAlias.empty()) {
+ continue;
+ }
+
+ references[column.Identifier] += 1;
+ }
+
+ TVector<TGenericName> replicatable;
+ for (auto& [column, count] : references) {
+ if (count != 1) {
+ continue;
+ }
+
+ // TODO(YQL-19747): introduce a single source of truth of filtration policy
+ if (!TCaseInsensitiveStringBuf(column).StartsWith(prefix)) {
+ continue;
+ }
+
+ TColumnName name;
+ name.Identifier = column;
+ replicatable.emplace_back(std::move(name));
+ }
+
+ return replicatable;
+ }
+
+ INameService::TPtr Origin_;
+ IRanking::TPtr Ranking_;
+ };
+
+ } // namespace
+
+ INameService::TPtr MakeColumnReplicatingService(INameService::TPtr origin, IRanking::TPtr ranking) {
+ return new TNameService(std::move(origin), std::move(ranking));
+ }
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/name/service/column/replicating.h b/yql/essentials/sql/v1/complete/name/service/column/replicating.h
new file mode 100644
index 00000000000..120d08cc264
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/name/service/column/replicating.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <yql/essentials/sql/v1/complete/name/service/ranking/ranking.h>
+#include <yql/essentials/sql/v1/complete/name/service/name_service.h>
+
+namespace NSQLComplete {
+
+ INameService::TPtr MakeColumnReplicatingService(INameService::TPtr origin, IRanking::TPtr ranking);
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/name/service/column/ya.make b/yql/essentials/sql/v1/complete/name/service/column/ya.make
index 64c11175e2f..26d6e7b8e2d 100644
--- a/yql/essentials/sql/v1/complete/name/service/column/ya.make
+++ b/yql/essentials/sql/v1/complete/name/service/column/ya.make
@@ -2,6 +2,7 @@ LIBRARY()
SRCS(
name_service.cpp
+ replicating.cpp
)
PEERDIR(
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 d8bcf5aaeb0..9b3d01e2fab 100644
--- a/yql/essentials/sql/v1/complete/name/service/name_service.h
+++ b/yql/essentials/sql/v1/complete/name/service/name_service.h
@@ -74,6 +74,7 @@ namespace NSQLComplete {
struct TColumnName: TIdentifier {
struct TConstraints {
+ TString TableAlias;
TVector<TAliased<TTableId>> Tables;
THashMap<TString, THashSet<TString>> WithoutByTableAlias;
};
diff --git a/yql/essentials/sql/v1/complete/name/service/ranking/ranking.cpp b/yql/essentials/sql/v1/complete/name/service/ranking/ranking.cpp
index 032b2a858f1..2e00224f768 100644
--- a/yql/essentials/sql/v1/complete/name/service/ranking/ranking.cpp
+++ b/yql/essentials/sql/v1/complete/name/service/ranking/ranking.cpp
@@ -59,7 +59,7 @@ namespace NSQLComplete {
return std::visit([this](const auto& name) -> size_t {
using T = std::decay_t<decltype(name)>;
- auto content = NormalizeName(ContentView(name));
+ TString content = NormalizeName(ContentView(name).first);
if constexpr (std::is_same_v<T, TKeyword>) {
if (auto weight = Frequency_.Keywords.FindPtr(content)) {
@@ -97,6 +97,10 @@ namespace NSQLComplete {
return std::numeric_limits<size_t>::max();
}
+ if constexpr (std::is_same_v<T, TBindingName>) {
+ return std::numeric_limits<size_t>::max() - 4;
+ }
+
if constexpr (std::is_same_v<T, TClusterName>) {
return std::numeric_limits<size_t>::max() - 8;
}
@@ -109,17 +113,20 @@ namespace NSQLComplete {
return std::numeric_limits<size_t>::max() - weight;
}
- TStringBuf ContentView(const TGenericName& name Y_LIFETIME_BOUND) const {
- return std::visit([](const auto& name) -> TStringBuf {
+ std::pair<TStringBuf, TStringBuf> ContentView(const TGenericName& name Y_LIFETIME_BOUND) const {
+ return std::visit([](const auto& name) -> std::pair<TStringBuf, TStringBuf> {
using T = std::decay_t<decltype(name)>;
if constexpr (std::is_base_of_v<TKeyword, T>) {
- return name.Content;
+ return {name.Content, ""};
+ }
+ if constexpr (std::is_base_of_v<TColumnName, T>) {
+ return {name.TableAlias, name.Identifier};
}
if constexpr (std::is_base_of_v<TIdentifier, T>) {
- return name.Identifier;
+ return {name.Identifier, ""};
}
if constexpr (std::is_base_of_v<TUnknownName, T>) {
- return name.Content;
+ return {name.Content, ""};
}
}, name);
}
diff --git a/yql/essentials/sql/v1/complete/sql_complete.cpp b/yql/essentials/sql/v1/complete/sql_complete.cpp
index 23b62f7d0a8..fc8db52af13 100644
--- a/yql/essentials/sql/v1/complete/sql_complete.cpp
+++ b/yql/essentials/sql/v1/complete/sql_complete.cpp
@@ -8,6 +8,7 @@
#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/column/name_service.h>
+#include <yql/essentials/sql/v1/complete/name/service/column/replicating.h>
#include <yql/essentials/sql/v1/complete/name/service/schema/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>
@@ -33,6 +34,7 @@ namespace NSQLComplete {
TSqlCompletionEngine(
TLexerSupplier lexer,
INameService::TPtr names,
+ IRanking::TPtr ranking,
TConfiguration configuration)
: Configuration_(std::move(configuration))
, SyntaxAnalysis_(MakeLocalSyntaxAnalysis(
@@ -42,7 +44,9 @@ namespace NSQLComplete {
Configuration_.ForcedPreviousByToken_))
, GlobalAnalysis_(MakeGlobalAnalysis())
, Names_(std::move(names))
+ , Ranking_(std::move(ranking))
{
+ Ranking_ = Ranking_ ? Ranking_ : MakeDummyRanking();
}
NThreading::TFuture<TCompletion>
@@ -87,7 +91,12 @@ namespace NSQLComplete {
children.emplace_back(Names_);
}
- return MakeUnionNameService(std::move(children), MakeDummyRanking())
+ INameService::TPtr service =
+ MakeColumnReplicatingService(
+ MakeUnionNameService(std::move(children), Ranking_),
+ Ranking_);
+
+ return service
->Lookup(std::move(request))
.Apply([this, input, local = std::move(local)](auto f) {
return ToCompletion(input, std::move(local), f.ExtractValue());
@@ -165,6 +174,7 @@ namespace NSQLComplete {
table = !table->empty() ? table : Nothing();
request.Constraints.Column = TColumnName::TConstraints();
+ request.Constraints.Column->TableAlias = local.Column->Table;
request.Constraints.Column->Tables =
TColumnContext(*global.Column).ExtractAliased(table).Tables;
request.Constraints.Column->WithoutByTableAlias = global.Column->WithoutByTableAlias;
@@ -231,14 +241,16 @@ namespace NSQLComplete {
ILocalSyntaxAnalysis::TPtr SyntaxAnalysis_;
IGlobalAnalysis::TPtr GlobalAnalysis_;
INameService::TPtr Names_;
+ IRanking::TPtr Ranking_;
};
ISqlCompletionEngine::TPtr MakeSqlCompletionEngine(
TLexerSupplier lexer,
INameService::TPtr names,
- TConfiguration configuration) {
+ TConfiguration configuration,
+ IRanking::TPtr ranking) {
return MakeHolder<TSqlCompletionEngine>(
- lexer, std::move(names), std::move(configuration));
+ lexer, std::move(names), std::move(ranking), std::move(configuration));
}
} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/sql_complete.h b/yql/essentials/sql/v1/complete/sql_complete.h
index a347134e2f5..8fb02c37b06 100644
--- a/yql/essentials/sql/v1/complete/sql_complete.h
+++ b/yql/essentials/sql/v1/complete/sql_complete.h
@@ -4,6 +4,7 @@
#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/ranking/ranking.h>
#include <yql/essentials/sql/v1/complete/name/service/name_service.h>
#include <yql/essentials/sql/v1/lexer/lexer.h>
@@ -70,6 +71,7 @@ namespace NSQLComplete {
ISqlCompletionEngine::TPtr MakeSqlCompletionEngine(
TLexerSupplier lexer,
INameService::TPtr names,
- TConfiguration configuration = {});
+ TConfiguration configuration = {},
+ IRanking::TPtr ranking = nullptr);
} // 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 7ee10977a6a..52eb2033717 100644
--- a/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
+++ b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
@@ -155,6 +155,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
TVector<TString> clusters(begin(clustersIt), end(clustersIt));
TFrequencyData frequency;
+ IRanking::TPtr ranking = MakeDefaultRanking(frequency);
TVector<INameService::TPtr> children = {
MakeStaticNameService(std::move(names), frequency),
@@ -166,10 +167,10 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
MakeClusterNameService(
MakeStaticClusterDiscovery(std::move(clusters)))),
};
- INameService::TPtr service = MakeUnionNameService(
- std::move(children), MakeDefaultRanking(frequency));
+ INameService::TPtr service = MakeUnionNameService(std::move(children), ranking);
- return MakeSqlCompletionEngine(std::move(lexer), std::move(service));
+ TConfiguration config;
+ return MakeSqlCompletionEngine(std::move(lexer), std::move(service), config, ranking);
}
TVector<TCandidate> Complete(ISqlCompletionEngine::TPtr& engine, TString sharped, TEnvironment env = {}) {
@@ -256,8 +257,8 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
};
TVector<TCandidate> expected = {
- {BindingName, "$cluster_name"},
{TableName, "`maxim`"},
+ {BindingName, "$cluster_name"},
{ClusterName, "example"},
{ClusterName, "loggy"},
{ClusterName, "saurus"},
@@ -761,8 +762,8 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
}
{
TVector<TCandidate> expected = {
- {BindingName, "$hello"},
{TableName, "`maxim`"},
+ {BindingName, "$hello"},
{ClusterName, "example"},
{ClusterName, "loggy"},
{ClusterName, "saurus"},
@@ -778,9 +779,9 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
}
{
TVector<TCandidate> expected = {
- {BindingName, "$action"},
{TableName, "`people`"},
{FolderName, "`yql/`", 1},
+ {BindingName, "$action"},
{ClusterName, "example"},
{ClusterName, "loggy"},
{ClusterName, "saurus"},
@@ -1259,17 +1260,19 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
}
{
TVector<TCandidate> expected = {
+ {ColumnName, "Age"},
+ {ColumnName, "Name"},
{ColumnName, "x.Age"},
{ColumnName, "x.Name"},
};
- UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, "SELECT # FROM example.`/people` AS x"), expected);
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, "SELECT # FROM example.`/people` AS x"), expected);
}
{
TVector<TCandidate> expected = {
{ColumnName, "Age"},
{ColumnName, "Name"},
};
- UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, "SELECT x.# FROM example.`/people` AS x"), expected);
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(3, engine, "SELECT x.# FROM example.`/people` AS x"), expected);
}
{
TVector<TCandidate> expected = {
@@ -1293,13 +1296,18 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
)";
TVector<TCandidate> expected = {
+ {ColumnName, "Age"},
+ {ColumnName, "Name"},
+ {ColumnName, "course"},
+ {ColumnName, "room"},
+ {ColumnName, "time"},
{ColumnName, "ep.Age"},
{ColumnName, "ep.Name"},
{ColumnName, "et.course"},
{ColumnName, "et.room"},
{ColumnName, "et.time"},
};
- UNIT_ASSERT_VALUES_EQUAL(CompleteTop(5, engine, query), expected);
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, query), expected);
}
}
@@ -1313,13 +1321,17 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
)";
TVector<TCandidate> expected = {
- {ColumnName, "et.Room"},
- {ColumnName, "et.time"},
+ {ColumnName, "Age"},
+ {ColumnName, "Name"},
+ {ColumnName, "Room"},
+ {ColumnName, "time"},
{ColumnName, "ep.Age"},
{ColumnName, "ep.Name"},
+ {ColumnName, "et.Room"},
+ {ColumnName, "et.time"},
{Keyword, "ALL"},
};
- UNIT_ASSERT_VALUES_EQUAL(CompleteTop(5, engine, query), expected);
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, query), expected);
}
{
TString query = R"(
@@ -1329,15 +1341,15 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
)";
TVector<TCandidate> expected = {
- {ColumnName, "y.course"},
{ColumnName, "x.course"},
{ColumnName, "x.room"},
- {ColumnName, "y.room"},
{ColumnName, "x.time"},
+ {ColumnName, "y.course"},
+ {ColumnName, "y.room"},
{ColumnName, "y.time"},
{Keyword, "ALL"},
};
- UNIT_ASSERT_VALUES_EQUAL(CompleteTop(7, engine, query), expected);
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, query), expected);
}
{
TString query = "SELECT # FROM (SELECT 1 AS x)";
@@ -1359,12 +1371,15 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
)";
TVector<TCandidate> expected = {
- {ColumnName, "ep.test"},
+ {ColumnName, "Age"},
+ {ColumnName, "Name"},
+ {ColumnName, "test"},
{ColumnName, "ep.Age"},
{ColumnName, "ep.Name"},
+ {ColumnName, "ep.test"},
{Keyword, "ALL"},
};
- UNIT_ASSERT_VALUES_EQUAL(CompleteTop(4, engine, query), expected);
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, query), expected);
}
{
TString query = R"(
@@ -1381,14 +1396,17 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
)";
TVector<TCandidate> expected = {
- {ColumnName, "x.Age"},
- {ColumnName, "x.Name"},
+ {ColumnName, "Age"},
+ {ColumnName, "room"},
+ {ColumnName, "time"},
{ColumnName, "ep.Name"},
{ColumnName, "ep.room"},
{ColumnName, "ep.time"},
+ {ColumnName, "x.Age"},
+ {ColumnName, "x.Name"},
{Keyword, "ALL"},
};
- UNIT_ASSERT_VALUES_EQUAL(CompleteTop(6, engine, query), expected);
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, query), expected);
}
}
@@ -1418,9 +1436,9 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
};
TVector<TCandidate> expected = {
- {BindingName, "$x"},
{ColumnName, "Age"},
{ColumnName, "Name"},
+ {BindingName, "$x"},
};
TEnvironment env = {
@@ -1536,11 +1554,12 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
)sql";
TVector<TCandidate> expected = {
+ {ColumnName, "room"},
+ {ColumnName, "query.room"},
{ColumnName, "roommate.Age"},
{ColumnName, "roommate.Name"},
- {ColumnName, "query.room"},
};
- UNIT_ASSERT_VALUES_EQUAL(CompleteTop(4, engine, query), expected);
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, query), expected);
}
Y_UNIT_TEST(QualifiedColumnAtWhere) {
@@ -1564,13 +1583,15 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
TString prefix = R"sql(SELECT * FROM example.`/people` AS x )sql";
TVector<TCandidate> expected = {
+ {ColumnName, "Age"},
+ {ColumnName, "Name"},
{ColumnName, "x.Age"},
{ColumnName, "x.Name"},
};
- UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, prefix + "WHERE #"), expected);
- UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, prefix + "GROUP BY #"), expected);
- UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, prefix + "HAVING #"), expected);
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, prefix + "WHERE #"), expected);
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, prefix + "GROUP BY #"), expected);
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, prefix + "HAVING #"), expected);
}
Y_UNIT_TEST(ColumnFromQuotedAlias) {
@@ -1579,14 +1600,23 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
TString query;
TVector<TCandidate> expected = {
+ {ColumnName, "Age"},
+ {ColumnName, "Name"},
{ColumnName, "`per son`.Age"},
{ColumnName, "`per son`.Name"},
};
query = R"sql(SELECT # FROM example.`/people` AS `per son`)sql";
- UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, query), expected);
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, query), expected);
+ }
+ {
+ TString query = R"sql(SELECT per# FROM example.`/people` AS `per son`)sql";
+
+ TVector<TCandidate> expected = {
+ {ColumnName, "`per son`.Age"},
+ {ColumnName, "`per son`.Name"},
+ };
- query = R"sql(SELECT per# FROM example.`/people` AS `per son`)sql";
UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, query), expected);
}
{
@@ -1640,11 +1670,10 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
prefix + R"sql(SELECT * FROM $source AS x FROM x.#)sql",
};
- // FIXME: change ranking.
TVector<TCandidate> expected = {
- {BindingName, "$source"},
{ColumnName, "Age"},
{ColumnName, "Name"},
+ {BindingName, "$source"},
};
UNIT_ASSERT_VALUES_EQUAL(CompleteTop(3, engine, input[0]), expected);
@@ -1667,6 +1696,82 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
UNIT_ASSERT_VALUES_EQUAL(Complete(engine, "SELECT `Y #` FROM saurus.maxim").size(), 0);
}
+ Y_UNIT_TEST(ColumnReplicationConflict) {
+ auto engine = MakeSqlCompletionEngineUT();
+
+ TString query = R"sql(
+ SELECT #
+ FROM (SELECT a, b) AS x
+ JOIN (SELECT a, b) AS y
+ )sql";
+
+ TVector<TCandidate> expected = {
+ {ColumnName, "x.a"},
+ {ColumnName, "x.b"},
+ {ColumnName, "y.a"},
+ {ColumnName, "y.b"},
+ };
+
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, query), expected);
+ }
+
+ Y_UNIT_TEST(ColumnReplication) {
+ auto engine = MakeSqlCompletionEngineUT();
+
+ TString query = R"sql(
+ SELECT #
+ FROM (SELECT a, b) AS x
+ JOIN (SELECT b, c) AS y
+ )sql";
+
+ TVector<TCandidate> expected = {
+ {ColumnName, "a"},
+ {ColumnName, "c"},
+ {ColumnName, "x.a"},
+ {ColumnName, "x.b"},
+ {ColumnName, "y.b"},
+ {ColumnName, "y.c"},
+ };
+
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, query), expected);
+ }
+
+ Y_UNIT_TEST(ColumnReplicationCaseSensivity) {
+ auto engine = MakeSqlCompletionEngineUT();
+
+ TString query = R"sql(
+ SELECT #
+ FROM (SELECT A, B) AS x
+ JOIN (SELECT a, b) AS y
+ )sql";
+
+ TVector<TCandidate> expected = {
+ {ColumnName, "A"},
+ {ColumnName, "B"},
+ {ColumnName, "a"},
+ {ColumnName, "b"},
+ };
+
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, query), expected);
+ }
+
+ Y_UNIT_TEST(ColumnReplicationFiltration) {
+ auto engine = MakeSqlCompletionEngineUT();
+
+ TVector<TString> queries = {
+ R"sql(SELECT x# FROM (SELECT XXX) AS xxx)sql",
+ R"sql(SELECT X# FROM (SELECT XXX) AS xxx)sql",
+ };
+
+ TVector<TCandidate> expected = {
+ {ColumnName, "XXX"},
+ {ColumnName, "xxx.XXX"},
+ };
+
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, queries[0]), expected);
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(expected.size(), engine, queries[1]), expected);
+ }
+
Y_UNIT_TEST(NoBindingAtQuoted) {
auto engine = MakeSqlCompletionEngineUT();