Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

24-3: Enable subqueries inside views (#10517) #10632

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE VIEW in_subquery WITH (security_invoker = TRUE) AS
SELECT
*
FROM series
WHERE series_id IN (
SELECT
series_id
FROM series
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP VIEW in_subquery;
12 changes: 12 additions & 0 deletions ydb/core/kqp/ut/view/input/cases/in_subquery/etalon_query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
SELECT
*
FROM (
SELECT
*
FROM series
WHERE series_id IN (
SELECT
series_id
FROM series
)
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SELECT
*
FROM in_subquery;
16 changes: 8 additions & 8 deletions ydb/core/kqp/ut/view/view_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,10 @@ void CompareResults(const NQuery::TExecuteQueryResult& first, const NQuery::TExe
CompareResults(first.GetResultSets(), second.GetResultSets());
}

void InitializeTablesAndSecondaryViews(TSession& session) {
void InitializeTablesAndSecondaryViews(NQuery::TSession& session) {
const auto inputFolder = ArcadiaFromCurrentLocation(__SOURCE_FILE__, "input");
ExecuteDataDefinitionQuery(session, ReadWholeFile(inputFolder + "/create_tables_and_secondary_views.sql"));
ExecuteDataModificationQuery(session, ReadWholeFile(inputFolder + "/fill_tables.sql"));
ExecuteQuery(session, ReadWholeFile(inputFolder + "/create_tables_and_secondary_views.sql"));
ExecuteQuery(session, ReadWholeFile(inputFolder + "/fill_tables.sql"));
}

}
Expand Down Expand Up @@ -582,7 +582,7 @@ Y_UNIT_TEST_SUITE(TSelectFromViewTest) {
Y_UNIT_TEST(ReadTestCasesFromFiles) {
TKikimrRunner kikimr;
EnableViewsFeatureFlag(kikimr);
auto session = kikimr.GetTableClient().CreateSession().GetValueSync().GetSession();
auto session = kikimr.GetQueryClient().GetSession().ExtractValueSync().GetSession();

InitializeTablesAndSecondaryViews(session);
EnableLogging();
Expand All @@ -593,13 +593,13 @@ Y_UNIT_TEST_SUITE(TSelectFromViewTest) {
TString testcase;
while (testcase = testcases.Next()) {
const auto pathPrefix = TStringBuilder() << testcasesFolder << '/' << testcase << '/';
ExecuteDataDefinitionQuery(session, ReadWholeFile(pathPrefix + "create_view.sql"));
ExecuteQuery(session, ReadWholeFile(pathPrefix + "create_view.sql"));

const auto etalonResults = ExecuteDataModificationQuery(session, ReadWholeFile(pathPrefix + "etalon_query.sql"));
const auto selectFromViewResults = ExecuteDataModificationQuery(session, ReadWholeFile(pathPrefix + "select_from_view.sql"));
const auto etalonResults = ExecuteQuery(session, ReadWholeFile(pathPrefix + "etalon_query.sql"));
const auto selectFromViewResults = ExecuteQuery(session, ReadWholeFile(pathPrefix + "select_from_view.sql"));
CompareResults(etalonResults, selectFromViewResults);

ExecuteDataDefinitionQuery(session, ReadWholeFile(pathPrefix + "drop_view.sql"));
ExecuteQuery(session, ReadWholeFile(pathPrefix + "drop_view.sql"));
}
}

Expand Down
57 changes: 38 additions & 19 deletions ydb/library/yql/sql/v1/sql_translation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,48 +54,67 @@ TString CollectTokens(const TRule_select_stmt& selectStatement) {
return tokenCollector.Tokens;
}

bool RestoreContext(
TContext& ctx, const NSQLTranslation::TTranslationSettings& settings, const TString& contextRestorationQuery
bool RecreateContext(
TContext& ctx, const NSQLTranslation::TTranslationSettings& settings, const TString& recreationQuery
) {
const TString queryName = "context restoration query";
if (!recreationQuery) {
return true;
}
const TString queryName = "context recreation query";

const auto* ast = NSQLTranslationV1::SqlAST(
contextRestorationQuery, queryName, ctx.Issues,
recreationQuery, queryName, ctx.Issues,
settings.MaxErrors, settings.AnsiLexer, settings.Arena
);
if (!ast) {
return false;
}

TSqlQuery query(ctx, ctx.Settings.Mode, true);
auto node = query.Build(static_cast<const TSQLv1ParserAST&>(*ast));
TSqlQuery queryTranslator(ctx, ctx.Settings.Mode, true);
auto node = queryTranslator.Build(static_cast<const TSQLv1ParserAST&>(*ast));

return node && node->Init(ctx, nullptr) && node->Translate(ctx);
}

TNodePtr BuildViewSelect(
const TRule_select_stmt& selectQuery,
const TRule_select_stmt& selectStatement,
TContext& parentContext,
const TString& contextRestorationQuery
const TString& contextRecreationQuery
) {
TContext context(parentContext.Settings, {}, parentContext.Issues);
RestoreContext(context, context.Settings, contextRestorationQuery);
TIssues issues;
TContext context(parentContext.Settings, {}, issues);
if (!RecreateContext(context, context.Settings, contextRecreationQuery)) {
parentContext.Issues.AddIssues(issues);
return nullptr;
}
issues.Clear();

// Holds (among other things) subquery references.
// These references need to be passed to the parent context
// to be able to compile view queries with subqueries.
context.PushCurrentBlocks(&parentContext.GetCurrentBlocks());

context.Settings.Mode = NSQLTranslation::ESqlMode::LIMITED_VIEW;

TSqlSelect select(context, context.Settings.Mode);
TPosition pos;
auto source = select.Build(selectQuery, pos);
TSqlSelect selectTranslator(context, context.Settings.Mode);
TPosition pos = parentContext.Pos();
auto source = selectTranslator.Build(selectStatement, pos);
if (!source) {
parentContext.Issues.AddIssues(issues);
return nullptr;
}
return BuildSelectResult(
auto node = BuildSelectResult(
pos,
std::move(source),
false,
false,
context.Scoped
);
if (!node) {
parentContext.Issues.AddIssues(issues);
return nullptr;
}
return node;
}

}
Expand Down Expand Up @@ -4589,27 +4608,27 @@ bool TSqlTranslation::ParseViewQuery(
const TRule_select_stmt& query
) {
TString queryText = CollectTokens(query);
TString contextRestorationQuery;
TString contextRecreationQuery;
{
const auto& service = Ctx.Scoped->CurrService;
const auto& cluster = Ctx.Scoped->CurrCluster;
const auto effectivePathPrefix = Ctx.GetPrefixPath(service, cluster);

// TO DO: capture all runtime pragmas in a similar fashion.
if (effectivePathPrefix != Ctx.Settings.PathPrefix) {
contextRestorationQuery = TStringBuilder() << "PRAGMA TablePathPrefix = \"" << effectivePathPrefix << "\";\n";
contextRecreationQuery = TStringBuilder() << "PRAGMA TablePathPrefix = \"" << effectivePathPrefix << "\";\n";
}

// TO DO: capture other compilation-affecting statements except USE.
if (cluster.GetLiteral() && *cluster.GetLiteral() != Ctx.Settings.DefaultCluster) {
contextRestorationQuery = TStringBuilder() << "USE " << *cluster.GetLiteral() << ";\n";
contextRecreationQuery = TStringBuilder() << "USE " << *cluster.GetLiteral() << ";\n";
}
}
features["query_text"] = { Ctx.Pos(), contextRestorationQuery + queryText };
features["query_text"] = { Ctx.Pos(), contextRecreationQuery + queryText };

// AST is needed for ready-made validation of CREATE VIEW statement.
// Query is stored as plain text, not AST.
const auto viewSelect = BuildViewSelect(query, Ctx, contextRestorationQuery);
const auto viewSelect = BuildViewSelect(query, Ctx, contextRecreationQuery);
if (!viewSelect) {
return false;
}
Expand Down
Loading