aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoraneporada <aneporada@ydb.tech>2022-12-07 12:15:08 +0300
committeraneporada <aneporada@ydb.tech>2022-12-07 12:15:08 +0300
commit62b457541e240a5ddad9a474cd0339951a166cb5 (patch)
treec7ed7ab2be3e15a27180610ac015fcdba7b7d535
parentc6801726ac1fd244024843fea3b8d4c76896090c (diff)
downloadydb-62b457541e240a5ddad9a474cd0339951a166cb5.tar.gz
Emit proper error message when top level named subquery statement is found in libraries
-rw-r--r--ydb/library/yql/sql/v1/node.h1
-rw-r--r--ydb/library/yql/sql/v1/select.cpp31
-rw-r--r--ydb/library/yql/sql/v1/sql.cpp6
-rw-r--r--ydb/library/yql/sql/v1/sql_ut.cpp21
4 files changed, 58 insertions, 1 deletions
diff --git a/ydb/library/yql/sql/v1/node.h b/ydb/library/yql/sql/v1/node.h
index f2c60b8dcf..538202854f 100644
--- a/ydb/library/yql/sql/v1/node.h
+++ b/ydb/library/yql/sql/v1/node.h
@@ -1307,6 +1307,7 @@ namespace NSQLTranslationV1 {
// Implemented in select.cpp
TNodePtr BuildSubquery(TSourcePtr source, const TString& alias, bool inSubquery, int ensureTupleSize, TScopedStatePtr scoped);
TNodePtr BuildSubqueryRef(TNodePtr subquery, const TString& alias, int tupleIndex = -1);
+ TNodePtr BuildInvalidSubqueryRef(TPosition subqueryPos);
TNodePtr BuildSourceNode(TPosition pos, TSourcePtr source, bool checkExist = false);
TSourcePtr BuildMuxSource(TPosition pos, TVector<TSourcePtr>&& sources);
TSourcePtr BuildFakeSource(TPosition pos, bool missingFrom = false, bool inSubquery = false);
diff --git a/ydb/library/yql/sql/v1/select.cpp b/ydb/library/yql/sql/v1/select.cpp
index a7d2d0a8c4..c3fa583668 100644
--- a/ydb/library/yql/sql/v1/select.cpp
+++ b/ydb/library/yql/sql/v1/select.cpp
@@ -650,6 +650,37 @@ TNodePtr BuildSubqueryRef(TNodePtr subquery, const TString& alias, int tupleInde
return new TSubqueryRefNode(std::move(subquery), alias, tupleIndex);
}
+class TInvalidSubqueryRefNode: public ISource {
+public:
+ TInvalidSubqueryRefNode(TPosition pos)
+ : ISource(pos)
+ , Pos(pos)
+ {
+ }
+
+ bool DoInit(TContext& ctx, ISource* src) override {
+ Y_UNUSED(src);
+ ctx.Error(Pos) << "Named subquery can not be used as a top level statement in libraries";
+ return false;
+ }
+
+ TNodePtr Build(TContext& ctx) override {
+ Y_UNUSED(ctx);
+ return {};
+ }
+
+ TPtr DoClone() const final {
+ return new TInvalidSubqueryRefNode(Pos);
+ }
+
+protected:
+ const TPosition Pos;
+};
+
+TNodePtr BuildInvalidSubqueryRef(TPosition subqueryPos) {
+ return new TInvalidSubqueryRefNode(subqueryPos);
+}
+
class TTableSource: public IRealSource {
public:
TTableSource(TPosition pos, const TTableRef& table, const TString& label)
diff --git a/ydb/library/yql/sql/v1/sql.cpp b/ydb/library/yql/sql/v1/sql.cpp
index b3bd162c18..475ccc5d18 100644
--- a/ydb/library/yql/sql/v1/sql.cpp
+++ b/ydb/library/yql/sql/v1/sql.cpp
@@ -8634,7 +8634,11 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core&
}
TVector<TNodePtr> nodes;
auto subquery = nodeExpr->GetSource();
- if (subquery) {
+ if (subquery && Mode == NSQLTranslation::ESqlMode::LIBRARY && Ctx.ScopeLevel == 0) {
+ for (size_t i = 0; i < names.size(); ++i) {
+ nodes.push_back(BuildInvalidSubqueryRef(subquery->GetPos()));
+ }
+ } else if (subquery) {
const auto alias = Ctx.MakeName("subquerynode");
const auto ref = Ctx.MakeName("subquery");
blocks.push_back(BuildSubquery(subquery, alias,
diff --git a/ydb/library/yql/sql/v1/sql_ut.cpp b/ydb/library/yql/sql/v1/sql_ut.cpp
index 9fe02d1f2a..6e993d79a4 100644
--- a/ydb/library/yql/sql/v1/sql_ut.cpp
+++ b/ydb/library/yql/sql/v1/sql_ut.cpp
@@ -3551,6 +3551,27 @@ select FormatType($f());
"<main>:1:42: Error: Source does not allow column references\n"
"<main>:1:71: Error: Column reference 'subkey'\n");
}
+
+ Y_UNIT_TEST(ErrorInLibraryWithTopLevelNamedSubquery) {
+ TString withUnusedSubq = "$unused = select max(key) from plato.Input;\n"
+ "\n"
+ "define subquery $foo() as\n"
+ " $count = select count(*) from plato.Input;\n"
+ " select * from plato.Input limit $count / 2;\n"
+ "end define;\n"
+ "export $foo;\n";
+ UNIT_ASSERT(SqlToYqlWithMode(withUnusedSubq, NSQLTranslation::ESqlMode::LIBRARY).IsOk());
+
+ TString withTopLevelSubq = "$count = select count(*) from plato.Input;\n"
+ "\n"
+ "define subquery $foo() as\n"
+ " select * from plato.Input limit $count / 2;\n"
+ "end define;\n"
+ "export $foo;\n";
+ auto res = SqlToYqlWithMode(withTopLevelSubq, NSQLTranslation::ESqlMode::LIBRARY);
+ UNIT_ASSERT(!res.Root);
+ UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:17: Error: Named subquery can not be used as a top level statement in libraries\n");
+ }
}
void CheckUnused(const TString& req, const TString& symbol, unsigned row, unsigned col) {