summaryrefslogtreecommitdiffstats
path: root/yql/essentials/sql
diff options
context:
space:
mode:
authorvitya-smirnov <[email protected]>2025-06-17 12:45:20 +0300
committervitya-smirnov <[email protected]>2025-06-17 13:12:27 +0300
commit633ab50dd51d6f17810f71559ccf6e5bfebe2044 (patch)
treeebb85b4dff775656e3b5ee1937c1ced01db0f853 /yql/essentials/sql
parentbacec6cbd87d14bf55c256f17797537ae8c2bbed (diff)
YQL-19747: Complete columns at simple select
Added support for a columns completion at a simple select. For example, `` SELECT # FROM hahn.`home/yql/tutorial/users` `` и `` USE hahn; SELECT $ FROM `home/yql/tutorial/users` ``. commit_hash:2254449e91255c19792a1dc521825e44dda7d63b
Diffstat (limited to 'yql/essentials/sql')
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/column.cpp116
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/column.h13
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/global.cpp25
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/global.h10
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/global_ut.cpp20
-rw-r--r--yql/essentials/sql/v1/complete/analysis/global/ya.make1
-rw-r--r--yql/essentials/sql/v1/complete/analysis/local/local.cpp5
-rw-r--r--yql/essentials/sql/v1/complete/analysis/local/local.h1
-rw-r--r--yql/essentials/sql/v1/complete/analysis/local/parser_call_stack.cpp4
-rw-r--r--yql/essentials/sql/v1/complete/analysis/local/parser_call_stack.h2
-rw-r--r--yql/essentials/sql/v1/complete/core/name.cpp8
-rw-r--r--yql/essentials/sql/v1/complete/core/name.h9
-rw-r--r--yql/essentials/sql/v1/complete/core/statement.cpp1
-rw-r--r--yql/essentials/sql/v1/complete/core/ya.make2
-rw-r--r--yql/essentials/sql/v1/complete/name/object/schema.h16
-rw-r--r--yql/essentials/sql/v1/complete/name/object/simple/schema.cpp63
-rw-r--r--yql/essentials/sql/v1/complete/name/object/simple/schema.h7
-rw-r--r--yql/essentials/sql/v1/complete/name/object/simple/static/schema.cpp45
-rw-r--r--yql/essentials/sql/v1/complete/name/object/simple/static/schema.h3
-rw-r--r--yql/essentials/sql/v1/complete/name/service/name_service.h11
-rw-r--r--yql/essentials/sql/v1/complete/name/service/ranking/ranking.cpp3
-rw-r--r--yql/essentials/sql/v1/complete/name/service/schema/name_service.cpp37
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete.cpp12
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete.h1
-rw-r--r--yql/essentials/sql/v1/complete/sql_complete_ut.cpp35
-rw-r--r--yql/essentials/sql/v1/complete/syntax/ya.make1
26 files changed, 421 insertions, 30 deletions
diff --git a/yql/essentials/sql/v1/complete/analysis/global/column.cpp b/yql/essentials/sql/v1/complete/analysis/global/column.cpp
new file mode 100644
index 00000000000..7e185697d3f
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/analysis/global/column.cpp
@@ -0,0 +1,116 @@
+#include "column.h"
+
+#include "narrowing_visitor.h"
+
+#include <yql/essentials/sql/v1/complete/syntax/format.h>
+
+namespace NSQLComplete {
+
+ namespace {
+
+ // TODO: Extract it to `identifier.cpp` and reuse it also at `use.cpp`
+ // and replace `GetId` at `parse_tree.cpp`.
+ class TIdentifierVisitor: public SQLv1Antlr4BaseVisitor {
+ public:
+ std::any visitCluster_expr(SQLv1::Cluster_exprContext* ctx) override {
+ if (auto* x = ctx->pure_column_or_named()) {
+ return visit(x);
+ }
+ return {};
+ }
+
+ std::any visitTable_key(SQLv1::Table_keyContext* ctx) override {
+ if (auto* x = ctx->id_table_or_type()) {
+ return visit(x);
+ }
+ return {};
+ }
+
+ std::any visitTerminal(antlr4::tree::TerminalNode* node) override {
+ TString text = GetText(node);
+ switch (node->getSymbol()->getType()) {
+ case SQLv1::TOKEN_ID_QUOTED: {
+ text = Unquoted(std::move(text));
+ } break;
+ }
+ return text;
+ }
+
+ private:
+ TString GetText(antlr4::tree::ParseTree* tree) const {
+ return TString(tree->getText());
+ }
+ };
+
+ TMaybe<TString> GetId(antlr4::ParserRuleContext* ctx) {
+ if (ctx == nullptr) {
+ return Nothing();
+ }
+
+ std::any result = TIdentifierVisitor().visit(ctx);
+ if (!result.has_value()) {
+ return Nothing();
+ }
+ return std::any_cast<TString>(result);
+ }
+
+ class TInferenceVisitor: public SQLv1Antlr4BaseVisitor {
+ public:
+ std::any visitTable_ref(SQLv1::Table_refContext* ctx) override {
+ TString cluster = GetId(ctx->cluster_expr()).GetOrElse("");
+
+ TMaybe<TString> path = GetId(ctx->table_key());
+ if (path.Empty()) {
+ return {};
+ }
+
+ return TColumnContext{
+ .Tables = {
+ {.Cluster = std::move(cluster), .Path = std::move(*path)},
+ },
+ };
+ }
+ };
+
+ class TVisitor: public TSQLv1NarrowingVisitor {
+ public:
+ TVisitor(antlr4::TokenStream* tokens, size_t cursorPosition)
+ : TSQLv1NarrowingVisitor(tokens, cursorPosition)
+ {
+ }
+
+ std::any visitSql_stmt_core(SQLv1::Sql_stmt_coreContext* ctx) override {
+ if (IsEnclosing(ctx)) {
+ return visitChildren(ctx);
+ }
+ return {};
+ }
+
+ std::any visitSelect_core(SQLv1::Select_coreContext* ctx) override {
+ SQLv1::Join_sourceContext* source = ctx->join_source(0);
+ if (source == nullptr) {
+ source = ctx->join_source(1);
+ }
+ if (source == nullptr) {
+ return {};
+ }
+
+ return TInferenceVisitor().visit(ctx);
+ }
+ };
+
+ } // namespace
+
+ TMaybe<TColumnContext> InferColumnContext(
+ SQLv1::Sql_queryContext* ctx,
+ antlr4::TokenStream* tokens,
+ size_t cursorPosition) {
+ // TODO: add utility `auto ToMaybe<T>(std::any any) -> TMaybe<T>`
+ std::any result = TVisitor(tokens, cursorPosition).visit(ctx);
+ if (!result.has_value()) {
+ return Nothing();
+ }
+ return std::any_cast<TColumnContext>(result);
+ }
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/analysis/global/column.h b/yql/essentials/sql/v1/complete/analysis/global/column.h
new file mode 100644
index 00000000000..790dbee1d15
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/analysis/global/column.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "global.h"
+#include "parse_tree.h"
+
+namespace NSQLComplete {
+
+ TMaybe<TColumnContext> InferColumnContext(
+ SQLv1::Sql_queryContext* ctx,
+ antlr4::TokenStream* tokens,
+ size_t cursorPosition);
+
+} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/analysis/global/global.cpp b/yql/essentials/sql/v1/complete/analysis/global/global.cpp
index 8f5df3f6e76..7574b6bdfc0 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 "column.h"
#include "function.h"
#include "named_node.h"
#include "parse_tree.h"
@@ -8,6 +9,10 @@
#include <yql/essentials/sql/v1/complete/antlr4/pipeline.h>
#include <yql/essentials/sql/v1/complete/syntax/ansi.h>
+#include <library/cpp/iterator/functools.h>
+
+#include <util/string/join.h>
+
namespace NSQLComplete {
class TErrorStrategy: public antlr4::DefaultErrorStrategy {
@@ -54,6 +59,11 @@ namespace NSQLComplete {
ctx.Use = FindUseStatement(sqlQuery, &Tokens_, input.CursorPosition, env);
ctx.Names = CollectNamedNodes(sqlQuery, &Tokens_, input.CursorPosition);
ctx.EnclosingFunction = EnclosingFunction(sqlQuery, &Tokens_, input.CursorPosition);
+ ctx.Column = InferColumnContext(sqlQuery, &Tokens_, input.CursorPosition);
+
+ if (ctx.Use && ctx.Column) {
+ EnrichTableClusters(*ctx.Column, *ctx.Use);
+ }
return ctx;
}
@@ -67,6 +77,14 @@ namespace NSQLComplete {
return Parser_.sql_query();
}
+ void EnrichTableClusters(TColumnContext& column, const TUseContext& use) {
+ for (auto& table : column.Tables) {
+ if (table.Cluster.empty()) {
+ table.Cluster = use.Cluster;
+ }
+ }
+ }
+
antlr4::ANTLRInputStream Chars_;
G::TLexer Lexer_;
antlr4::CommonTokenStream Tokens_;
@@ -97,3 +115,10 @@ namespace NSQLComplete {
}
} // namespace NSQLComplete
+
+template <>
+void Out<NSQLComplete::TColumnContext>(IOutputStream& out, const NSQLComplete::TColumnContext& value) {
+ out << "TColumnContext { ";
+ out << "Tables: " << JoinSeq(", ", value.Tables);
+ out << " }";
+}
diff --git a/yql/essentials/sql/v1/complete/analysis/global/global.h b/yql/essentials/sql/v1/complete/analysis/global/global.h
index 3999aeb4a56..fe929bf77c7 100644
--- a/yql/essentials/sql/v1/complete/analysis/global/global.h
+++ b/yql/essentials/sql/v1/complete/analysis/global/global.h
@@ -1,7 +1,8 @@
#pragma once
-#include <yql/essentials/sql/v1/complete/core/input.h>
#include <yql/essentials/sql/v1/complete/core/environment.h>
+#include <yql/essentials/sql/v1/complete/core/input.h>
+#include <yql/essentials/sql/v1/complete/core/name.h>
#include <util/generic/ptr.h>
#include <util/generic/maybe.h>
@@ -15,10 +16,17 @@ namespace NSQLComplete {
TString Cluster;
};
+ struct TColumnContext {
+ TVector<TTableId> Tables;
+
+ friend bool operator==(const TColumnContext& lhs, const TColumnContext& rhs) = default;
+ };
+
struct TGlobalContext {
TMaybe<TUseContext> Use;
TVector<TString> Names;
TMaybe<TString> EnclosingFunction;
+ TMaybe<TColumnContext> Column;
};
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
index 58aea33b379..c8222580cce 100644
--- a/yql/essentials/sql/v1/complete/analysis/global/global_ut.cpp
+++ b/yql/essentials/sql/v1/complete/analysis/global/global_ut.cpp
@@ -98,4 +98,24 @@ Y_UNIT_TEST_SUITE(GlobalAnalysisTests) {
}
}
+ Y_UNIT_TEST(SimpleSelectFrom) {
+ IGlobalAnalysis::TPtr global = MakeGlobalAnalysis();
+ {
+ TString query = "SELECT # FROM plato.Input";
+
+ TGlobalContext ctx = global->Analyze(SharpedInput(query), {});
+
+ TColumnContext expected = {.Tables = {{"plato", "Input"}}};
+ UNIT_ASSERT_VALUES_EQUAL(ctx.Column, expected);
+ }
+ {
+ TString query = "SELECT # FROM plato.`//home/input`";
+
+ TGlobalContext ctx = global->Analyze(SharpedInput(query), {});
+
+ TColumnContext expected = {.Tables = {{"plato", "//home/input"}}};
+ UNIT_ASSERT_VALUES_EQUAL(ctx.Column, expected);
+ }
+ }
+
} // Y_UNIT_TEST_SUITE(GlobalAnalysisTests)
diff --git a/yql/essentials/sql/v1/complete/analysis/global/ya.make b/yql/essentials/sql/v1/complete/analysis/global/ya.make
index b37fa3faa22..1f1670a9675 100644
--- a/yql/essentials/sql/v1/complete/analysis/global/ya.make
+++ b/yql/essentials/sql/v1/complete/analysis/global/ya.make
@@ -1,6 +1,7 @@
LIBRARY()
SRCS(
+ column.cpp
evaluate.cpp
function.cpp
global.cpp
diff --git a/yql/essentials/sql/v1/complete/analysis/local/local.cpp b/yql/essentials/sql/v1/complete/analysis/local/local.cpp
index b7e97b51e59..1b732ae5db7 100644
--- a/yql/essentials/sql/v1/complete/analysis/local/local.cpp
+++ b/yql/essentials/sql/v1/complete/analysis/local/local.cpp
@@ -104,6 +104,7 @@ namespace NSQLComplete {
result.Hint = HintMatch(candidates);
result.Object = ObjectMatch(context, candidates);
result.Cluster = ClusterMatch(context, candidates);
+ result.Column = ColumnMatch(candidates);
result.Binding = BindingMatch(candidates);
return result;
@@ -296,6 +297,10 @@ namespace NSQLComplete {
return cluster;
}
+ bool ColumnMatch(const TC3Candidates& candidates) const {
+ return AnyOf(candidates.Rules, RuleAdapted(IsLikelyColumnStack));
+ }
+
bool BindingMatch(const TC3Candidates& candidates) const {
return AnyOf(candidates.Rules, RuleAdapted(IsLikelyBindingStack));
}
diff --git a/yql/essentials/sql/v1/complete/analysis/local/local.h b/yql/essentials/sql/v1/complete/analysis/local/local.h
index 5557d6b6dc0..6cf6fc33c51 100644
--- a/yql/essentials/sql/v1/complete/analysis/local/local.h
+++ b/yql/essentials/sql/v1/complete/analysis/local/local.h
@@ -55,6 +55,7 @@ namespace NSQLComplete {
TMaybe<THint> Hint;
TMaybe<TObject> Object;
TMaybe<TCluster> Cluster;
+ bool Column = false;
bool Binding = false;
TEditRange EditRange;
};
diff --git a/yql/essentials/sql/v1/complete/analysis/local/parser_call_stack.cpp b/yql/essentials/sql/v1/complete/analysis/local/parser_call_stack.cpp
index 9837e404bd6..8ac4ff17999 100644
--- a/yql/essentials/sql/v1/complete/analysis/local/parser_call_stack.cpp
+++ b/yql/essentials/sql/v1/complete/analysis/local/parser_call_stack.cpp
@@ -123,6 +123,10 @@ namespace NSQLComplete {
return Contains({RULE(Cluster_expr)}, stack);
}
+ bool IsLikelyColumnStack(const TParserCallStack& stack) {
+ return Contains({RULE(Result_column)}, stack);
+ }
+
bool IsLikelyBindingStack(const TParserCallStack& stack) {
return EndsWith({RULE(Bind_parameter), RULE(An_id_or_type)}, stack);
}
diff --git a/yql/essentials/sql/v1/complete/analysis/local/parser_call_stack.h b/yql/essentials/sql/v1/complete/analysis/local/parser_call_stack.h
index c8daf9114fe..a0c479a8be0 100644
--- a/yql/essentials/sql/v1/complete/analysis/local/parser_call_stack.h
+++ b/yql/essentials/sql/v1/complete/analysis/local/parser_call_stack.h
@@ -21,6 +21,8 @@ namespace NSQLComplete {
bool IsLikelyClusterStack(const TParserCallStack& stack);
+ bool IsLikelyColumnStack(const TParserCallStack& stack);
+
bool IsLikelyBindingStack(const TParserCallStack& stack);
TMaybe<EStatementKind> StatementKindOf(const TParserCallStack& stack);
diff --git a/yql/essentials/sql/v1/complete/core/name.cpp b/yql/essentials/sql/v1/complete/core/name.cpp
new file mode 100644
index 00000000000..65c28e51176
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/core/name.cpp
@@ -0,0 +1,8 @@
+#include "name.h"
+
+#include <util/stream/output.h>
+
+template <>
+void Out<NSQLComplete::TTableId>(IOutputStream& out, const NSQLComplete::TTableId& value) {
+ out << value.Cluster << ".`" << value.Path << "`";
+}
diff --git a/yql/essentials/sql/v1/complete/core/name.h b/yql/essentials/sql/v1/complete/core/name.h
index 02524766de1..3eac47f3871 100644
--- a/yql/essentials/sql/v1/complete/core/name.h
+++ b/yql/essentials/sql/v1/complete/core/name.h
@@ -1,5 +1,7 @@
#pragma once
+#include <util/generic/string.h>
+
namespace NSQLComplete {
enum class EObjectKind {
@@ -7,4 +9,11 @@ namespace NSQLComplete {
Table,
};
+ struct TTableId {
+ TString Cluster;
+ TString Path;
+
+ friend bool operator==(const TTableId& lhs, const TTableId& rhs) = default;
+ };
+
} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/core/statement.cpp b/yql/essentials/sql/v1/complete/core/statement.cpp
new file mode 100644
index 00000000000..c93e5a7f803
--- /dev/null
+++ b/yql/essentials/sql/v1/complete/core/statement.cpp
@@ -0,0 +1 @@
+#include "statement.h"
diff --git a/yql/essentials/sql/v1/complete/core/ya.make b/yql/essentials/sql/v1/complete/core/ya.make
index 599810301ca..0cd0776b11e 100644
--- a/yql/essentials/sql/v1/complete/core/ya.make
+++ b/yql/essentials/sql/v1/complete/core/ya.make
@@ -3,6 +3,8 @@ LIBRARY()
SRCS(
environment.cpp
input.cpp
+ name.cpp
+ statement.cpp
)
PEERDIR(
diff --git a/yql/essentials/sql/v1/complete/name/object/schema.h b/yql/essentials/sql/v1/complete/name/object/schema.h
index b9aa720d36f..d779b293674 100644
--- a/yql/essentials/sql/v1/complete/name/object/schema.h
+++ b/yql/essentials/sql/v1/complete/name/object/schema.h
@@ -41,12 +41,26 @@ namespace NSQLComplete {
TVector<TFolderEntry> Entries;
};
+ struct TDescribeTableRequest {
+ TString TableCluster;
+ TString TablePath;
+ TString ColumnPrefix;
+ size_t ColumnsLimit = 128; // TODO: introduce default limit constant
+ };
+
+ struct TDescribeTableResponse {
+ bool IsExisting = false;
+ TVector<TString> Columns;
+ };
+
class ISchema: public TThrRefBase {
public:
using TPtr = TIntrusivePtr<ISchema>;
- ~ISchema() override = default;
virtual NThreading::TFuture<TListResponse> List(const TListRequest& request) const = 0;
+
+ virtual NThreading::TFuture<TDescribeTableResponse>
+ Describe(const TDescribeTableRequest& request) const = 0;
};
} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/name/object/simple/schema.cpp b/yql/essentials/sql/v1/complete/name/object/simple/schema.cpp
index a9dd20ada99..043464bd73e 100644
--- a/yql/essentials/sql/v1/complete/name/object/simple/schema.cpp
+++ b/yql/essentials/sql/v1/complete/name/object/simple/schema.cpp
@@ -8,7 +8,7 @@ namespace NSQLComplete {
class TSimpleSchema: public ISchema {
private:
- static auto FilterByName(TString name) {
+ static auto FilterEntriesByName(TString name) {
return [name = std::move(name)](auto f) {
TVector<TFolderEntry> entries = f.ExtractValue();
EraseIf(entries, [prefix = ToLowerUTF8(name)](const TFolderEntry& entry) {
@@ -18,7 +18,7 @@ namespace NSQLComplete {
};
}
- static auto FilterByTypes(TMaybe<THashSet<TString>> types) {
+ static auto FilterEntriesByTypes(TMaybe<THashSet<TString>> types) {
return [types = std::move(types)](auto f) mutable {
TVector<TFolderEntry> entries = f.ExtractValue();
EraseIf(entries, [types = std::move(types)](const TFolderEntry& entry) {
@@ -28,7 +28,7 @@ namespace NSQLComplete {
};
}
- static auto Crop(size_t limit) {
+ static auto CropEntries(size_t limit) {
return [limit](auto f) {
TVector<TFolderEntry> entries = f.ExtractValue();
entries.crop(limit);
@@ -36,7 +36,7 @@ namespace NSQLComplete {
};
}
- static auto ToResponse(TStringBuf name) {
+ static auto ToListResponse(TStringBuf name) {
const auto length = name.length();
return [length](auto f) {
return TListResponse{
@@ -46,6 +46,38 @@ namespace NSQLComplete {
};
}
+ static auto FilterColumnsByName(TString name) {
+ return [name = std::move(name)](auto f) {
+ return f.ExtractValue().Transform([&](auto&& table) {
+ EraseIf(table.Columns, [prefix = ToLowerUTF8(name)](const TString& name) {
+ return !name.StartsWith(prefix);
+ });
+ return table;
+ });
+ };
+ }
+
+ static auto CropColumns(size_t limit) {
+ return [limit](auto f) {
+ return f.ExtractValue().Transform([&](auto&& table) {
+ table.Columns.crop(limit);
+ return table;
+ });
+ };
+ }
+
+ static auto ToTableDescribeResponse() {
+ return [](auto f) {
+ TMaybe<TTableDetails> table = f.ExtractValue();
+ return TDescribeTableResponse{
+ .IsExisting = table.Defined(),
+ .Columns = table
+ .Transform([](auto&& table) { return table.Columns; })
+ .GetOrElse({}),
+ };
+ };
+ }
+
public:
explicit TSimpleSchema(ISimpleSchema::TPtr simple)
: Simple_(std::move(simple))
@@ -55,10 +87,19 @@ namespace NSQLComplete {
NThreading::TFuture<TListResponse> List(const TListRequest& request) const override {
auto [path, name] = Simple_->Split(request.Path);
return Simple_->List(request.Cluster, TString(path))
- .Apply(FilterByName(TString(name)))
- .Apply(FilterByTypes(request.Filter.Types))
- .Apply(Crop(request.Limit))
- .Apply(ToResponse(name));
+ .Apply(FilterEntriesByName(TString(name)))
+ .Apply(FilterEntriesByTypes(request.Filter.Types))
+ .Apply(CropEntries(request.Limit))
+ .Apply(ToListResponse(name));
+ }
+
+ NThreading::TFuture<TDescribeTableResponse>
+ Describe(const TDescribeTableRequest& request) const override {
+ return Simple_
+ ->DescribeTable(request.TableCluster, request.TablePath)
+ .Apply(FilterColumnsByName(TString(request.ColumnPrefix)))
+ .Apply(CropColumns(request.ColumnsLimit))
+ .Apply(ToTableDescribeResponse());
}
private:
@@ -77,6 +118,12 @@ namespace NSQLComplete {
return List(std::move(folder));
}
+ NThreading::TFuture<TMaybe<TTableDetails>>
+ ISimpleSchema::DescribeTable(const TString& cluster, const TString& path) const {
+ Y_UNUSED(cluster, path);
+ return NThreading::MakeFuture<TMaybe<TTableDetails>>(Nothing());
+ }
+
ISchema::TPtr MakeSimpleSchema(ISimpleSchema::TPtr simple) {
return ISchema::TPtr(new TSimpleSchema(std::move(simple)));
}
diff --git a/yql/essentials/sql/v1/complete/name/object/simple/schema.h b/yql/essentials/sql/v1/complete/name/object/simple/schema.h
index e3b5cb4cb1b..2bdb428b191 100644
--- a/yql/essentials/sql/v1/complete/name/object/simple/schema.h
+++ b/yql/essentials/sql/v1/complete/name/object/simple/schema.h
@@ -9,6 +9,10 @@ namespace NSQLComplete {
TStringBuf NameHint;
};
+ struct TTableDetails {
+ TVector<TString> Columns;
+ };
+
class ISimpleSchema: public TThrRefBase {
public:
using TPtr = TIntrusivePtr<ISimpleSchema>;
@@ -22,6 +26,9 @@ namespace NSQLComplete {
virtual NThreading::TFuture<TVector<TFolderEntry>>
List(TString cluster, TString folder) const;
+
+ virtual NThreading::TFuture<TMaybe<TTableDetails>>
+ DescribeTable(const TString& cluster, const TString& path) const;
};
ISchema::TPtr MakeSimpleSchema(ISimpleSchema::TPtr simple);
diff --git a/yql/essentials/sql/v1/complete/name/object/simple/static/schema.cpp b/yql/essentials/sql/v1/complete/name/object/simple/static/schema.cpp
index 3482da8a332..7707d8dc4ba 100644
--- a/yql/essentials/sql/v1/complete/name/object/simple/static/schema.cpp
+++ b/yql/essentials/sql/v1/complete/name/object/simple/static/schema.cpp
@@ -1,20 +1,32 @@
#include "schema.h"
+#include <library/cpp/iterator/concatenate.h>
+
namespace NSQLComplete {
namespace {
class TSimpleSchema: public ISimpleSchema {
public:
- explicit TSimpleSchema(THashMap<TString, THashMap<TString, TVector<TFolderEntry>>> data)
- : Data_(std::move(data))
+ explicit TSimpleSchema(
+ THashMap<TString, THashMap<TString, TVector<TFolderEntry>>> folders,
+ THashMap<TString, THashMap<TString, TTableDetails>> tables)
+ : Folders_(std::move(folders))
+ , Tables_(std::move(tables))
{
- for (const auto& [_, tables] : Data_) {
- for (const auto& [k, _] : tables) {
+ for (const auto& [_, paths] : Folders_) {
+ for (const auto& [k, _] : paths) {
Y_ENSURE(k.StartsWith("/"), k << " must start with the '/'");
Y_ENSURE(k.EndsWith("/"), k << " must end with the '/'");
}
}
+
+ for (const auto& [_, paths] : Tables_) {
+ for (const auto& [k, _] : paths) {
+ Y_ENSURE(k.StartsWith("/"), k << " must start with the '/'");
+ Y_ENSURE(!k.EndsWith("/"), k << " must not end with the '/'");
+ }
+ }
}
TSplittedPath Split(TStringBuf path) const override {
@@ -37,7 +49,7 @@ namespace NSQLComplete {
const THashMap<TString, TVector<TFolderEntry>>* tables = nullptr;
const TVector<TFolderEntry>* items = nullptr;
- if ((tables = Data_.FindPtr(cluster)) &&
+ if ((tables = Folders_.FindPtr(cluster)) &&
(items = tables->FindPtr(folder))) {
entries = *items;
}
@@ -45,15 +57,32 @@ namespace NSQLComplete {
return NThreading::MakeFuture(std::move(entries));
}
+ NThreading::TFuture<TMaybe<TTableDetails>>
+ DescribeTable(const TString& cluster, const TString& path) const override {
+ auto* tables = Tables_.FindPtr(cluster);
+ if (tables == nullptr) {
+ return NThreading::MakeFuture<TMaybe<TTableDetails>>(Nothing());
+ }
+
+ auto* details = tables->FindPtr(path);
+ if (details == nullptr) {
+ return NThreading::MakeFuture<TMaybe<TTableDetails>>(Nothing());
+ }
+
+ return NThreading::MakeFuture<TMaybe<TTableDetails>>(*details);
+ }
+
private:
- THashMap<TString, THashMap<TString, TVector<TFolderEntry>>> Data_;
+ THashMap<TString, THashMap<TString, TVector<TFolderEntry>>> Folders_;
+ THashMap<TString, THashMap<TString, TTableDetails>> Tables_;
};
} // namespace
ISimpleSchema::TPtr MakeStaticSimpleSchema(
- THashMap<TString, THashMap<TString, TVector<TFolderEntry>>> fs) {
- return new TSimpleSchema(std::move(fs));
+ THashMap<TString, THashMap<TString, TVector<TFolderEntry>>> folders,
+ THashMap<TString, THashMap<TString, TTableDetails>> tables) {
+ return new TSimpleSchema(std::move(folders), std::move(tables));
}
} // namespace NSQLComplete
diff --git a/yql/essentials/sql/v1/complete/name/object/simple/static/schema.h b/yql/essentials/sql/v1/complete/name/object/simple/static/schema.h
index 009b433ee4a..57fb322d6e2 100644
--- a/yql/essentials/sql/v1/complete/name/object/simple/static/schema.h
+++ b/yql/essentials/sql/v1/complete/name/object/simple/static/schema.h
@@ -5,6 +5,7 @@
namespace NSQLComplete {
ISimpleSchema::TPtr MakeStaticSimpleSchema(
- THashMap<TString, THashMap<TString, TVector<TFolderEntry>>> fs);
+ THashMap<TString, THashMap<TString, TVector<TFolderEntry>>> folders,
+ THashMap<TString, THashMap<TString, TTableDetails>> tables = {});
} // namespace NSQLComplete
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 7b7bc9f9755..5139b27dfa3 100644
--- a/yql/essentials/sql/v1/complete/name/service/name_service.h
+++ b/yql/essentials/sql/v1/complete/name/service/name_service.h
@@ -66,6 +66,12 @@ namespace NSQLComplete {
struct TConstraints: TNamespaced {};
};
+ struct TColumnName: TIndentifier {
+ struct TConstraints {
+ TVector<TTableId> Tables;
+ };
+ };
+
struct TBindingName: TIndentifier {
};
@@ -83,6 +89,7 @@ namespace NSQLComplete {
TFolderName,
TTableName,
TClusterName,
+ TColumnName,
TBindingName,
TUnkownName>;
@@ -93,6 +100,7 @@ namespace NSQLComplete {
TMaybe<THintName::TConstraints> Hint;
TMaybe<TObjectNameConstraints> Object;
TMaybe<TClusterName::TConstraints> Cluster;
+ TMaybe<TColumnName::TConstraints> Column;
bool IsEmpty() const {
return !Pragma &&
@@ -100,7 +108,8 @@ namespace NSQLComplete {
!Function &&
!Hint &&
!Object &&
- !Cluster;
+ !Cluster &&
+ !Column;
}
TGenericName Qualified(TGenericName unqualified) const;
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 9f30ead37d5..d8e0e0ff1bb 100644
--- a/yql/essentials/sql/v1/complete/name/service/ranking/ranking.cpp
+++ b/yql/essentials/sql/v1/complete/name/service/ranking/ranking.cpp
@@ -92,7 +92,8 @@ namespace NSQLComplete {
}
if constexpr (std::is_same_v<T, TFolderName> ||
- std::is_same_v<T, TTableName>) {
+ std::is_same_v<T, TTableName> ||
+ std::is_same_v<T, TColumnName>) {
return std::numeric_limits<size_t>::max();
}
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 b21ad828317..2970f06d7cd 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
@@ -12,13 +12,26 @@ namespace NSQLComplete {
}
NThreading::TFuture<TNameResponse> Lookup(TNameRequest request) const override {
- if (!request.Constraints.Object) {
- return NThreading::MakeFuture<TNameResponse>({});
+ if (request.Constraints.Object) {
+ return Schema_
+ ->List(ToListRequest(std::move(request)))
+ .Apply(ToListNameResponse);
}
- return Schema_
- ->List(ToListRequest(std::move(request)))
- .Apply(ToNameResponse);
+ if (request.Constraints.Column) {
+ Y_ENSURE(request.Constraints.Column->Tables.size() == 1, "Not Implemented");
+ TTableId table = request.Constraints.Column->Tables[0];
+ return Schema_
+ ->Describe({
+ .TableCluster = table.Cluster,
+ .TablePath = table.Path,
+ .ColumnPrefix = request.Prefix,
+ .ColumnsLimit = request.Limit,
+ })
+ .Apply(ToDescribeNameResponse);
+ }
+
+ return NThreading::MakeFuture<TNameResponse>({});
}
private:
@@ -53,7 +66,7 @@ namespace NSQLComplete {
}
}
- static TNameResponse ToNameResponse(NThreading::TFuture<TListResponse> f) {
+ static TNameResponse ToListNameResponse(NThreading::TFuture<TListResponse> f) {
TListResponse list = f.ExtractValue();
TNameResponse response;
@@ -83,6 +96,18 @@ namespace NSQLComplete {
return name;
}
+ static TNameResponse ToDescribeNameResponse(NThreading::TFuture<TDescribeTableResponse> f) {
+ TDescribeTableResponse table = f.ExtractValue();
+
+ TNameResponse response;
+ for (TString& column : table.Columns) {
+ TColumnName name;
+ name.Indentifier = std::move(column);
+ response.RankedNames.emplace_back(std::move(name));
+ }
+ return response;
+ }
+
ISchema::TPtr Schema_;
};
diff --git a/yql/essentials/sql/v1/complete/sql_complete.cpp b/yql/essentials/sql/v1/complete/sql_complete.cpp
index eea7571e8f1..2f61b1fe4df 100644
--- a/yql/essentials/sql/v1/complete/sql_complete.cpp
+++ b/yql/essentials/sql/v1/complete/sql_complete.cpp
@@ -142,6 +142,11 @@ namespace NSQLComplete {
object->Kinds.emplace(EObjectKind::Table);
}
+ if (context.Column && global.Column) {
+ request.Constraints.Column = TColumnName::TConstraints();
+ request.Constraints.Column->Tables = std::move(global.Column->Tables);
+ }
+
return request;
}
@@ -231,6 +236,10 @@ namespace NSQLComplete {
return {ECandidateKind::ClusterName, std::move(name.Indentifier)};
}
+ if constexpr (std::is_base_of_v<TColumnName, T>) {
+ return {ECandidateKind::ColumnName, std::move(name.Indentifier)};
+ }
+
if constexpr (std::is_base_of_v<TBindingName, T>) {
if (!context.Binding) {
name.Indentifier.prepend('$');
@@ -333,6 +342,9 @@ void Out<NSQLComplete::ECandidateKind>(IOutputStream& out, NSQLComplete::ECandid
case NSQLComplete::ECandidateKind::BindingName:
out << "BindingName";
break;
+ case NSQLComplete::ECandidateKind::ColumnName:
+ out << "ColumnName";
+ 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 df5159aff3c..33ca0c00ac9 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,
+ ColumnName,
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 878897a3844..d37d34be06b 100644
--- a/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
+++ b/yql/essentials/sql/v1/complete/sql_complete_ut.cpp
@@ -43,6 +43,7 @@ public:
Y_UNIT_TEST_SUITE(SqlCompleteTests) {
using ECandidateKind::BindingName;
using ECandidateKind::ClusterName;
+ using ECandidateKind::ColumnName;
using ECandidateKind::FolderName;
using ECandidateKind::FunctionName;
using ECandidateKind::HintName;
@@ -83,7 +84,7 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
},
};
- THashMap<TString, THashMap<TString, TVector<TFolderEntry>>> fs = {
+ THashMap<TString, THashMap<TString, TVector<TFolderEntry>>> folders = {
{"", {{"/", {{"Folder", "local"},
{"Folder", "test"},
{"Folder", "prod"},
@@ -103,15 +104,18 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
{{"/", {{"Table", "maxim"}}}}},
};
+ THashMap<TString, THashMap<TString, TTableDetails>> tables = {
+ {"example", {{"/people", {{"name", "age"}}}}}};
+
auto clustersIt = NFuncTools::Filter(
- [](const auto& x) { return !x.empty(); }, IterateKeys(fs));
+ [](const auto& x) { return !x.empty(); }, IterateKeys(folders));
TVector<TString> clusters(begin(clustersIt), end(clustersIt));
TFrequencyData frequency;
TVector<INameService::TPtr> children = {
MakeStaticNameService(std::move(names), frequency),
- MakeSchemaNameService(MakeSimpleSchema(MakeStaticSimpleSchema(std::move(fs)))),
+ MakeSchemaNameService(MakeSimpleSchema(MakeStaticSimpleSchema(std::move(folders), std::move(tables)))),
MakeClusterNameService(MakeStaticClusterDiscovery(std::move(clusters))),
};
INameService::TPtr service = MakeUnionNameService(
@@ -1045,6 +1049,31 @@ Y_UNIT_TEST_SUITE(SqlCompleteTests) {
CompleteTop(1, engine, "SELECT Max(#)").at(0).Kind, FolderName);
}
+ Y_UNIT_TEST(ColumnsAtSimpleSelect) {
+ auto engine = MakeSqlCompletionEngineUT();
+ {
+ TVector<TCandidate> expected = {
+ {ColumnName, "age"},
+ {ColumnName, "name"},
+ };
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, "SELECT # FROM example.`/people`"), expected);
+ }
+ {
+ TVector<TCandidate> expected = {
+ {ColumnName, "age"},
+ {Keyword, "ALL"},
+ };
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, "SELECT a# FROM example.`/people`"), expected);
+ }
+ {
+ TVector<TCandidate> expected = {
+ {ColumnName, "age"},
+ {ColumnName, "name"},
+ };
+ UNIT_ASSERT_VALUES_EQUAL(CompleteTop(2, engine, "USE example; SELECT # FROM `/people`"), expected);
+ }
+ }
+
Y_UNIT_TEST(Typing) {
const auto queryUtf16 = TUtf16String::FromUtf8(
"SELECT \n"
diff --git a/yql/essentials/sql/v1/complete/syntax/ya.make b/yql/essentials/sql/v1/complete/syntax/ya.make
index 6a35a80375e..f60a05f7b59 100644
--- a/yql/essentials/sql/v1/complete/syntax/ya.make
+++ b/yql/essentials/sql/v1/complete/syntax/ya.make
@@ -14,6 +14,7 @@ PEERDIR(
yql/essentials/sql/settings
yql/essentials/sql/v1/lexer
yql/essentials/sql/v1/reflect
+ yql/essentials/sql/v1/complete/antlr4
yql/essentials/sql/v1/complete/core
yql/essentials/sql/v1/complete/text
)