Skip to content

Commit 5a98e59

Browse files
jepett0zinal
authored andcommitted
24-3: Enable subqueries inside views (ydb-platform#10517) (ydb-platform#10632)
1 parent 1553b8e commit 5a98e59

File tree

6 files changed

+145
-82
lines changed

6 files changed

+145
-82
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CREATE VIEW in_subquery WITH (security_invoker = TRUE) AS
2+
SELECT
3+
*
4+
FROM series
5+
WHERE series_id IN (
6+
SELECT
7+
series_id
8+
FROM series
9+
);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DROP VIEW in_subquery;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
SELECT
2+
*
3+
FROM (
4+
SELECT
5+
*
6+
FROM series
7+
WHERE series_id IN (
8+
SELECT
9+
series_id
10+
FROM series
11+
)
12+
);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SELECT
2+
*
3+
FROM in_subquery;

ydb/core/kqp/ut/view/view_ut.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,18 @@ void CompareResults(const TDataQueryResult& first, const TDataQueryResult& secon
120120
}
121121
}
122122

123-
void InitializeTablesAndSecondaryViews(TSession& session) {
123+
void CompareResults(const TDataQueryResult& first, const TDataQueryResult& second) {
124+
CompareResults(first.GetResultSets(), second.GetResultSets());
125+
}
126+
127+
void CompareResults(const NQuery::TExecuteQueryResult& first, const NQuery::TExecuteQueryResult& second) {
128+
CompareResults(first.GetResultSets(), second.GetResultSets());
129+
}
130+
131+
void InitializeTablesAndSecondaryViews(NQuery::TSession& session) {
124132
const auto inputFolder = ArcadiaFromCurrentLocation(__SOURCE_FILE__, "input");
125-
ExecuteDataDefinitionQuery(session, ReadWholeFile(inputFolder + "/create_tables_and_secondary_views.sql"));
126-
ExecuteDataModificationQuery(session, ReadWholeFile(inputFolder + "/fill_tables.sql"));
133+
ExecuteQuery(session, ReadWholeFile(inputFolder + "/create_tables_and_secondary_views.sql"));
134+
ExecuteQuery(session, ReadWholeFile(inputFolder + "/fill_tables.sql"));
127135
}
128136

129137
}
@@ -439,7 +447,7 @@ Y_UNIT_TEST_SUITE(TSelectFromViewTest) {
439447
Y_UNIT_TEST(ReadTestCasesFromFiles) {
440448
TKikimrRunner kikimr;
441449
EnableViewsFeatureFlag(kikimr);
442-
auto session = kikimr.GetTableClient().CreateSession().GetValueSync().GetSession();
450+
auto session = kikimr.GetQueryClient().GetSession().ExtractValueSync().GetSession();
443451

444452
InitializeTablesAndSecondaryViews(session);
445453
EnableLogging();
@@ -450,13 +458,13 @@ Y_UNIT_TEST_SUITE(TSelectFromViewTest) {
450458
TString testcase;
451459
while (testcase = testcases.Next()) {
452460
const auto pathPrefix = TStringBuilder() << testcasesFolder << '/' << testcase << '/';
453-
ExecuteDataDefinitionQuery(session, ReadWholeFile(pathPrefix + "create_view.sql"));
461+
ExecuteQuery(session, ReadWholeFile(pathPrefix + "create_view.sql"));
454462

455-
const auto etalonResults = ExecuteDataModificationQuery(session, ReadWholeFile(pathPrefix + "etalon_query.sql"));
456-
const auto selectFromViewResults = ExecuteDataModificationQuery(session, ReadWholeFile(pathPrefix + "select_from_view.sql"));
463+
const auto etalonResults = ExecuteQuery(session, ReadWholeFile(pathPrefix + "etalon_query.sql"));
464+
const auto selectFromViewResults = ExecuteQuery(session, ReadWholeFile(pathPrefix + "select_from_view.sql"));
457465
CompareResults(etalonResults, selectFromViewResults);
458466

459-
ExecuteDataDefinitionQuery(session, ReadWholeFile(pathPrefix + "drop_view.sql"));
467+
ExecuteQuery(session, ReadWholeFile(pathPrefix + "drop_view.sql"));
460468
}
461469
}
462470

ydb/library/yql/sql/v1/sql_translation.cpp

Lines changed: 104 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <ydb/library/yql/parser/proto_ast/gen/v1/SQLv1Lexer.h>
1010
#include <ydb/library/yql/sql/settings/partitioning.h>
11+
#include <ydb/library/yql/sql/v1/proto_parser/proto_parser.h>
1112

1213
#include <util/generic/scope.h>
1314
#include <util/string/join.h>
@@ -53,31 +54,67 @@ TString CollectTokens(const TRule_select_stmt& selectStatement) {
5354
return tokenCollector.Tokens;
5455
}
5556

56-
NSQLTranslation::TTranslationSettings CreateViewTranslationSettings(const NSQLTranslation::TTranslationSettings& base) {
57-
NSQLTranslation::TTranslationSettings settings;
57+
bool RecreateContext(
58+
TContext& ctx, const NSQLTranslation::TTranslationSettings& settings, const TString& recreationQuery
59+
) {
60+
if (!recreationQuery) {
61+
return true;
62+
}
63+
const TString queryName = "context recreation query";
5864

59-
settings.ClusterMapping = base.ClusterMapping;
60-
settings.Mode = NSQLTranslation::ESqlMode::LIMITED_VIEW;
65+
const auto* ast = NSQLTranslationV1::SqlAST(
66+
recreationQuery, queryName, ctx.Issues,
67+
settings.MaxErrors, settings.AnsiLexer, settings.Arena
68+
);
69+
if (!ast) {
70+
return false;
71+
}
72+
73+
TSqlQuery queryTranslator(ctx, ctx.Settings.Mode, true);
74+
auto node = queryTranslator.Build(static_cast<const TSQLv1ParserAST&>(*ast));
6175

62-
return settings;
76+
return node && node->Init(ctx, nullptr) && node->Translate(ctx);
6377
}
6478

65-
TNodePtr BuildViewSelect(const TRule_select_stmt& query, TContext& ctx) {
66-
const auto viewTranslationSettings = CreateViewTranslationSettings(ctx.Settings);
67-
TContext viewParsingContext(viewTranslationSettings, {}, ctx.Issues);
68-
TSqlSelect select(viewParsingContext, viewTranslationSettings.Mode);
69-
TPosition pos;
70-
auto source = select.Build(query, pos);
79+
TNodePtr BuildViewSelect(
80+
const TRule_select_stmt& selectStatement,
81+
TContext& parentContext,
82+
const TString& contextRecreationQuery
83+
) {
84+
TIssues issues;
85+
TContext context(parentContext.Settings, {}, issues);
86+
if (!RecreateContext(context, context.Settings, contextRecreationQuery)) {
87+
parentContext.Issues.AddIssues(issues);
88+
return nullptr;
89+
}
90+
issues.Clear();
91+
92+
// Holds (among other things) subquery references.
93+
// These references need to be passed to the parent context
94+
// to be able to compile view queries with subqueries.
95+
context.PushCurrentBlocks(&parentContext.GetCurrentBlocks());
96+
97+
context.Settings.Mode = NSQLTranslation::ESqlMode::LIMITED_VIEW;
98+
99+
TSqlSelect selectTranslator(context, context.Settings.Mode);
100+
TPosition pos = parentContext.Pos();
101+
auto source = selectTranslator.Build(selectStatement, pos);
71102
if (!source) {
103+
parentContext.Issues.AddIssues(issues);
72104
return nullptr;
73105
}
74-
return BuildSelectResult(
106+
auto node = BuildSelectResult(
75107
pos,
76108
std::move(source),
77109
false,
78110
false,
79-
viewParsingContext.Scoped
111+
context.Scoped
80112
);
113+
if (!node) {
114+
parentContext.Issues.AddIssues(issues);
115+
return nullptr;
116+
}
117+
return node;
81118
}
82119

83120
}
@@ -1629,16 +1666,6 @@ namespace {
16291666
return true;
16301667
}
16311668

1632-
bool StoreBool(const TRule_table_setting_value& from, TDeferredAtom& to, TContext& ctx) {
1633-
if (!from.HasAlt_table_setting_value6()) {
1634-
return false;
1635-
}
1636-
// bool_value
1637-
const TString value = to_lower(ctx.Token(from.GetAlt_table_setting_value6().GetRule_bool_value1().GetToken1()));
1638-
to = TDeferredAtom(BuildLiteralBool(ctx.Pos(), FromString<bool>(value)), ctx);
1639-
return true;
1640-
}
1641-
16421669
bool StoreSplitBoundary(const TRule_literal_value_list& boundary, TVector<TVector<TNodePtr>>& to,
16431670
TSqlExpression& expr, TContext& ctx) {
16441671
TVector<TNodePtr> boundaryKeys;
@@ -1765,26 +1792,6 @@ namespace {
17651792
return true;
17661793
}
17671794

1768-
bool StoreViewOptionsEntry(const TIdentifier& id,
1769-
const TRule_table_setting_value& value,
1770-
std::map<TString, TDeferredAtom>& features,
1771-
TContext& ctx) {
1772-
const auto name = to_lower(id.Name);
1773-
const auto publicName = to_upper(name);
1774-
1775-
if (features.find(name) != features.end()) {
1776-
ctx.Error(ctx.Pos()) << publicName << " is a duplicate";
1777-
return false;
1778-
}
1779-
1780-
if (!StoreBool(value, features[name], ctx)) {
1781-
ctx.Error(ctx.Pos()) << "Value of " << publicName << " must be a bool";
1782-
return false;
1783-
}
1784-
1785-
return true;
1786-
}
1787-
17881795
template<typename TChar>
17891796
struct TPatternComponent {
17901797
TBasicString<TChar> Prefix;
@@ -1892,6 +1899,17 @@ bool TSqlTranslation::StoreExternalTableSettingsEntry(const TIdentifier& id, con
18921899
return true;
18931900
}
18941901

1902+
bool TSqlTranslation::ValidateTableSettings(const TTableSettings& settings) {
1903+
if (settings.PartitionCount) {
1904+
if (!settings.StoreType || to_lower(settings.StoreType->Name) != "column") {
1905+
Ctx.Error() << " PARTITION_COUNT can be used only with STORE=COLUMN";
1906+
return false;
1907+
}
1908+
}
1909+
1910+
return true;
1911+
}
1912+
18951913
bool TSqlTranslation::StoreTableSettingsEntry(const TIdentifier& id, const TRule_table_setting_value* value,
18961914
TTableSettings& settings, bool alter, bool reset) {
18971915
YQL_ENSURE(value || reset);
@@ -1950,6 +1968,16 @@ bool TSqlTranslation::StoreTableSettingsEntry(const TIdentifier& id, const TRule
19501968
Ctx.Error() << to_upper(id.Name) << " value should be an integer";
19511969
return false;
19521970
}
1971+
} else if (to_lower(id.Name) == "partition_count") {
1972+
if (reset) {
1973+
Ctx.Error() << to_upper(id.Name) << " reset is not supported";
1974+
return false;
1975+
}
1976+
1977+
if (!StoreInt(*value, settings.PartitionCount, Ctx)) {
1978+
Ctx.Error() << to_upper(id.Name) << " value should be an integer";
1979+
return false;
1980+
}
19531981
} else if (to_lower(id.Name) == "uniform_partitions") {
19541982
if (alter) {
19551983
Ctx.Error() << to_upper(id.Name) << " alter is not supported";
@@ -2040,7 +2068,8 @@ bool TSqlTranslation::StoreTableSettingsEntry(const TIdentifier& id, const TRule
20402068
Ctx.Error() << "Unknown table setting: " << id.Name;
20412069
return false;
20422070
}
2043-
return true;
2071+
2072+
return ValidateTableSettings(settings);
20442073
}
20452074

20462075
bool TSqlTranslation::StoreTableSettingsEntry(const TIdentifier& id, const TRule_table_setting_value& value,
@@ -4354,7 +4383,7 @@ bool TSqlTranslation::BindParameterClause(const TRule_bind_parameter& node, TDef
43544383
}
43554384

43564385
bool TSqlTranslation::ObjectFeatureValueClause(const TRule_object_feature_value& node, TDeferredAtom& result) {
4357-
// object_feature_value: an_id_or_type | bind_parameter;
4386+
// object_feature_value: id_or_type | bind_parameter | STRING_VALUE | bool_value;
43584387
switch (node.Alt_case()) {
43594388
case TRule_object_feature_value::kAltObjectFeatureValue1:
43604389
{
@@ -4379,6 +4408,12 @@ bool TSqlTranslation::ObjectFeatureValueClause(const TRule_object_feature_value&
43794408
result = TDeferredAtom(Ctx.Pos(), strValue->Content);
43804409
break;
43814410
}
4411+
case TRule_object_feature_value::kAltObjectFeatureValue4:
4412+
{
4413+
TString value = Ctx.Token(node.GetAlt_object_feature_value4().GetRule_bool_value1().GetToken1());
4414+
result = TDeferredAtom(BuildLiteralBool(Ctx.Pos(), FromString<bool>(value)), Ctx);
4415+
break;
4416+
}
43824417
case TRule_object_feature_value::ALT_NOT_SET:
43834418
Y_ABORT("You should change implementation according to grammar changes");
43844419
}
@@ -4568,38 +4603,32 @@ bool TSqlTranslation::ValidateExternalTable(const TCreateTableParameters& params
45684603
return true;
45694604
}
45704605

4571-
bool TSqlTranslation::ParseViewOptions(std::map<TString, TDeferredAtom>& features,
4572-
const TRule_with_table_settings& options) {
4573-
const auto& firstEntry = options.GetRule_table_settings_entry3();
4574-
if (!StoreViewOptionsEntry(IdEx(firstEntry.GetRule_an_id1(), *this),
4575-
firstEntry.GetRule_table_setting_value3(),
4576-
features,
4577-
Ctx)) {
4578-
return false;
4579-
}
4580-
for (const auto& block : options.GetBlock4()) {
4581-
const auto& entry = block.GetRule_table_settings_entry2();
4582-
if (!StoreViewOptionsEntry(IdEx(entry.GetRule_an_id1(), *this),
4583-
entry.GetRule_table_setting_value3(),
4584-
features,
4585-
Ctx)) {
4586-
return false;
4606+
bool TSqlTranslation::ParseViewQuery(
4607+
std::map<TString, TDeferredAtom>& features,
4608+
const TRule_select_stmt& query
4609+
) {
4610+
TString queryText = CollectTokens(query);
4611+
TString contextRecreationQuery;
4612+
{
4613+
const auto& service = Ctx.Scoped->CurrService;
4614+
const auto& cluster = Ctx.Scoped->CurrCluster;
4615+
const auto effectivePathPrefix = Ctx.GetPrefixPath(service, cluster);
4616+
4617+
// TO DO: capture all runtime pragmas in a similar fashion.
4618+
if (effectivePathPrefix != Ctx.Settings.PathPrefix) {
4619+
contextRecreationQuery = TStringBuilder() << "PRAGMA TablePathPrefix = \"" << effectivePathPrefix << "\";\n";
45874620
}
4588-
}
4589-
if (const auto securityInvoker = features.find("security_invoker");
4590-
securityInvoker == features.end() || securityInvoker->second.Build()->GetLiteralValue() != "true") {
4591-
Ctx.Error(Ctx.Pos()) << "SECURITY_INVOKER option must be explicitly enabled";
4592-
return false;
4593-
}
4594-
return true;
4595-
}
45964621

4597-
bool TSqlTranslation::ParseViewQuery(std::map<TString, TDeferredAtom>& features,
4598-
const TRule_select_stmt& query) {
4599-
const TString queryText = CollectTokens(query);
4600-
features["query_text"] = {Ctx.Pos(), queryText};
4622+
// TO DO: capture other compilation-affecting statements except USE.
4623+
if (cluster.GetLiteral() && *cluster.GetLiteral() != Ctx.Settings.DefaultCluster) {
4624+
contextRecreationQuery = TStringBuilder() << "USE " << *cluster.GetLiteral() << ";\n";
4625+
}
4626+
}
4627+
features["query_text"] = { Ctx.Pos(), contextRecreationQuery + queryText };
46014628

4602-
const auto viewSelect = BuildViewSelect(query, Ctx);
4629+
// AST is needed for ready-made validation of CREATE VIEW statement.
4630+
// Query is stored as plain text, not AST.
4631+
const auto viewSelect = BuildViewSelect(query, Ctx, contextRecreationQuery);
46034632
if (!viewSelect) {
46044633
return false;
46054634
}
@@ -4745,3 +4774,4 @@ bool TSqlTranslation::ParseResourcePoolSettings(std::map<TString, TDeferredAtom>
47454774
}
47464775

47474776
} // namespace NSQLTranslationV1
4777+

0 commit comments

Comments
 (0)