summaryrefslogtreecommitdiffstats
path: root/yql/essentials/sql
diff options
context:
space:
mode:
authorvitya-smirnov <[email protected]>2025-06-25 11:16:34 +0300
committervitya-smirnov <[email protected]>2025-06-25 11:58:48 +0300
commit4eb432c5aeea350c39ca2d2708b0e351dc31c42b (patch)
treecd90f145da50f3bca595ba71716b31f906fdc45e /yql/essentials/sql
parentb55241a32a513cefebe8f89003917ec3f1ef6232 (diff)
YQL-19747: Fix self-join completion
Fixed a bug on self-join, as table path was used as a key to match columns with tables instead of an alias. commit_hash:0f9735a4c5ba0b2b88efc764bc5e7f5d41633fd8
Diffstat (limited to 'yql/essentials/sql')
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/global.cpp6
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/global.h26
-rw-r--r--yql/essentials/sql/v1/complete/core/name.h24
-rw-r--r--yql/essentials/sql/v1/complete/name/service/column/name_service.cpp2
-rw-r--r--yql/essentials/sql/v1/complete/name/service/name_service.h4
-rw-r--r--yql/essentials/sql/v1/complete/name/service/schema/name_service.cpp52
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete.cpp38
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete_ut.cpp18
8 files changed, 90 insertions, 80 deletions
diff --git a/yql/essentials/sql/v1/complete/analysis/global/global.cpp b/yql/essentials/sql/v1/complete/analysis/global/global.cpp
index 74f9b6909e8..191814a8e55 100644
--- a/yql/essentials/sql/v1/complete/analysis/global/global.cpp
+++ b/yql/essentials/sql/v1/complete/analysis/global/global.cpp
@@ -19,13 +19,13 @@ namespace NSQLComplete {
return std::tie(lhs.TableAlias, lhs.Name) < std::tie(rhs.TableAlias, rhs.Name);
}
- TVector<TTableId> TColumnContext::TablesWithAlias(TStringBuf alias) const {
+ TVector<TAliased<TTableId>> TColumnContext::TablesWithAlias(TStringBuf alias) const {
if (alias.empty()) {
- return TVector<TTableId>(Tables.begin(), Tables.end());
+ return TVector<TAliased<TTableId>>(Tables.begin(), Tables.end());
}
auto filtered = NFuncTools::Filter([&](const auto& x) { return x.Alias == alias; }, Tables);
- return TVector<TTableId>(filtered.begin(), filtered.end());
+ return TVector<TAliased<TTableId>>(filtered.begin(), filtered.end());
}
bool TColumnContext::IsAsterisk() const {
diff --git a/yql/essentials/sql/v1/complete/analysis/global/global.h b/yql/essentials/sql/v1/complete/analysis/global/global.h
index 972b2caa609..98445540b31 100644
--- a/yql/essentials/sql/v1/complete/analysis/global/global.h
+++ b/yql/essentials/sql/v1/complete/analysis/global/global.h
@@ -17,30 +17,6 @@ namespace NSQLComplete {
TString Cluster;
};
- template <class T>
- requires std::regular<T> &&
- requires(T x) { {x < x} -> std::convertible_to<bool>; }
- struct TAliased: T {
- TString Alias;
-
- TAliased(TString alias, T value)
- : T(std::move(value))
- , Alias(std::move(alias))
- {
- }
-
- TAliased(T value)
- : T(std::move(value))
- {
- }
-
- friend bool operator<(const TAliased& lhs, const TAliased& rhs) {
- return std::tie(lhs.Alias, static_cast<const T&>(lhs)) < std::tie(rhs.Alias, static_cast<const T&>(rhs));
- }
-
- friend bool operator==(const TAliased& lhs, const TAliased& rhs) = default;
- };
-
struct TColumnId {
TString TableAlias;
TString Name;
@@ -53,7 +29,7 @@ namespace NSQLComplete {
TVector<TAliased<TTableId>> Tables;
TVector<TColumnId> Columns;
- TVector<TTableId> TablesWithAlias(TStringBuf alias) const;
+ TVector<TAliased<TTableId>> TablesWithAlias(TStringBuf alias) const;
bool IsAsterisk() const;
TColumnContext Renamed(TStringBuf alias) &&;
diff --git a/yql/essentials/sql/v1/complete/core/name.h b/yql/essentials/sql/v1/complete/core/name.h
index 4765cedc315..55c80fa6f98 100644
--- a/yql/essentials/sql/v1/complete/core/name.h
+++ b/yql/essentials/sql/v1/complete/core/name.h
@@ -18,6 +18,30 @@ namespace NSQLComplete {
friend bool operator==(const TTableId& lhs, const TTableId& rhs) = default;
};
+ template <class T>
+ requires std::regular<T> &&
+ requires(T x) { {x < x} -> std::convertible_to<bool>; }
+ struct TAliased: T {
+ TString Alias;
+
+ TAliased(TString alias, T value)
+ : T(std::move(value))
+ , Alias(std::move(alias))
+ {
+ }
+
+ TAliased(T value)
+ : T(std::move(value))
+ {
+ }
+
+ friend bool operator<(const TAliased& lhs, const TAliased& rhs) {
+ return std::tie(lhs.Alias, static_cast<const T&>(lhs)) < std::tie(rhs.Alias, static_cast<const T&>(rhs));
+ }
+
+ friend bool operator==(const TAliased& lhs, const TAliased& rhs) = default;
+ };
+
} // namespace NSQLComplete
template <>
diff --git a/yql/essentials/sql/v1/complete/name/service/column/name_service.cpp b/yql/essentials/sql/v1/complete/name/service/column/name_service.cpp
index 9b87f5fe5e1..7c81be5a37d 100644
--- a/yql/essentials/sql/v1/complete/name/service/column/name_service.cpp
+++ b/yql/essentials/sql/v1/complete/name/service/column/name_service.cpp
@@ -44,7 +44,7 @@ namespace NSQLComplete {
Y_ENSURE(table.IsExisting);
for (TString& column : table.Columns) {
TColumnName name;
- name.Table = {.Cluster = "", .Path = tableName};
+ name.TableAlias = tableName;
name.Indentifier = std::move(column);
response.RankedNames.emplace_back(std::move(name));
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 63fc5cbdac8..e4971b86250 100644
--- a/yql/essentials/sql/v1/complete/name/service/name_service.h
+++ b/yql/essentials/sql/v1/complete/name/service/name_service.h
@@ -69,10 +69,10 @@ namespace NSQLComplete {
struct TColumnName: TIndentifier {
struct TConstraints {
- TVector<TTableId> Tables;
+ TVector<TAliased<TTableId>> Tables;
};
- TTableId Table;
+ TString TableAlias;
};
struct TBindingName: TIndentifier {
diff --git a/yql/essentials/sql/v1/complete/name/service/schema/name_service.cpp b/yql/essentials/sql/v1/complete/name/service/schema/name_service.cpp
index 5adff4a67a3..736f7c3090e 100644
--- a/yql/essentials/sql/v1/complete/name/service/schema/name_service.cpp
+++ b/yql/essentials/sql/v1/complete/name/service/schema/name_service.cpp
@@ -1,7 +1,7 @@
#include "name_service.h"
#include <library/cpp/threading/future/wait/wait.h>
-#include <library/cpp/iterator/functools.h>
+#include <library/cpp/iterator/iterate_values.h>
namespace NSQLComplete {
@@ -33,34 +33,48 @@ namespace NSQLComplete {
private:
NThreading::TFuture<TNameResponse> BatchDescribe(
- TVector<TTableId> tables, TString prefix, ui64 limit) const {
- TVector<NThreading::TFuture<TDescribeTableResponse>> futures;
- for (const auto& table : tables) {
+ TVector<TAliased<TTableId>> tables, TString prefix, ui64 limit) const {
+ THashMap<TTableId, TVector<TString>> aliasesByTable;
+ for (TAliased<TTableId> table : std::move(tables)) {
+ aliasesByTable[std::move(static_cast<TTableId&>(table))]
+ .emplace_back(std::move(table.Alias));
+ }
+
+ THashMap<TTableId, NThreading::TFuture<TDescribeTableResponse>> futuresByTable;
+ for (const auto& [table, _] : aliasesByTable) {
TDescribeTableRequest request = {
.TableCluster = table.Cluster,
.TablePath = table.Path,
.ColumnPrefix = prefix,
.ColumnsLimit = limit,
};
- futures.emplace_back(Schema_->Describe(request));
- }
- return NThreading::WaitAll(futures).Apply([tables, futures](auto) mutable {
- TNameResponse response;
-
- for (auto [table, f] : NFuncTools::Zip(tables, futures)) {
- TDescribeTableResponse description = f.ExtractValue();
- for (TString& column : description.Columns) {
- TColumnName name;
- name.Indentifier = std::move(column);
- name.Table = table;
+ futuresByTable.emplace(table, Schema_->Describe(request));
+ }
- response.RankedNames.emplace_back(std::move(name));
+ auto futuresIt = IterateValues(futuresByTable);
+ TVector<NThreading::TFuture<TDescribeTableResponse>> futures(begin(futuresIt), end(futuresIt));
+
+ return NThreading::WaitAll(std::move(futures))
+ .Apply([aliasesByTable = std::move(aliasesByTable),
+ futuresByTable = std::move(futuresByTable)](auto) mutable {
+ TNameResponse response;
+
+ for (auto [table, f] : futuresByTable) {
+ TDescribeTableResponse description = f.ExtractValue();
+ for (const TString& column : description.Columns) {
+ for (const TString& alias : aliasesByTable[table]) {
+ TColumnName name;
+ name.Indentifier = column;
+ name.TableAlias = alias;
+
+ response.RankedNames.emplace_back(std::move(name));
+ }
+ }
}
- }
- return response;
- });
+ return response;
+ });
}
static TListRequest ToListRequest(TNameRequest request) {
diff --git a/yql/essentials/sql/v1/complete/sql_complete.cpp b/yql/essentials/sql/v1/complete/sql_complete.cpp
index 2cb88c714a7..7cb9788be5b 100644
--- a/yql/essentials/sql/v1/complete/sql_complete.cpp
+++ b/yql/essentials/sql/v1/complete/sql_complete.cpp
@@ -77,8 +77,8 @@ namespace NSQLComplete {
return MakeUnionNameService(std::move(children), MakeDummyRanking())
->Lookup(std::move(request))
- .Apply([this, input, context = std::move(context), global = std::move(global)](auto f) {
- return ToCompletion(input, std::move(context), global, f.ExtractValue());
+ .Apply([this, input, context = std::move(context)](auto f) {
+ return ToCompletion(input, std::move(context), f.ExtractValue());
});
}
@@ -166,11 +166,10 @@ namespace NSQLComplete {
TCompletion ToCompletion(
TCompletionInput input,
TLocalSyntaxContext context,
- const TGlobalContext& global,
TNameResponse response) const {
TCompletion completion = {
.CompletedToken = GetCompletedToken(input, context.EditRange),
- .Candidates = Convert(std::move(response.RankedNames), std::move(context), global),
+ .Candidates = Convert(std::move(response.RankedNames), std::move(context)),
};
if (response.NameHintLength) {
@@ -185,33 +184,17 @@ namespace NSQLComplete {
return completion;
}
- static TVector<TCandidate> Convert(
- TVector<TGenericName> names,
- TLocalSyntaxContext context,
- const TGlobalContext& global) {
+ static TVector<TCandidate> Convert(TVector<TGenericName> names, TLocalSyntaxContext context) {
TVector<TCandidate> candidates;
candidates.reserve(names.size());
for (auto& name : names) {
- candidates.emplace_back(Convert(std::move(name), context, global));
+ candidates.emplace_back(Convert(std::move(name), context));
}
return candidates;
}
// TODO(YQL-19747): extract to a separate file
- static TCandidate Convert(
- TGenericName name,
- TLocalSyntaxContext& context,
- const TGlobalContext& global) {
- // TODO(YQL-19747): support multiple aliases for a single table
- THashMap<TTableId, TString> aliasByTable;
- global.Column.Transform([&](auto&& column) {
- aliasByTable.reserve(column.Tables.size());
- for (const auto& table : column.Tables) {
- aliasByTable[table] = table.Alias;
- }
- return std::monostate();
- });
-
+ static TCandidate Convert(TGenericName name, TLocalSyntaxContext& context) {
return std::visit([&](auto&& name) -> TCandidate {
using T = std::decay_t<decltype(name)>;
@@ -309,14 +292,9 @@ namespace NSQLComplete {
}
if constexpr (std::is_base_of_v<TColumnName, T>) {
- TString alias = name.Table.Path;
- if (auto it = aliasByTable.find(name.Table); it != end(aliasByTable)) {
- alias = it->second;
- }
-
- if (context.Column->Table.empty() && !alias.empty()) {
+ if (context.Column->Table.empty() && !name.TableAlias.empty()) {
name.Indentifier.prepend('.');
- name.Indentifier.prepend(alias);
+ name.Indentifier.prepend(name.TableAlias);
}
return {ECandidateKind::ColumnName, 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 603ddae812a..5b99c6a3fe1 100644
--- a/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
+++ b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
@@ -1160,6 +1160,24 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
UNIT_ASSERT_VALUES_EQUAL(CompleteTop(5, engine, query), expected);
}
{
+ TString query = R"(
+ SELECT #
+ FROM example.`/yql/tutorial` AS x
+ JOIN example.`/yql/tutorial` AS y ON 1 = 1
+ )";
+
+ TVector<TCandidate> expected = {
+ {ColumnName, "y.course"},
+ {ColumnName, "x.course"},
+ {ColumnName, "x.room"},
+ {ColumnName, "y.room"},
+ {ColumnName, "x.time"},
+ {ColumnName, "y.time"},
+ {Keyword, "ALL"},
+ };
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(7, engine, query), expected);
+ }
+ {
TString query = "SELECT # FROM (SELECT 1 AS x)";
TVector<TCandidate> expected = {