diff options
| author | vitya-smirnov <[email protected]> | 2025-06-25 11:16:34 +0300 |
|---|---|---|
| committer | vitya-smirnov <[email protected]> | 2025-06-25 11:58:48 +0300 |
| commit | 4eb432c5aeea350c39ca2d2708b0e351dc31c42b (patch) | |
| tree | cd90f145da50f3bca595ba71716b31f906fdc45e /yql/essentials/sql | |
| parent | b55241a32a513cefebe8f89003917ec3f1ef6232 (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')
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 = { |
