diff --git a/ydb/core/kqp/ut/view/input/cases/in_subquery/create_view.sql b/ydb/core/kqp/ut/view/input/cases/in_subquery/create_view.sql new file mode 100644 index 000000000000..b8793469c350 --- /dev/null +++ b/ydb/core/kqp/ut/view/input/cases/in_subquery/create_view.sql @@ -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 + ); diff --git a/ydb/core/kqp/ut/view/input/cases/in_subquery/drop_view.sql b/ydb/core/kqp/ut/view/input/cases/in_subquery/drop_view.sql new file mode 100644 index 000000000000..da81f5486bc6 --- /dev/null +++ b/ydb/core/kqp/ut/view/input/cases/in_subquery/drop_view.sql @@ -0,0 +1 @@ +DROP VIEW in_subquery; diff --git a/ydb/core/kqp/ut/view/input/cases/in_subquery/etalon_query.sql b/ydb/core/kqp/ut/view/input/cases/in_subquery/etalon_query.sql new file mode 100644 index 000000000000..596f8521ea79 --- /dev/null +++ b/ydb/core/kqp/ut/view/input/cases/in_subquery/etalon_query.sql @@ -0,0 +1,12 @@ +SELECT + * +FROM ( + SELECT + * + FROM series + WHERE series_id IN ( + SELECT + series_id + FROM series + ) +); diff --git a/ydb/core/kqp/ut/view/input/cases/in_subquery/select_from_view.sql b/ydb/core/kqp/ut/view/input/cases/in_subquery/select_from_view.sql new file mode 100644 index 000000000000..c0c1b52763e0 --- /dev/null +++ b/ydb/core/kqp/ut/view/input/cases/in_subquery/select_from_view.sql @@ -0,0 +1,3 @@ +SELECT + * +FROM in_subquery; diff --git a/ydb/core/kqp/ut/view/view_ut.cpp b/ydb/core/kqp/ut/view/view_ut.cpp index 111c2faee84f..ba7aca97e589 100644 --- a/ydb/core/kqp/ut/view/view_ut.cpp +++ b/ydb/core/kqp/ut/view/view_ut.cpp @@ -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")); } } @@ -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(); @@ -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")); } } diff --git a/ydb/library/yql/sql/v1/sql_translation.cpp b/ydb/library/yql/sql/v1/sql_translation.cpp index 2a390b24d294..d38ecd031d1a 100644 --- a/ydb/library/yql/sql/v1/sql_translation.cpp +++ b/ydb/library/yql/sql/v1/sql_translation.cpp @@ -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(*ast)); + TSqlQuery queryTranslator(ctx, ctx.Settings.Mode, true); + auto node = queryTranslator.Build(static_cast(*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; } } @@ -4589,7 +4608,7 @@ 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; @@ -4597,19 +4616,19 @@ bool TSqlTranslation::ParseViewQuery( // 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; }