summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvitya-smirnov <[email protected]>2025-06-18 17:03:59 +0300
committervitya-smirnov <[email protected]>2025-06-18 17:40:32 +0300
commit0ac6c9eac8c5c9d71141af3c89f7cfc1b66a279e (patch)
tree54b115e79e90c7c8253dec042c81035e4c071cb8
parent93d0e40990c109589c2afd7e2758dc107064fa4e (diff)
YQL-19747: Support table aliases
commit_hash:6d67ec1fa5023083debd89aaa99950019ca37c90
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/column.cpp39
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/global.cpp15
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/global.h23
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/global_ut.cpp12
-rw-r--r--yql/essentials/sql/v1/complete/analysis/local/local.cpp17
-rw-r--r--yql/essentials/sql/v1/complete/analysis/local/local.h6
-rw-r--r--yql/essentials/sql/v1/complete/core/name.h8
-rw-r--r--yql/essentials/sql/v1/complete/name/service/name_service.h2
-rw-r--r--yql/essentials/sql/v1/complete/name/service/schema/name_service.cpp15
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete.cpp38
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete_ut.cpp21
11 files changed, 176 insertions, 20 deletions
diff --git a/yql/essentials/sql/v1/complete/analysis/global/column.cpp b/yql/essentials/sql/v1/complete/analysis/global/column.cpp
index 7e185697d3f..8eed8a8dfd9 100644
--- a/yql/essentials/sql/v1/complete/analysis/global/column.cpp
+++ b/yql/essentials/sql/v1/complete/analysis/global/column.cpp
@@ -56,6 +56,26 @@ namespace NSQLComplete {
class TInferenceVisitor: public SQLv1Antlr4BaseVisitor {
public:
+ std::any visitNamed_single_source(SQLv1::Named_single_sourceContext* ctx) override {
+ SQLv1::Single_sourceContext* singleSource = ctx->single_source();
+ if (singleSource == nullptr) {
+ return {};
+ }
+
+ std::any any = visit(singleSource);
+ if (!any.has_value()) {
+ return {};
+ }
+ TColumnContext context = std::move(std::any_cast<TColumnContext>(any));
+
+ TMaybe<TString> alias = GetAlias(ctx);
+ if (alias.Empty()) {
+ return context;
+ }
+
+ return Renamed(std::move(context), *alias);
+ }
+
std::any visitTable_ref(SQLv1::Table_refContext* ctx) override {
TString cluster = GetId(ctx->cluster_expr()).GetOrElse("");
@@ -66,10 +86,27 @@ namespace NSQLComplete {
return TColumnContext{
.Tables = {
- {.Cluster = std::move(cluster), .Path = std::move(*path)},
+ TTableId{std::move(cluster), std::move(*path)},
},
};
}
+
+ private:
+ TMaybe<TString> GetAlias(SQLv1::Named_single_sourceContext* ctx) const {
+ TMaybe<TString> alias = GetId(ctx->an_id());
+ alias = alias.Defined() ? alias : GetId(ctx->an_id_as_compat());
+ return alias;
+ }
+
+ TColumnContext Renamed(TColumnContext context, TString alias) {
+ Y_ENSURE(!alias.empty());
+
+ for (TAliased<TTableId>& table : context.Tables) {
+ table.Alias = alias;
+ }
+
+ return context;
+ }
};
class TVisitor: public TSQLv1NarrowingVisitor {
diff --git a/yql/essentials/sql/v1/complete/analysis/global/global.cpp b/yql/essentials/sql/v1/complete/analysis/global/global.cpp
index 7574b6bdfc0..f73cad926e3 100644
--- a/yql/essentials/sql/v1/complete/analysis/global/global.cpp
+++ b/yql/essentials/sql/v1/complete/analysis/global/global.cpp
@@ -15,6 +15,15 @@
namespace NSQLComplete {
+ TVector<TTableId> TColumnContext::TablesWithAlias(TStringBuf alias) const {
+ if (alias.empty()) {
+ return TVector<TTableId>(Tables.begin(), Tables.end());
+ }
+
+ auto filtered = NFuncTools::Filter([&](const auto& x) { return x.Alias == alias; }, Tables);
+ return TVector<TTableId>(filtered.begin(), filtered.end());
+ }
+
class TErrorStrategy: public antlr4::DefaultErrorStrategy {
public:
antlr4::Token* singleTokenDeletion(antlr4::Parser* /* recognizer */) override {
@@ -117,6 +126,12 @@ namespace NSQLComplete {
} // namespace NSQLComplete
template <>
+void Out<NSQLComplete::TAliased<NSQLComplete::TTableId>>(IOutputStream& out, const NSQLComplete::TAliased<NSQLComplete::TTableId>& value) {
+ Out<NSQLComplete::TTableId>(out, value);
+ out << " AS " << value.Alias;
+}
+
+template <>
void Out<NSQLComplete::TColumnContext>(IOutputStream& out, const NSQLComplete::TColumnContext& value) {
out << "TColumnContext { ";
out << "Tables: " << JoinSeq(", ", value.Tables);
diff --git a/yql/essentials/sql/v1/complete/analysis/global/global.h b/yql/essentials/sql/v1/complete/analysis/global/global.h
index 1ef1344e3c9..69eab86049d 100644
--- a/yql/essentials/sql/v1/complete/analysis/global/global.h
+++ b/yql/essentials/sql/v1/complete/analysis/global/global.h
@@ -8,6 +8,7 @@
#include <util/generic/maybe.h>
#include <util/generic/string.h>
#include <util/generic/vector.h>
+#include <util/generic/hash.h>
namespace NSQLComplete {
@@ -16,8 +17,28 @@ namespace NSQLComplete {
TString Cluster;
};
+ template <std::regular T>
+ 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) = default;
+ };
+
struct TColumnContext {
- TVector<TTableId> Tables;
+ TVector<TAliased<TTableId>> Tables;
+
+ TVector<TTableId> TablesWithAlias(TStringBuf alias) const;
friend bool operator==(const TColumnContext& lhs, const TColumnContext& rhs) = default;
};
diff --git a/yql/essentials/sql/v1/complete/analysis/global/global_ut.cpp b/yql/essentials/sql/v1/complete/analysis/global/global_ut.cpp
index c8222580cce..d8b19786c11 100644
--- a/yql/essentials/sql/v1/complete/analysis/global/global_ut.cpp
+++ b/yql/essentials/sql/v1/complete/analysis/global/global_ut.cpp
@@ -105,7 +105,7 @@ Y_UNIT_TEST_SUITE(GlobalAnalysisTests) {
TGlobalContext ctx = global->Analyze(SharpedInput(query), {});
- TColumnContext expected = {.Tables = {{"plato", "Input"}}};
+ TColumnContext expected = {.Tables = {TTableId{"plato", "Input"}}};
UNIT_ASSERT_VALUES_EQUAL(ctx.Column, expected);
}
{
@@ -113,7 +113,15 @@ Y_UNIT_TEST_SUITE(GlobalAnalysisTests) {
TGlobalContext ctx = global->Analyze(SharpedInput(query), {});
- TColumnContext expected = {.Tables = {{"plato", "//home/input"}}};
+ TColumnContext expected = {.Tables = {TTableId{"plato", "//home/input"}}};
+ UNIT_ASSERT_VALUES_EQUAL(ctx.Column, expected);
+ }
+ {
+ TString query = "SELECT # FROM plato.Input AS x";
+
+ TGlobalContext ctx = global->Analyze(SharpedInput(query), {});
+
+ TColumnContext expected = {.Tables = {TAliased<TTableId>("x", TTableId{"plato", "Input"})}};
UNIT_ASSERT_VALUES_EQUAL(ctx.Column, expected);
}
}
diff --git a/yql/essentials/sql/v1/complete/analysis/local/local.cpp b/yql/essentials/sql/v1/complete/analysis/local/local.cpp
index 13e2fce69ad..014ac61e803 100644
--- a/yql/essentials/sql/v1/complete/analysis/local/local.cpp
+++ b/yql/essentials/sql/v1/complete/analysis/local/local.cpp
@@ -107,7 +107,7 @@ namespace NSQLComplete {
result.Hint = HintMatch(candidates);
result.Object = ObjectMatch(context, candidates);
result.Cluster = ClusterMatch(context, candidates);
- result.Column = ColumnMatch(candidates);
+ result.Column = ColumnMatch(context, candidates);
result.Binding = BindingMatch(candidates);
return result;
@@ -322,8 +322,19 @@ namespace NSQLComplete {
return cluster;
}
- bool ColumnMatch(const TC3Candidates& candidates) const {
- return AnyOf(candidates.Rules, RuleAdapted(IsLikelyColumnStack));
+ TMaybe<TLocalSyntaxContext::TColumn> ColumnMatch(
+ const TCursorTokenContext& context, const TC3Candidates& candidates) const {
+ if (!AnyOf(candidates.Rules, RuleAdapted(IsLikelyColumnStack))) {
+ return Nothing();
+ }
+
+ TLocalSyntaxContext::TColumn column;
+ if (TMaybe<TRichParsedToken> begin;
+ (begin = context.MatchCursorPrefix({"ID_PLAIN", "DOT"})) ||
+ (begin = context.MatchCursorPrefix({"ID_PLAIN", "DOT", ""}))) {
+ column.Table = begin->Base->Content;
+ }
+ return column;
}
bool BindingMatch(const TC3Candidates& candidates) const {
diff --git a/yql/essentials/sql/v1/complete/analysis/local/local.h b/yql/essentials/sql/v1/complete/analysis/local/local.h
index cca182aacdb..c5d73d52018 100644
--- a/yql/essentials/sql/v1/complete/analysis/local/local.h
+++ b/yql/essentials/sql/v1/complete/analysis/local/local.h
@@ -48,6 +48,10 @@ namespace NSQLComplete {
}
};
+ struct TColumn {
+ TString Table;
+ };
+
TKeywords Keywords;
TMaybe<TPragma> Pragma;
bool Type = false;
@@ -55,7 +59,7 @@ namespace NSQLComplete {
TMaybe<THint> Hint;
TMaybe<TObject> Object;
TMaybe<TCluster> Cluster;
- bool Column = false;
+ TMaybe<TColumn> Column;
bool Binding = false;
TEditRange EditRange;
};
diff --git a/yql/essentials/sql/v1/complete/core/name.h b/yql/essentials/sql/v1/complete/core/name.h
index 3eac47f3871..380fabdf5a4 100644
--- a/yql/essentials/sql/v1/complete/core/name.h
+++ b/yql/essentials/sql/v1/complete/core/name.h
@@ -1,6 +1,7 @@
#pragma once
#include <util/generic/string.h>
+#include <util/generic/hash.h>
namespace NSQLComplete {
@@ -17,3 +18,10 @@ namespace NSQLComplete {
};
} // namespace NSQLComplete
+
+template <>
+struct THash<NSQLComplete::TTableId> {
+ inline size_t operator()(const NSQLComplete::TTableId& x) const {
+ return THash<std::tuple<TString, TString>>()(std::tie(x.Cluster, x.Path));
+ }
+};
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 5139b27dfa3..090b96c4022 100644
--- a/yql/essentials/sql/v1/complete/name/service/name_service.h
+++ b/yql/essentials/sql/v1/complete/name/service/name_service.h
@@ -70,6 +70,8 @@ namespace NSQLComplete {
struct TConstraints {
TVector<TTableId> Tables;
};
+
+ TTableId Table;
};
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 2970f06d7cd..47b8e4c4457 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
@@ -18,7 +18,7 @@ namespace NSQLComplete {
.Apply(ToListNameResponse);
}
- if (request.Constraints.Column) {
+ if (request.Constraints.Column && !request.Constraints.Column->Tables.empty()) {
Y_ENSURE(request.Constraints.Column->Tables.size() == 1, "Not Implemented");
TTableId table = request.Constraints.Column->Tables[0];
return Schema_
@@ -28,7 +28,9 @@ namespace NSQLComplete {
.ColumnPrefix = request.Prefix,
.ColumnsLimit = request.Limit,
})
- .Apply(ToDescribeNameResponse);
+ .Apply([table = std::move(table)](auto f) {
+ return ToDescribeNameResponse(std::move(f), std::move(table));
+ });
}
return NThreading::MakeFuture<TNameResponse>({});
@@ -96,13 +98,16 @@ namespace NSQLComplete {
return name;
}
- static TNameResponse ToDescribeNameResponse(NThreading::TFuture<TDescribeTableResponse> f) {
- TDescribeTableResponse table = f.ExtractValue();
+ static TNameResponse ToDescribeNameResponse(
+ NThreading::TFuture<TDescribeTableResponse> f,
+ TTableId table) {
+ TDescribeTableResponse info = f.ExtractValue();
TNameResponse response;
- for (TString& column : table.Columns) {
+ for (TString& column : info.Columns) {
TColumnName name;
name.Indentifier = std::move(column);
+ name.Table = table;
response.RankedNames.emplace_back(std::move(name));
}
return response;
diff --git a/yql/essentials/sql/v1/complete/sql_complete.cpp b/yql/essentials/sql/v1/complete/sql_complete.cpp
index 07e837ed3f4..36aecf06b20 100644
--- a/yql/essentials/sql/v1/complete/sql_complete.cpp
+++ b/yql/essentials/sql/v1/complete/sql_complete.cpp
@@ -68,8 +68,8 @@ namespace NSQLComplete {
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());
+ .Apply([this, input, context = std::move(context), global = std::move(global)](auto f) {
+ return ToCompletion(input, std::move(context), global, f.ExtractValue());
});
}
@@ -148,7 +148,7 @@ namespace NSQLComplete {
if (context.Column && global.Column) {
request.Constraints.Column = TColumnName::TConstraints();
- request.Constraints.Column->Tables = std::move(global.Column->Tables);
+ request.Constraints.Column->Tables = global.Column->TablesWithAlias(context.Column->Table);
}
return request;
@@ -157,10 +157,11 @@ 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)),
+ .Candidates = Convert(std::move(response.RankedNames), std::move(context), global),
};
if (response.NameHintLength) {
@@ -175,16 +176,33 @@ namespace NSQLComplete {
return completion;
}
- static TVector<TCandidate> Convert(TVector<TGenericName> names, TLocalSyntaxContext context) {
+ static TVector<TCandidate> Convert(
+ TVector<TGenericName> names,
+ TLocalSyntaxContext context,
+ const TGlobalContext& global) {
TVector<TCandidate> candidates;
candidates.reserve(names.size());
for (auto& name : names) {
- candidates.emplace_back(Convert(std::move(name), context));
+ candidates.emplace_back(Convert(std::move(name), context, global));
}
return candidates;
}
- static TCandidate Convert(TGenericName name, TLocalSyntaxContext& context) {
+ // 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();
+ });
+
return std::visit([&](auto&& name) -> TCandidate {
using T = std::decay_t<decltype(name)>;
@@ -241,6 +259,12 @@ namespace NSQLComplete {
}
if constexpr (std::is_base_of_v<TColumnName, T>) {
+ const TString& alias = aliasByTable.at(name.Table);
+ if (context.Column->Table.empty() && !alias.empty()) {
+ name.Indentifier.prepend('.');
+ name.Indentifier.prepend(alias);
+ }
+
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 1f29751d017..f78d2f54cb0 100644
--- a/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
+++ b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
@@ -1072,6 +1072,27 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
};
UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, "USE example; SELECT # FROM `/people`"), expected);
}
+ {
+ TVector<TCandidate> expected = {
+ {ColumnName, "x.age"},
+ {ColumnName, "x.name"},
+ };
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, "SELECT # FROM example.`/people` AS x"), expected);
+ }
+ { // It is parsed into ``` SELECT x.FROM example.`/people` AS x ```
+ TVector<TCandidate> expected = {};
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, "SELECT x.# FROM example.`/people` AS x"), expected);
+ }
+ {
+ TVector<TCandidate> expected = {
+ {ColumnName, "age"},
+ };
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, "SELECT x.a# FROM example.`/people` AS x"), expected);
+ }
+ {
+ TVector<TCandidate> expected = {};
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, "SELECT y.a# FROM example.`/people` AS x"), expected);
+ }
}
Y_UNIT_TEST(Typing) {