summaryrefslogtreecommitdiffstats
path: root/yql/essentials/sql
diff options
context:
space:
mode:
Diffstat (limited to 'yql/essentials/sql')
-rw-r--r--yql/essentials/sql/pg/pg_sql.cpp59
-rw-r--r--yql/essentials/sql/pg/pg_sql_ut.cpp28
-rw-r--r--yql/essentials/sql/pg/ya.make1
-rw-r--r--yql/essentials/sql/v1/SQLv1Antlr4.g.in11
-rw-r--r--yql/essentials/sql/v1/builtin.cpp127
-rw-r--r--yql/essentials/sql/v1/format/sql_format_ut.h26
-rw-r--r--yql/essentials/sql/v1/node.h8
-rw-r--r--yql/essentials/sql/v1/query.cpp17
-rw-r--r--yql/essentials/sql/v1/select.cpp16
-rw-r--r--yql/essentials/sql/v1/source.h2
-rw-r--r--yql/essentials/sql/v1/sql_query.cpp16
-rw-r--r--yql/essentials/sql/v1/sql_select.cpp34
-rw-r--r--yql/essentials/sql/v1/sql_translation.cpp71
-rw-r--r--yql/essentials/sql/v1/sql_ut_common.h440
14 files changed, 789 insertions, 67 deletions
diff --git a/yql/essentials/sql/pg/pg_sql.cpp b/yql/essentials/sql/pg/pg_sql.cpp
index 6990cbfefa7..81b9eb396ee 100644
--- a/yql/essentials/sql/pg/pg_sql.cpp
+++ b/yql/essentials/sql/pg/pg_sql.cpp
@@ -40,6 +40,7 @@ extern "C" {
#include <yql/essentials/providers/common/provider/yql_provider_names.h>
#include <yql/essentials/minikql/mkql_type_builder.h>
#include <yql/essentials/core/issue/yql_issue.h>
+#include <yql/essentials/core/langver/feature.gen.h>
#include <yql/essentials/public/issue/yql_warning.h>
#include <yql/essentials/core/sql_types/yql_callable_names.h>
#include <yql/essentials/utils/log/log_level.h>
@@ -2517,7 +2518,7 @@ public:
return nullptr;
}
} else if (name == "warning") {
- if (auto langver = NYql::MakeLangVersion(2026, 01);
+ if (auto langver = NYql::NFeature::PgPragmaWarning.MinLangVer;
!NYql::IsBackwardCompatibleFeatureAvailable(
Settings_.LangVer, langver, Settings_.BackportMode))
{
@@ -3725,6 +3726,33 @@ public:
return result;
}
+ bool ExtractCollationName(const CollateClause* value, TString& name) {
+ auto len = ListLength(value->collname);
+ if (len == 0) {
+ AddError("CollateClause: missing collation name");
+ return false;
+ }
+
+ auto x = ListNodeNth(value->collname, len - 1);
+ if (NodeTag(x) != T_String) {
+ NodeNotImplemented(value, x);
+ return false;
+ }
+
+ name = StrVal(x);
+ return true;
+ }
+
+ TAstNode* ParseCollateClause(const CollateClause* value, const TExprSettings& settings) {
+ AT_LOCATION(value);
+ // Collation propagation through arbitrary expressions (COALESCE, CASE, plain
+ // projections, ...) is not implemented - only ParseFuncCall picks up an explicit
+ // COLLATE that directly wraps one of its arguments (see there). Elsewhere COLLATE
+ // only affects collation-sensitive semantics (comparison, case mapping), not the
+ // expression's runtime value, so we just parse through to the wrapped expression.
+ return ParseExpr(value->arg, settings);
+ }
+
TAstNode* ParseExpr(const Node* node, const TExprSettings& settings) {
switch (NodeTag(node)) {
case T_A_Const: {
@@ -3742,6 +3770,9 @@ public:
case T_TypeCast: {
return ParseTypeCast(CAST_NODE(TypeCast, node), settings);
}
+ case T_CollateClause: {
+ return ParseCollateClause(CAST_NODE(CollateClause, node), settings);
+ }
case T_BoolExpr: {
return ParseBoolExpr(CAST_NODE(BoolExpr, node), settings);
}
@@ -4267,6 +4298,32 @@ public:
callSettings.push_back(QL(QA("range")));
}
+ if (!value->agg_star) {
+ TMaybe<TString> collation;
+ for (int i = 0; i < ListLength(value->args); ++i) {
+ auto x = ListNodeNth(value->args, i);
+ if (NodeTag(x) != T_CollateClause) {
+ continue;
+ }
+
+ TString collationName;
+ if (!ExtractCollationName(CAST_NODE(CollateClause, x), collationName)) {
+ return nullptr;
+ }
+
+ if (collation && *collation != collationName) {
+ AddError(TStringBuilder() << "FuncCall: conflicting explicit collations: " << *collation << " and " << collationName);
+ return nullptr;
+ }
+
+ collation = collationName;
+ }
+
+ if (collation) {
+ callSettings.push_back(QL(QA("collation"), QAX(*collation)));
+ }
+ }
+
args.push_back(QVL(callSettings.data(), callSettings.size()));
if (value->agg_star) {
if (name != "count") {
diff --git a/yql/essentials/sql/pg/pg_sql_ut.cpp b/yql/essentials/sql/pg/pg_sql_ut.cpp
index 2d07350d6ea..18477d3a807 100644
--- a/yql/essentials/sql/pg/pg_sql_ut.cpp
+++ b/yql/essentials/sql/pg/pg_sql_ut.cpp
@@ -1,5 +1,7 @@
#include "ut/util.h"
+#include <yql/essentials/core/langver/feature.gen.h>
+
#include <library/cpp/testing/unittest/registar.h>
#include <yql/essentials/parser/pg_wrapper/interface/parser.h>
@@ -1138,7 +1140,7 @@ Y_UNIT_TEST(WarningAsError) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1155,7 +1157,7 @@ Y_UNIT_TEST(WarningDisable) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(res.Root);
UNIT_ASSERT_EQUAL(res.Issues.Size(), 0);
@@ -1168,7 +1170,7 @@ Y_UNIT_TEST(WarningDefault) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1184,7 +1186,7 @@ Y_UNIT_TEST(WarningInvalidAction) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1200,7 +1202,7 @@ Y_UNIT_TEST(WarningInvalidPattern) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1216,7 +1218,7 @@ Y_UNIT_TEST(WarningMissingArguments) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1232,7 +1234,7 @@ Y_UNIT_TEST(WarningTooManyArguments) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1249,7 +1251,7 @@ Y_UNIT_TEST(WarningSpecificCode) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1266,7 +1268,7 @@ Y_UNIT_TEST(WarningSpecificCodeDisable) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(res.Root);
UNIT_ASSERT_EQUAL(res.Issues.Size(), 0);
@@ -1278,7 +1280,7 @@ Y_UNIT_TEST(WarningNonStringAction) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1294,7 +1296,7 @@ Y_UNIT_TEST(WarningNonStringPattern) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1312,7 +1314,7 @@ Y_UNIT_TEST(WarningMultipleRules) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
@@ -1328,7 +1330,7 @@ Y_UNIT_TEST(WarningNoArguments) {
)sql";
TTranslationSettings settings;
- settings.LangVer = NYql::MakeLangVersion(2026, 01);
+ settings.LangVer = NYql::NFeature::PgPragmaWarning.MinLangVer;
auto res = SqlToYqlWithMode(query, NSQLTranslation::ESqlMode::QUERY, 10, {}, EDebugOutput::None, /*ansiLexer=*/false, settings);
UNIT_ASSERT(!res.Root);
UNIT_ASSERT(res.Issues.Size() > 0);
diff --git a/yql/essentials/sql/pg/ya.make b/yql/essentials/sql/pg/ya.make
index ab493603bf5..edbcfa2aa30 100644
--- a/yql/essentials/sql/pg/ya.make
+++ b/yql/essentials/sql/pg/ya.make
@@ -6,6 +6,7 @@ PROVIDES(
PEERDIR(
yql/essentials/ast
+ yql/essentials/core/langver
yql/essentials/core/sql_types
yql/essentials/parser/pg_catalog
yql/essentials/minikql
diff --git a/yql/essentials/sql/v1/SQLv1Antlr4.g.in b/yql/essentials/sql/v1/SQLv1Antlr4.g.in
index d1eba59441a..54ffbdeec54 100644
--- a/yql/essentials/sql/v1/SQLv1Antlr4.g.in
+++ b/yql/essentials/sql/v1/SQLv1Antlr4.g.in
@@ -882,6 +882,7 @@ column_option:
| default_value
| compression
| encoding
+ | generated_always
;
compression: COMPRESSION LPAREN (compression_setting_entry (COMMA compression_setting_entry)*)? COMMA? RPAREN;
@@ -896,6 +897,7 @@ encoding_config: encoding_config_name (LPAREN (encoding_setting_entry (COMMA enc
encoding_config_name: an_id_or_type;
encoding_setting_entry: an_id EQUALS encoding_setting_value;
encoding_setting_value: integer | id;
+generated_always: (GENERATED ALWAYS)? AS LPAREN expr RPAREN (STORED | VIRTUAL)?;
column_order_by_specification: an_id (ASC | DESC)?;
@@ -1476,6 +1478,7 @@ keyword_as_compat:
| ADD
| AFTER
| ALTER
+ | ALWAYS
| ANALYZE
| AND
| ANSI
@@ -1551,6 +1554,7 @@ keyword_as_compat:
| FOR
| FOREIGN
| FUNCTION
+ | GENERATED
| GLOB
| GLOBAL
| GRANT
@@ -1669,6 +1673,7 @@ keyword_as_compat:
| SOURCE
| START
| STATISTICS
+ | STORED
| STREAMING
| SUBQUERY
| SUBSET
@@ -1717,6 +1722,7 @@ keyword_compat: (
| ADD
| AFTER
| ALTER
+ | ALWAYS
| ANALYZE
| AND
| ANSI
@@ -1792,6 +1798,7 @@ keyword_compat: (
| FOR
| FOREIGN
| FUNCTION
+ | GENERATED
| GLOB
| GLOBAL
| GRANT
@@ -1910,6 +1917,7 @@ keyword_compat: (
| SOURCE
| START
| STATISTICS
+ | STORED
| STREAMING
| SUBQUERY
| SUBSET
@@ -2055,6 +2063,7 @@ ADD: A D D;
AFTER: A F T E R;
ALL: A L L;
ALTER: A L T E R;
+ALWAYS: A L W A Y S;
ANALYZE: A N A L Y Z E;
AND: A N D;
ANSI: A N S I;
@@ -2159,6 +2168,7 @@ FOREIGN: F O R E I G N;
FROM: F R O M;
FULL: F U L L;
FUNCTION: F U N C T I O N;
+GENERATED: G E N E R A T E D;
GLOB: G L O B;
GLOBAL: G L O B A L;
GRANT: G R A N T;
@@ -2300,6 +2310,7 @@ SECRET: S E C R E T;
SEQUENCE: S E Q U E N C E;
SOURCE: S O U R C E;
START: S T A R T;
+STORED: S T O R E D;
STATISTICS: S T A T I S T I C S;
STREAM: S T R E A M;
STREAMING: S T R E A M I N G;
diff --git a/yql/essentials/sql/v1/builtin.cpp b/yql/essentials/sql/v1/builtin.cpp
index cb91f956f89..8fcb3ae0945 100644
--- a/yql/essentials/sql/v1/builtin.cpp
+++ b/yql/essentials/sql/v1/builtin.cpp
@@ -10,6 +10,7 @@
#include <yql/essentials/ast/yql_type_string.h>
#include <yql/essentials/public/udf/udf_data_type.h>
#include <yql/essentials/core/sql_types/simple_types.h>
+#include <yql/essentials/core/langver/feature.gen.h>
#include <yql/essentials/minikql/mkql_program_builder.h>
#include <yql/essentials/minikql/mkql_type_ops.h>
#include <yql/essentials/public/issue/yql_issue_id.h>
@@ -860,7 +861,35 @@ public:
}
Args_[0] = BuildQuotedAtom(Args_[0]->GetPos(), Args_[0]->GetLiteralValue());
- Args_.insert(Args_.begin() + 1, RangeFunction ? Q(Y(Q(Y(Q("range"))))) : Q(Y()));
+
+ TNodePtr settingsList = Y();
+ if (RangeFunction) {
+ settingsList = L(settingsList, Q(Y(Q("range"))));
+ }
+
+ // Optional named argument: PgCall("upper", x, "de-DE-x-icu" as Collation)
+ for (size_t i = 1; i < Args_.size();) {
+ const auto& label = Args_[i]->GetLabel();
+ if (label.empty()) {
+ ++i;
+ continue;
+ }
+
+ if (label != "Collation") {
+ ctx.Error(Args_[i]->GetPos()) << "PgCall: unsupported named argument: " << label << "; expected: Collation";
+ return false;
+ }
+
+ auto arg = Args_[i];
+ if (!arg->Init(ctx, src)) {
+ return false;
+ }
+
+ settingsList = L(settingsList, Q(Y(Q("collation"), MakeAtomFromExpression(Pos_, ctx, arg).Build())));
+ Args_.erase(Args_.begin() + i);
+ }
+
+ Args_.insert(Args_.begin() + 1, Q(settingsList));
return TCallNode::DoInit(ctx, src);
}
@@ -3115,7 +3144,7 @@ struct TBuiltinFuncData {
{"byteat", {"ByteAt", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("ByteAt", 2, 2)}},
{"startswith", {"StartsWith", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("StartsWith", 2, 2)}},
{"endswith", {"EndsWith", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("EndsWith", 2, 2)}},
- {"concat", {"Concat", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("SqlConcat", 1, -1), NYql::MakeLangVersion(2025, 4)}},
+ {"concat", {"Concat", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("SqlConcat", 1, -1), NYql::NFeature::ConcatFunction.MinLangVer}},
// Numeric builtins
{"abs", {"Abs", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("Abs", 1, 1)}},
@@ -3207,30 +3236,30 @@ struct TBuiltinFuncData {
{"dictlookup", {"DictLookup", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("Lookup", 2, 2)}},
{"dictcontains", {"DictContains", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("Contains", 2, 2)}},
- {"todynamiclinear", {"ToDynamicLinear", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("ToDynamicLinear", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"fromdynamiclinear", {"FromDynamicLinear", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("FromDynamicLinear", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"lineardestroy", {"LinearDestroy", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearDestroy", 1, -1), NYql::MakeLangVersion(2025, 5)}},
+ {"todynamiclinear", {"ToDynamicLinear", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("ToDynamicLinear", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"fromdynamiclinear", {"FromDynamicLinear", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("FromDynamicLinear", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"lineardestroy", {"LinearDestroy", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearDestroy", 1, -1), NYql::NFeature::LinearDestroy.MinLangVer}},
// MutDict builtins
- {"mutdictcreate", {"MutDictCreate", "Normal", BuildSimpleBuiltinFactoryCallback<TMutDictCreateBuiltin>(), NYql::MakeLangVersion(2025, 4)}},
- {"tomutdict", {"ToMutDict", "Normal", BuildSimpleBuiltinFactoryCallback<TToMutDictBuiltin>(), NYql::MakeLangVersion(2025, 4)}},
- {"frommutdict", {"FromMutDict", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("FromMutDict", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictinsert", {"MutDictInsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictInsert", 3, 3), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictupsert", {"MutDictUpsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictUpsert", 3, 3), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictupdate", {"MutDictUpdate", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictUpdate", 3, 3), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictremove", {"MutDictRemove", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictRemove", 2, 2), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictpop", {"MutDictPop", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictPop", 2, 2), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictcontains", {"MutDictContains", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictContains", 2, 2), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictlookup", {"MutDictLookup", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictLookup", 2, 2), NYql::MakeLangVersion(2025, 4)}},
- {"mutdicthasitems", {"MutDictHasItems", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictHasItems", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictlength", {"MutDictLength", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictLength", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictitems", {"MutDictItems", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictItems", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictkeys", {"MutDictKeys", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictKeys", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"mutdictpayloads", {"MutDictPayloads", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictPayloads", 1, 1), NYql::MakeLangVersion(2025, 4)}},
+ {"mutdictcreate", {"MutDictCreate", "Normal", BuildSimpleBuiltinFactoryCallback<TMutDictCreateBuiltin>(), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"tomutdict", {"ToMutDict", "Normal", BuildSimpleBuiltinFactoryCallback<TToMutDictBuiltin>(), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"frommutdict", {"FromMutDict", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("FromMutDict", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictinsert", {"MutDictInsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictInsert", 3, 3), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictupsert", {"MutDictUpsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictUpsert", 3, 3), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictupdate", {"MutDictUpdate", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictUpdate", 3, 3), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictremove", {"MutDictRemove", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictRemove", 2, 2), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictpop", {"MutDictPop", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictPop", 2, 2), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictcontains", {"MutDictContains", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictContains", 2, 2), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictlookup", {"MutDictLookup", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictLookup", 2, 2), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdicthasitems", {"MutDictHasItems", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictHasItems", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictlength", {"MutDictLength", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictLength", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictitems", {"MutDictItems", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictItems", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictkeys", {"MutDictKeys", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictKeys", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"mutdictpayloads", {"MutDictPayloads", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("MutDictPayloads", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
- {"dictinsert", {"DictInsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictInsert", 3, 3), NYql::MakeLangVersion(2025, 4)}},
- {"dictupsert", {"DictUpsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictUpsert", 3, 3), NYql::MakeLangVersion(2025, 4)}},
- {"dictupdate", {"DictUpdate", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictUpdate", 3, 3), NYql::MakeLangVersion(2025, 4)}},
- {"dictremove", {"DictRemove", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictRemove", 2, 2), NYql::MakeLangVersion(2025, 4)}},
+ {"dictinsert", {"DictInsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictInsert", 3, 3), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"dictupsert", {"DictUpsert", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictUpsert", 3, 3), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"dictupdate", {"DictUpdate", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictUpdate", 3, 3), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"dictremove", {"DictRemove", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictRemove", 2, 2), NYql::NFeature::LinearTypes.MinLangVer}},
// Atom builtins
{"asatom", {"AsAtom", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlAsAtom>()}},
@@ -3252,8 +3281,8 @@ struct TBuiltinFuncData {
{"ensuretype", {"EnsureType", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlTypeAssert<true>>()}},
{"ensureconvertibleto", {"EnsureConvertibleTo", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlTypeAssert<false>>()}},
{"ensure", {"Ensure", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("EnsureWarn", 2, 3)}},
- {"withsideeffects", {"WithSideEffects", "Normal", BuildSimpleBuiltinFactoryCallback<TSideEffects<false>>(), NYql::MakeLangVersion(2025, 4)}},
- {"withsideeffectsmode", {"WithSideEffectsMode", "Normal", BuildSimpleBuiltinFactoryCallback<TSideEffects<true>>(), NYql::MakeLangVersion(2025, 4)}},
+ {"withsideeffects", {"WithSideEffects", "Normal", BuildSimpleBuiltinFactoryCallback<TSideEffects<false>>(), NYql::NFeature::SideEffects.MinLangVer}},
+ {"withsideeffectsmode", {"WithSideEffectsMode", "Normal", BuildSimpleBuiltinFactoryCallback<TSideEffects<true>>(), NYql::NFeature::SideEffects.MinLangVer}},
{"evaluateexpr", {"EvaluateExpr", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("EvaluateExpr", 1, 1)}},
{"evaluateatom", {"EvaluateAtom", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("EvaluateAtom", 1, 1)}},
{"evaluatetype", {"EvaluateType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("EvaluateType", 1, 1)}},
@@ -3278,12 +3307,12 @@ struct TBuiltinFuncData {
{"pgarray", {"PgArray", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("PgArray", 1, -1)}},
{"typeof", {"TypeOf", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("TypeOf", 1, 1)}},
{"instanceof", {"InstanceOf", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("InstanceOf", 1, 1)}},
- {"positionof", {"PositionOf", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("PositionOf", 1, 1), NYql::MakeLangVersion(2026, 1)}},
- {"withissue", {"WithIssue", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlWithIssue>(), NYql::MakeLangVersion(2026, 1)}},
+ {"positionof", {"PositionOf", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("PositionOf", 1, 1), NYql::NFeature::PositionOfWithIssueFunction.MinLangVer}},
+ {"withissue", {"WithIssue", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlWithIssue>(), NYql::NFeature::PositionOfWithIssueFunction.MinLangVer}},
{"datatype", {"DataType", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlDataType>()}},
{"optionaltype", {"OptionalType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("OptionalType", 1, 1)}},
- {"asoptionaltype", {"AsOptionalType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("AsOptionalType", 1, 1), NYql::MakeLangVersion(2026, 1)}},
- {"asoptional", {"AsOptional", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("AsOptional", 1, 1), NYql::MakeLangVersion(2026, 1)}},
+ {"asoptionaltype", {"AsOptionalType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("AsOptionalType", 1, 1), NYql::NFeature::AsOptionalFunction.MinLangVer}},
+ {"asoptional", {"AsOptional", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("AsOptional", 1, 1), NYql::NFeature::AsOptionalFunction.MinLangVer}},
{"listtype", {"ListType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("ListType", 1, 1)}},
{"streamtype", {"StreamType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("StreamType", 1, 1)}},
{"dicttype", {"DictType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DictType", 2, 2)}},
@@ -3298,9 +3327,9 @@ struct TBuiltinFuncData {
{"taggedtype", {"TaggedType", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlTaggedType>()}},
{"varianttype", {"VariantType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("VariantType", 1, 1)}},
{"callabletype", {"CallableType", "Normal", BuildSimpleBuiltinFactoryCallback<TYqlCallableType>()}},
- {"lineartype", {"LinearType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearType", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"dynamiclineartype", {"DynamicLinearType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DynamicLinearType", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"linearitemtype", {"LinearItemType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearItemType", 1, 1), NYql::MakeLangVersion(2025, 4)}},
+ {"lineartype", {"LinearType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearType", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"dynamiclineartype", {"DynamicLinearType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DynamicLinearType", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"linearitemtype", {"LinearItemType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearItemType", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
{"optionalitemtype", {"OptionalItemType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("OptionalItemType", 1, 1)}},
{"listitemtype", {"ListItemType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("ListItemType", 1, 1)}},
{"streamitemtype", {"ListItemType", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("StreamItemType", 1, 1)}},
@@ -3335,8 +3364,8 @@ struct TBuiltinFuncData {
{"datatypecomponents", {"DataTypeComponents", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DataTypeComponents", 1, 1)}},
{"datatypehandle", {"DataTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DataTypeHandle", 1, 1)}},
{"optionaltypehandle", {"OptionalTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("OptionalTypeHandle", 1, 1)}},
- {"lineartypehandle", {"LinearTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearTypeHandle", 1, 1), NYql::MakeLangVersion(2025, 4)}},
- {"dynamiclineartypehandle", {"DynamicLinearTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DynamicLinearTypeHandle", 1, 1), NYql::MakeLangVersion(2025, 4)}},
+ {"lineartypehandle", {"LinearTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("LinearTypeHandle", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
+ {"dynamiclineartypehandle", {"DynamicLinearTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("DynamicLinearTypeHandle", 1, 1), NYql::NFeature::LinearTypes.MinLangVer}},
{"listtypehandle", {"ListTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("ListTypeHandle", 1, 1)}},
{"streamtypehandle", {"StreamTypeHandle", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("StreamTypeHandle", 1, 1)}},
{"tupletypecomponents", {"TupleTypeComponents", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("TupleTypeComponents", 1, 1)}},
@@ -3412,7 +3441,7 @@ struct TBuiltinFuncData {
{"coalesce", {"Coalesce", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("Coalesce", 1, -1)}},
{"nvl", {"Nvl", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("Coalesce", 1, -1)}},
{"nanvl", {"Nanvl", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("Nanvl", 2, 2)}},
- {"nullif", {"NullIf", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("NullIf", 2, 2), NYql::MakeLangVersion(2025, 4)}},
+ {"nullif", {"NullIf", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("NullIf", 2, 2), NYql::NFeature::NullIfFunction.MinLangVer}},
{"likely", {"Likely", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("Likely", 1, -1)}},
{"assumestrict", {"AssumeStrict", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("AssumeStrict", 1, 1)}},
{"assumenonstrict", {"AssumeNonStrict", "Normal", BuildNamedArgcBuiltinFactoryCallback<TCallNodeImpl>("AssumeNonStrict", 1, 1)}},
@@ -3572,8 +3601,8 @@ struct TBuiltinFuncData {
{"first", {"First", "MatchRec", BuildAggrFuncFactoryCallback("First", "first_traits_factory")}},
{"last", {"Last", "MatchRec", BuildAggrFuncFactoryCallback("Last", "last_traits_factory")}},
- {"randomsample", {"RandomSample", "Agg", BuildAggrFuncFactoryCallback("RandomSample", "random_sample_factory", RANDOM_SAMPLE), NYql::MakeLangVersion(2025, 4)}},
- {"randomvalue", {"RandomValue", "Agg", BuildAggrFuncFactoryCallback("RandomValue", "random_value_factory", RANDOM_VALUE), NYql::MakeLangVersion(2025, 4)}},
+ {"randomsample", {"RandomSample", "Agg", BuildAggrFuncFactoryCallback("RandomSample", "random_sample_factory", RANDOM_SAMPLE), NYql::NFeature::RandomValueSampleFunction.MinLangVer}},
+ {"randomvalue", {"RandomValue", "Agg", BuildAggrFuncFactoryCallback("RandomValue", "random_value_factory", RANDOM_VALUE), NYql::NFeature::RandomValueSampleFunction.MinLangVer}},
};
// NOLINTEND(modernize-use-designated-initializers)
return aggrFuncs;
@@ -4218,6 +4247,28 @@ TNodeResult BuildBuiltinFunc(
return TNonNull(TNodePtr(new TInvalidBuiltin(pos, "DISTINCT can only be used in aggregation functions")));
}
+ if ((normalizedName == "pgcall" || normalizedName == "pgrangecall") && mustUseNamed && *mustUseNamed) {
+ // PgCall/PgRangeCall support one named argument (collation => ...); TYqlPgCall::DoInit
+ // finds it by label, so just flatten the (positional tuple, named struct) pair that
+ // named-arg calls get reshaped into back into a single labeled arg list.
+ *mustUseNamed = false;
+ YQL_ENSURE(args.size() == 2);
+ auto positional = args[0]->GetTupleNode();
+ YQL_ENSURE(positional);
+ auto named = args[1]->GetStructNode();
+ YQL_ENSURE(named);
+
+ TVector<TNodePtr> flatArgs(positional->Elements());
+ for (const auto& arg : named->GetExprs()) {
+ flatArgs.push_back(arg);
+ }
+
+ const bool isRange = (normalizedName == "pgrangecall");
+ return TNonNull(isRange
+ ? TNodePtr(new TYqlPgCall<true>(pos, flatArgs))
+ : TNodePtr(new TYqlPgCall<false>(pos, flatArgs)));
+ }
+
auto builtinCallback = builtinFuncs.find(normalizedName);
if (builtinCallback != builtinFuncs.end()) {
const auto& funcInfo = builtinCallback->second;
diff --git a/yql/essentials/sql/v1/format/sql_format_ut.h b/yql/essentials/sql/v1/format/sql_format_ut.h
index ce5d448c9dd..6406608c26b 100644
--- a/yql/essentials/sql/v1/format/sql_format_ut.h
+++ b/yql/essentials/sql/v1/format/sql_format_ut.h
@@ -432,6 +432,20 @@ Y_UNIT_TEST(CreateTable) {
{"create table\tuser(key int32, val String encoding(off))", "CREATE TABLE user (\n\tkey int32,\n\tval String ENCODING (off)\n);\n"},
{"create table\tuser(key int32, val String encoding())", "CREATE TABLE user (\n\tkey int32,\n\tval String ENCODING ()\n);\n"},
{"create table user(key int32, val String encoding(dict(max_size=100)))", "CREATE TABLE user (\n\tkey int32,\n\tval String ENCODING (dict (max_size = 100))\n);\n"},
+ {"create table user(key int32, val int64 generated always as (key+1) stored)",
+ "CREATE TABLE user (\n\tkey int32,\n\tval int64 GENERATED ALWAYS AS (key + 1) STORED\n);\n"},
+ {"create table user(key int32, val int64 generated always as (key+1) virtual)",
+ "CREATE TABLE user (\n\tkey int32,\n\tval int64 GENERATED ALWAYS AS (key + 1) VIRTUAL\n);\n"},
+ {"create table user(key int32, val int64 generated always as (key+1))",
+ "CREATE TABLE user (\n\tkey int32,\n\tval int64 GENERATED ALWAYS AS (key + 1)\n);\n"},
+ {"create table user(key int32, val int64 as (key+1) stored)",
+ "CREATE TABLE user (\n\tkey int32,\n\tval int64 AS (key + 1) STORED\n);\n"},
+ {"create table user(key int32, val int64 as (key+1))",
+ "CREATE TABLE user (\n\tkey int32,\n\tval int64 AS (key + 1)\n);\n"},
+ {"create table user(key int32, val int64 GeNeRaTeD AlWaYs As (key+1) StOrEd)",
+ "CREATE TABLE user (\n\tkey int32,\n\tval int64 GENERATED ALWAYS AS (key + 1) STORED\n);\n"},
+ {"create table user(key int32, val int64 (not null, generated always as (key+1) stored))",
+ "CREATE TABLE user (\n\tkey int32,\n\tval int64 (NOT NULL, GENERATED ALWAYS AS (key + 1) STORED)\n);\n"},
};
TSetup setup;
@@ -691,6 +705,18 @@ Y_UNIT_TEST(AlterTable) {
"ALTER TABLE t\n\tALTER COLUMN c SET ENCODING ()\n;\n"},
{"alter table t alter column c set encoding(dict(max_size=100))",
"ALTER TABLE t\n\tALTER COLUMN c SET ENCODING (dict (max_size = 100))\n;\n"},
+ {"alter table user add column val int64 generated always as (key+1) stored",
+ "ALTER TABLE user\n\tADD COLUMN val int64 GENERATED ALWAYS AS (key + 1) STORED\n;\n"},
+ {"alter table user add column val int64 generated always as (key+1) virtual",
+ "ALTER TABLE user\n\tADD COLUMN val int64 GENERATED ALWAYS AS (key + 1) VIRTUAL\n;\n"},
+ {"alter table user add column val int64 generated always as (key+1)",
+ "ALTER TABLE user\n\tADD COLUMN val int64 GENERATED ALWAYS AS (key + 1)\n;\n"},
+ {"alter table user add column val int64 as (key+1) stored",
+ "ALTER TABLE user\n\tADD COLUMN val int64 AS (key + 1) STORED\n;\n"},
+ {"alter table user add val int64 GeNeRaTeD AlWaYs As (key+1) StOrEd",
+ "ALTER TABLE user\n\tADD val int64 GENERATED ALWAYS AS (key + 1) STORED\n;\n"},
+ {"alter table user add column val int64 (not null, generated always as (key+1) stored)",
+ "ALTER TABLE user\n\tADD COLUMN val int64 (NOT NULL, GENERATED ALWAYS AS (key + 1) STORED)\n;\n"},
};
TSetup setup;
diff --git a/yql/essentials/sql/v1/node.h b/yql/essentials/sql/v1/node.h
index c9e8b16a799..b79eb7d04bc 100644
--- a/yql/essentials/sql/v1/node.h
+++ b/yql/essentials/sql/v1/node.h
@@ -791,12 +791,19 @@ struct TEncoding {
TMap<TString, TNodePtr> Entries;
};
+struct TGeneratedColumn {
+ TString ContextPrefix;
+ TString ExprBody;
+ bool Stored = false;
+};
+
struct TColumnOptions {
TNodePtr DefaultExpr;
TVector<TIdentifier> Families;
TMaybe<TCompression> Compression;
bool Nullable = true;
TMaybe<TVector<TEncoding>> ColumnEncoding;
+ TMaybe<TGeneratedColumn> Generated;
};
struct TColumnSchema {
@@ -821,6 +828,7 @@ struct TColumnSchema {
bool Nullable = false;
bool Serial = false;
TMaybe<TVector<TEncoding>> ColumnEncoding;
+ TMaybe<TGeneratedColumn> Generated;
};
struct TColumns: public TSimpleRefCount<TColumns> {
diff --git a/yql/essentials/sql/v1/query.cpp b/yql/essentials/sql/v1/query.cpp
index 6ec40c25ce5..0276df12534 100644
--- a/yql/essentials/sql/v1/query.cpp
+++ b/yql/essentials/sql/v1/query.cpp
@@ -490,6 +490,13 @@ INode::TPtr CreateEncodingsListSettings(const TVector<TEncoding>& columnEncoding
return encodingsList;
}
+INode::TPtr CreateGeneratedValue(const TGeneratedColumn& generated, TPosition pos, const INode& node) {
+ const ui32 flags = NYql::TAstNodeFlags::ArbitraryContent | NYql::TAstNodeFlags::UnstableFormat;
+ auto contextText = BuildQuotedAtom(pos, generated.ContextPrefix, flags);
+ auto exprText = BuildQuotedAtom(pos, generated.ExprBody, flags);
+ return node.Q(node.Y(contextText, exprText, node.Q(generated.Stored ? "stored" : "virtual")));
+}
+
} // namespace
class TPrepTableKeys: public ITableKeys {
@@ -1357,6 +1364,11 @@ public:
columnConstraints = L(columnConstraints, Q(Y(Q("default"), col.DefaultExpr)));
}
+ if (col.Generated) {
+ auto generatedValue = CreateGeneratedValue(*col.Generated, Pos_, *this);
+ columnConstraints = L(columnConstraints, Q(Y(Q("generated"), generatedValue)));
+ }
+
columnDesc = L(columnDesc, Q(Y(Q("columnConstrains"), Q(columnConstraints))));
if (col.Compression) {
@@ -1715,6 +1727,11 @@ public:
columnConstraints = L(columnConstraints, Q(Y(Q("default"), col.DefaultExpr)));
}
+ if (col.Generated) {
+ auto generatedValue = CreateGeneratedValue(*col.Generated, Pos_, *this);
+ columnConstraints = L(columnConstraints, Q(Y(Q("generated"), generatedValue)));
+ }
+
columnDesc = L(columnDesc, Q(Y(Q("columnConstrains"), Q(columnConstraints))));
if (col.Compression) {
diff --git a/yql/essentials/sql/v1/select.cpp b/yql/essentials/sql/v1/select.cpp
index c1dc2acea1d..a2af18a0a6e 100644
--- a/yql/essentials/sql/v1/select.cpp
+++ b/yql/essentials/sql/v1/select.cpp
@@ -3916,15 +3916,16 @@ TSourcePtr BuildCombine(TPosition pos, TSourcePtr leftSource, TVector<TSortSpeci
class TWatermarkSource: public IProxySource {
public:
- TWatermarkSource(TPosition pos, TSourcePtr src, TNodePtr watermarkLambda)
+ TWatermarkSource(TPosition pos, TSourcePtr src, TNodePtr watermarkLambda, TNodePtr watermarkSettings)
: IProxySource(pos, src.Get())
, SourcePtr_(src)
, WatermarkLambda_(std::move(watermarkLambda))
+ , WatermarkSettings_(std::move(watermarkSettings))
{
}
TNodePtr Build(TContext& ctx) final {
- return Y("WatermarkGenerator", SourcePtr_->Build(ctx), WatermarkLambda_);
+ return Y("WatermarkGenerator", SourcePtr_->Build(ctx), WatermarkLambda_, WatermarkSettings_);
}
bool DoInit(TContext& ctx, ISource* src) final {
@@ -3936,20 +3937,25 @@ public:
return false;
}
+ if (!WatermarkSettings_->Init(ctx, this)) {
+ return false;
+ }
+
return IProxySource::DoInit(ctx, src);
}
TNodePtr DoClone() const final {
- return MakeIntrusive<TWatermarkSource>(Pos_, SourcePtr_->CloneSource(), WatermarkLambda_->Clone());
+ return MakeIntrusive<TWatermarkSource>(Pos_, SourcePtr_->CloneSource(), WatermarkLambda_->Clone(), WatermarkSettings_->Clone());
}
private:
TSourcePtr SourcePtr_;
TNodePtr WatermarkLambda_;
+ TNodePtr WatermarkSettings_;
};
-TSourcePtr BuildWatermarkSource(TPosition pos, TSourcePtr src, TNodePtr watermarkLambda) {
- return MakeIntrusive<TWatermarkSource>(pos, std::move(src), std::move(watermarkLambda));
+TSourcePtr BuildWatermarkSource(TPosition pos, TSourcePtr src, TNodePtr watermarkLambda, TNodePtr watermarkSettings) {
+ return MakeIntrusive<TWatermarkSource>(pos, std::move(src), std::move(watermarkLambda), std::move(watermarkSettings));
}
} // namespace NSQLTranslationV1
diff --git a/yql/essentials/sql/v1/source.h b/yql/essentials/sql/v1/source.h
index 1ebd69d050d..54a1127f0c6 100644
--- a/yql/essentials/sql/v1/source.h
+++ b/yql/essentials/sql/v1/source.h
@@ -314,7 +314,7 @@ TSourcePtr BuildSelectCore(
TColumnsSets&& distinctSets);
TSourcePtr BuildSelect(TPosition pos, TSourcePtr source, TNodePtr skipTake);
TSourcePtr BuildAnyColumnSource(TPosition pos);
-TSourcePtr BuildWatermarkSource(TPosition pos, TSourcePtr src, TNodePtr watermarkLambda);
+TSourcePtr BuildWatermarkSource(TPosition pos, TSourcePtr src, TNodePtr watermarkLambda, TNodePtr watermarkSettings);
enum class EReduceMode {
ByPartition,
diff --git a/yql/essentials/sql/v1/sql_query.cpp b/yql/essentials/sql/v1/sql_query.cpp
index c6de747288b..42228fa5586 100644
--- a/yql/essentials/sql/v1/sql_query.cpp
+++ b/yql/essentials/sql/v1/sql_query.cpp
@@ -2778,6 +2778,17 @@ bool TSqlQuery::AlterTableAddColumn(const TRule_alter_table_add_column& node, TA
Ctx_.Error() << "Several column families for a single column are not yet supported";
return false;
}
+ if (columnSchema->Generated) {
+ const TString& service = Ctx_.Scoped->CurrService;
+ if (service != KikimrProviderName && service != YdbProviderName) {
+ Ctx_.Error(columnSchema->Pos) << "GENERATED ALWAYS AS columns are supported only for the ydb provider";
+ return false;
+ }
+ if (params.TableType != ETableType::Table) {
+ Ctx_.Error(columnSchema->Pos) << "GENERATED ALWAYS AS columns are supported only for CREATE TABLE and ALTER TABLE";
+ return false;
+ }
+ }
params.AddColumns.push_back(*columnSchema);
return true;
}
@@ -5020,6 +5031,11 @@ bool BuildColumnFeatures(std::map<TString, TDeferredAtom>& result, const TRule_c
return false;
}
+ if (options->Generated) {
+ translation.Context().Error(pos) << "GENERATED ALWAYS AS columns are supported only for CREATE TABLE and ALTER TABLE";
+ return false;
+ }
+
auto& typeBind = columnSchema.GetRule_type_name_or_bind2();
switch (typeBind.Alt_case()) {
case TRule_type_name_or_bind::kAltTypeNameOrBind1: {
diff --git a/yql/essentials/sql/v1/sql_select.cpp b/yql/essentials/sql/v1/sql_select.cpp
index 48a87dd301c..6d1dbe6fac4 100644
--- a/yql/essentials/sql/v1/sql_select.cpp
+++ b/yql/essentials/sql/v1/sql_select.cpp
@@ -630,11 +630,28 @@ TSourcePtr TSqlSelect::HintedSingleSource(
}
TNodePtr watermarkLambda;
- if (auto it = hints.find("watermark"); it != hints.end() && !ret->IsTableSource()) {
- auto& exprs = it->second;
- YQL_ENSURE(exprs.size() == 1);
- watermarkLambda = std::move(exprs[0]);
- hints.erase(it);
+ TTableHints watermarkHints;
+ if (!ret->IsTableSource()) {
+ for (auto it = hints.begin(); it != hints.end();) {
+ auto& [name, value] = *it;
+ auto normalizedName = name;
+ auto normalizeError = NormalizeName(Ctx_.Pos(), normalizedName);
+ if (!normalizeError.Empty()) {
+ YQL_ENSURE(value.empty() || value.front());
+ const auto pos = value ? value.front()->GetPos() : Ctx_.Pos();
+ Ctx_.Error(pos) << normalizeError->GetMessage();
+ return nullptr;
+ } else if (normalizedName == "watermark") {
+ YQL_ENSURE(value.size() == 1);
+ watermarkLambda = std::move(value[0]);
+ it = hints.erase(it);
+ } else if (normalizedName.StartsWith("watermark")) {
+ watermarkHints.emplace(normalizedName, std::move(value));
+ it = hints.erase(it);
+ } else {
+ ++it;
+ }
+ }
}
if (hints || contextHints) {
@@ -649,7 +666,12 @@ TSourcePtr TSqlSelect::HintedSingleSource(
if (watermarkLambda) {
auto pos = watermarkLambda->GetPos();
- ret = BuildWatermarkSource(std::move(pos), std::move(ret), std::move(watermarkLambda));
+ auto watermarkSettings = BuildInputOptions(pos, watermarkHints);
+ if (!watermarkSettings) {
+ watermarkSettings = BuildList(pos);
+ }
+ watermarkSettings = BuildQuote(pos, watermarkSettings);
+ ret = BuildWatermarkSource(std::move(pos), std::move(ret), std::move(watermarkLambda), std::move(watermarkSettings));
}
return ret;
diff --git a/yql/essentials/sql/v1/sql_translation.cpp b/yql/essentials/sql/v1/sql_translation.cpp
index 141d416f6d4..c40afa7705e 100644
--- a/yql/essentials/sql/v1/sql_translation.cpp
+++ b/yql/essentials/sql/v1/sql_translation.cpp
@@ -17,6 +17,7 @@
#include <util/generic/scope.h>
#include <util/string/join.h>
+#include <util/string/strip.h>
#include <library/cpp/protobuf/util/simple_reflection.h>
@@ -1731,6 +1732,7 @@ TMaybe<TColumnOptions> ColumnOptions(const TRule_column_schema& node, TSqlTransl
TMaybe<TCompression> compression;
bool nullable = true;
TMaybe<TVector<TEncoding>> columnEncoding;
+ TMaybe<TGeneratedColumn> generated;
const auto& optionsList = node.GetRule_column_option_list3();
@@ -1740,9 +1742,10 @@ TMaybe<TColumnOptions> ColumnOptions(const TRule_column_schema& node, TSqlTransl
DefaultValue,
Compression,
Encoding,
+ Generated,
};
- TVector<TRule_column_option> columnOptions(Reserve(static_cast<size_t>(EOption::Encoding) + 1));
+ TVector<TRule_column_option> columnOptions(Reserve(static_cast<size_t>(EOption::Generated) + 1));
{
switch (optionsList.Alt_case()) {
@@ -1873,6 +1876,57 @@ TMaybe<TColumnOptions> ColumnOptions(const TRule_column_schema& node, TSqlTransl
break;
}
+ case TRule_column_option::kAltColumnOption6: { // generated_always
+ const auto& opt = rule.GetAlt_column_option6().GetRule_generated_always1();
+ if (std::find(usedOptions.begin(), usedOptions.end(), EOption::Generated) != usedOptions.end()) {
+ TPosition pos = ctx.Context().TokenPosition(opt.GetToken2());
+ ctx.Context().Error(pos) << "'GENERATED ALWAYS AS' option can be specified only once";
+ return {};
+ }
+
+ usedOptions.push_back(EOption::Generated);
+
+ {
+ TColumnRefScope scope(ctx.Context(), EColumnRefState::Allow);
+ TSqlExpression expr(ctx);
+ if (!Unwrap(expr.Build(opt.GetRule_expr4()))) {
+ return {};
+ }
+ }
+
+ TStringBuilder contextPrefix;
+ if (!BuildContextRecreationQuery(ctx.Context(), contextPrefix)) {
+ return {};
+ }
+
+ const auto& lparen = opt.GetToken3();
+ const auto& rparen = opt.GetToken5();
+ auto begin = GetQueryPosition(ctx.Context().Query, lparen);
+ auto end = GetQueryPosition(ctx.Context().Query, rparen);
+
+ if (begin == std::string::npos || end == std::string::npos) {
+ TPosition pos = ctx.Context().TokenPosition(opt.GetToken2());
+ ctx.Context().Error(pos) << "Failed to extract GENERATED ALWAYS AS expression text";
+ return {};
+ }
+
+ begin += lparen.value().size();
+ TString exprBody = StripString(ctx.Context().Query.substr(begin, end - begin));
+
+ bool generatedStored = false;
+ if (opt.HasBlock6()) {
+ const auto tokenId = opt.GetBlock6().GetToken1().GetId();
+ generatedStored = IS_TOKEN(tokenId, STORED);
+ }
+
+ generated = TGeneratedColumn{
+ .ContextPrefix = std::move(contextPrefix),
+ .ExprBody = std::move(exprBody),
+ .Stored = generatedStored,
+ };
+
+ break;
+ }
case TRule_column_option::ALT_NOT_SET:
YQL_ENSURE(false, "Unreachable");
}
@@ -1885,6 +1939,7 @@ TMaybe<TColumnOptions> ColumnOptions(const TRule_column_schema& node, TSqlTransl
.Compression = std::move(compression),
.Nullable = nullable,
.ColumnEncoding = std::move(columnEncoding),
+ .Generated = std::move(generated),
};
}
@@ -1899,7 +1954,7 @@ TMaybe<TColumnSchema> TSqlTranslation::ColumnSchemaImpl(const TRule_column_schem
return {};
}
- auto&& [defaultExpr, families, compression, nullable, columnEncoding] = columnOptions.GetRef();
+ auto&& [defaultExpr, families, compression, nullable, columnEncoding, generated] = columnOptions.GetRef();
if (!type) {
type = TypeNodeOrBind(node.GetRule_type_name_or_bind2());
@@ -1919,6 +1974,7 @@ TMaybe<TColumnSchema> TSqlTranslation::ColumnSchemaImpl(const TRule_column_schem
.Nullable = nullable,
.Serial = serial,
.ColumnEncoding = columnEncoding,
+ .Generated = generated,
};
}
@@ -2056,6 +2112,17 @@ bool TSqlTranslation::CreateTableEntry(const TRule_create_table_entry& node, TCr
Ctx_.Error() << "Several column families for a single column are not yet supported";
return false;
}
+ if (columnSchema->Generated) {
+ const TString& service = Ctx_.Scoped->CurrService;
+ if (service != KikimrProviderName && service != YdbProviderName) {
+ Ctx_.Error(columnSchema->Pos) << "GENERATED ALWAYS AS columns are supported only for the ydb provider";
+ return false;
+ }
+ if (params.TableType != ETableType::Table) {
+ Ctx_.Error(columnSchema->Pos) << "GENERATED ALWAYS AS columns are supported only for CREATE TABLE and ALTER TABLE";
+ return false;
+ }
+ }
params.Columns.push_back(*columnSchema);
break;
}
diff --git a/yql/essentials/sql/v1/sql_ut_common.h b/yql/essentials/sql/v1/sql_ut_common.h
index 05e7d38b87f..8558598e1da 100644
--- a/yql/essentials/sql/v1/sql_ut_common.h
+++ b/yql/essentials/sql/v1/sql_ut_common.h
@@ -12209,6 +12209,33 @@ Y_UNIT_TEST(ParseProtoseq) {
UNIT_ASSERT_VALUES_EQUAL(stat["WatermarkGenerator"], 1);
}
+Y_UNIT_TEST(ComplexParseWithOptions) {
+ auto res = SqlToYql(R"sql(
+ USE plato;
+
+ $input = SELECT Protobuf::Parse(records) AS event FROM Input
+ FLATTEN LIST BY (ChunksSplitters::Protoseq(Data).SplitRecords AS records);
+
+ SELECT * FROM $input
+ WITH (
+ WATERMARK = event.ts - Interval("PT1S"),
+ WATERMARK_GRANULARITY = "PT1S",
+ WATERMARK_IDLE_TIMEOUT = "PT1M"
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ TWordCountHive stat = {
+ "WatermarkGenerator",
+ "watermarkgranularity",
+ "watermarkidletimeout",
+ };
+ VerifyProgram(res, stat);
+ UNIT_ASSERT_VALUES_EQUAL(stat["WatermarkGenerator"], 1);
+ UNIT_ASSERT_VALUES_EQUAL(stat["watermarkgranularity"], 1);
+ UNIT_ASSERT_VALUES_EQUAL(stat["watermarkidletimeout"], 1);
+}
+
Y_UNIT_TEST(ExprList) {
auto res = SqlToYql(R"sql(
USE plato;
@@ -13532,7 +13559,7 @@ Y_UNIT_TEST(ReadsNamedNodeExpresionSubquery) {
Y_UNIT_TEST(ReadsProjectionFromSubquery) {
NSQLTranslation::TTranslationSettings s;
- s.LangVer = NYql::MakeLangVersion(2025, 4);
+ s.LangVer = NYql::NFeature::InlineSubquery.MinLangVer;
NYql::TAstParseResult res = SqlToYqlWithSettings(R"sql(
SELECT (SELECT a FROM plato.x) FROM (SELECT * FROM plato.y);
@@ -16163,4 +16190,415 @@ Y_UNIT_TEST(OptionalNull) {
} // Y_UNIT_TEST_SUITE(NullAsTypeName)
+Y_UNIT_TEST_SUITE(GeneratedColumns) {
+
+Y_UNIT_TEST(CreateTableGeneratedVirtualByDefault) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ CREATE TABLE tbl (
+ key Uint32,
+ gen Int32 AS (5),
+ PRIMARY KEY (key)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"5" 'virtual)))#");
+ UNIT_ASSERT(!program.Contains("stored"));
+ UNIT_ASSERT(!program.Contains(R"#((Int32 '"5"))#"));
+}
+
+Y_UNIT_TEST(CreateTableGeneratedExplicitVirtual) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ CREATE TABLE tbl (
+ key Uint32,
+ gen Int32 AS (5) VIRTUAL,
+ PRIMARY KEY (key)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"5" 'virtual)))#");
+ UNIT_ASSERT(!program.Contains("stored"));
+}
+
+Y_UNIT_TEST(CreateTableGeneratedStored) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ CREATE TABLE tbl (
+ key Uint32,
+ gen Int32 AS (5) STORED,
+ PRIMARY KEY (key)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"5" 'stored)))#");
+ UNIT_ASSERT(!program.Contains("virtual"));
+}
+
+Y_UNIT_TEST(CreateTableGeneratedAlwaysKeyword) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ CREATE TABLE tbl (
+ key Uint32,
+ gen Int32 GENERATED ALWAYS AS (5) STORED,
+ PRIMARY KEY (key)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"5" 'stored)))#");
+ UNIT_ASSERT(!program.Contains("virtual"));
+}
+
+Y_UNIT_TEST(CreateTableGeneratedWithoutAlways) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ CREATE TABLE tbl (
+ id Uint32,
+ Col2 Uint32,
+ Col3 Uint32,
+ Col1 Uint32 AS (2 + 3),
+ PRIMARY KEY (id)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"2 + 3" 'virtual)))#");
+ UNIT_ASSERT(!program.Contains("stored"));
+}
+
+Y_UNIT_TEST(CreateTableGeneratedDeterministicExpr) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ CREATE TABLE tbl (
+ key Uint32,
+ gen Int32 AS (1 + 2) VIRTUAL,
+ PRIMARY KEY (key)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"1 + 2" 'virtual)))#");
+ UNIT_ASSERT(!program.Contains("stored"));
+}
+
+Y_UNIT_TEST(CreateTableGeneratedNonDeterministicExpr) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ CREATE TABLE tbl (
+ key Uint32,
+ gen Uint64 AS (RandomNumber(1)) STORED,
+ PRIMARY KEY (key)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"RandomNumber(1)" 'stored)))#");
+ UNIT_ASSERT(!program.Contains("virtual"));
+}
+
+Y_UNIT_TEST(CreateTableGeneratedReferencesColumns) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ CREATE TABLE tbl (
+ a Int32,
+ b Int32,
+ gen Int32 GENERATED ALWAYS AS (a + b) STORED,
+ PRIMARY KEY (a)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"a + b" 'stored)))#");
+ UNIT_ASSERT(!program.Contains("virtual"));
+}
+
+Y_UNIT_TEST(CreateTableMultipleGeneratedColumns) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ CREATE TABLE tbl (
+ key Uint32,
+ x Int32 AS (1) VIRTUAL,
+ y Int32 AS (2) STORED,
+ PRIMARY KEY (key)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"1" 'virtual)))#");
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"2" 'stored)))#");
+}
+
+Y_UNIT_TEST(AlterTableAddGeneratedVirtualColumn) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ ALTER TABLE tbl ADD COLUMN gen Int32 AS (5);
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, "addColumns");
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"5" 'virtual)))#");
+ UNIT_ASSERT(!program.Contains("stored"));
+}
+
+Y_UNIT_TEST(AlterTableAddGeneratedStoredColumn) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ ALTER TABLE tbl ADD COLUMN gen Int32 GENERATED ALWAYS AS (a + b) STORED;
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, "addColumns");
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"a + b" 'stored)))#");
+ UNIT_ASSERT(!program.Contains("virtual"));
+ UNIT_ASSERT(!program.Contains("(Member row"));
+}
+
+Y_UNIT_TEST(AlterTableAddGeneratedColumnWithNamedLambda) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ $f = ($x) -> ($x * 2);
+ ALTER TABLE tbl ADD COLUMN gen Int32 GENERATED ALWAYS AS ($f(a)) STORED;
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, "addColumns");
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n$f = ($x) -> ($x * 2);\n" '"$f(a)" 'stored)))#");
+ UNIT_ASSERT(!program.Contains("virtual"));
+}
+
+Y_UNIT_TEST(CreateTableGeneratedColumnWithNamedNodeRef) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ $factor = 10;
+ CREATE TABLE tbl (
+ key Uint32,
+ num Int32,
+ gen Int32 GENERATED ALWAYS AS (num * $factor) STORED,
+ PRIMARY KEY (key)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n$factor = 10;\n" '"num * $factor" 'stored)))#");
+ UNIT_ASSERT(!program.Contains("virtual"));
+ UNIT_ASSERT(!program.Contains("Read"));
+}
+
+Y_UNIT_TEST(CreateTableGeneratedColumnWithSubqueryNamedNodeRef) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ $ids = (SELECT id FROM other);
+ CREATE TABLE tbl (
+ key Uint32,
+ num Int32,
+ gen Bool GENERATED ALWAYS AS (num IN $ids) STORED,
+ PRIMARY KEY (key)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n$ids = (SELECT id FROM other);\n" '"num IN $ids" 'stored)))#");
+ UNIT_ASSERT(!program.Contains("virtual"));
+ UNIT_ASSERT(!program.Contains("Read"));
+}
+
+Y_UNIT_TEST(CreateTableGeneratedColumnWithInlineSubquery) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ CREATE TABLE tbl (
+ key Uint32,
+ num Int32,
+ gen Bool GENERATED ALWAYS AS (num IN (SELECT id FROM other)) STORED,
+ PRIMARY KEY (key)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"num IN (SELECT id FROM other)" 'stored)))#");
+ UNIT_ASSERT(!program.Contains("virtual"));
+ UNIT_ASSERT(!program.Contains("Read"));
+}
+
+Y_UNIT_TEST(CreateTableGeneratedColumnWithScalarSubquery) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ CREATE TABLE tbl (
+ key Uint32,
+ num Int32,
+ gen Int32 GENERATED ALWAYS AS ((SELECT MAX(id) FROM other)) STORED,
+ PRIMARY KEY (key)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"(SELECT MAX(id) FROM other)" 'stored)))#");
+ UNIT_ASSERT(!program.Contains("virtual"));
+ UNIT_ASSERT(!program.Contains("Read"));
+}
+
+Y_UNIT_TEST(CreateTableGeneratedWithPragma) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ PRAGMA FlexibleTypes;
+ CREATE TABLE tbl (
+ key Uint32,
+ num Int32,
+ gen Int32 GENERATED ALWAYS AS (num / 2) STORED,
+ PRIMARY KEY (key)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\nPRAGMA FlexibleTypes;\n" '"num / 2" 'stored)))#");
+ UNIT_ASSERT(!program.Contains("virtual"));
+}
+
+Y_UNIT_TEST(CreateTableGeneratedNestedPath) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ CREATE TABLE `a/b/tbl` (
+ key Uint32,
+ num Int32,
+ gen Int32 GENERATED ALWAYS AS (num / 2) STORED,
+ PRIMARY KEY (key)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"num / 2" 'stored)))#");
+}
+
+Y_UNIT_TEST(AlterTableAddGeneratedColumnNestedPath) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ ALTER TABLE `a/b/tbl` ADD COLUMN gen Int32 GENERATED ALWAYS AS (a + b) STORED;
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"a + b" 'stored)))#");
+}
+
+Y_UNIT_TEST(CreateTableStoreGeneratedColumnFails) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ CREATE TABLESTORE tbl (
+ key Uint32 NOT NULL,
+ gen Int32 AS (5) STORED,
+ PRIMARY KEY (key)
+ );
+ )sql");
+ UNIT_ASSERT(!res.IsOk());
+ UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "GENERATED ALWAYS AS columns are supported only for CREATE TABLE and ALTER TABLE");
+}
+
+Y_UNIT_TEST(CreateExternalTableGeneratedColumnFails) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ CREATE EXTERNAL TABLE tbl (
+ a Int32,
+ gen Int32 AS (a) STORED
+ ) WITH (
+ DATA_SOURCE="/Root/mydatasource",
+ LOCATION="/folder1/*"
+ );
+ )sql");
+ UNIT_ASSERT(!res.IsOk());
+ UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "GENERATED ALWAYS AS columns are supported only for CREATE TABLE and ALTER TABLE");
+}
+
+Y_UNIT_TEST(AlterExternalTableAddGeneratedColumnFails) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ ALTER EXTERNAL TABLE tbl ADD COLUMN gen Int32 AS (5) STORED;
+ )sql");
+ UNIT_ASSERT(!res.IsOk());
+ UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "GENERATED ALWAYS AS columns are supported only for CREATE TABLE and ALTER TABLE");
+}
+
+Y_UNIT_TEST(AlterTableStoreAddGeneratedColumnFails) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ ALTER TABLESTORE tbl ADD COLUMN gen Int32 AS (5) STORED;
+ )sql");
+ UNIT_ASSERT(!res.IsOk());
+ UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "GENERATED ALWAYS AS columns are supported only for CREATE TABLE and ALTER TABLE");
+}
+
+Y_UNIT_TEST(CreateTableGeneratedColumnNonYdbProviderFails) {
+ auto res = SqlToYql(R"sql(
+ USE plato;
+ CREATE TABLE tbl (
+ a Int32,
+ gen Int32 AS (a) STORED,
+ PRIMARY KEY (a)
+ );
+ )sql");
+ UNIT_ASSERT(!res.IsOk());
+ UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "GENERATED ALWAYS AS columns are supported only for the ydb provider");
+}
+
+Y_UNIT_TEST(CreateTableGeneratedColumnNonYdbProviderVirtualFails) {
+ auto res = SqlToYql(R"sql(
+ USE plato;
+ CREATE TABLE tbl (
+ key Uint32,
+ gen Int32 GENERATED ALWAYS AS (5),
+ PRIMARY KEY (key)
+ );
+ )sql");
+ UNIT_ASSERT(!res.IsOk());
+ UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "GENERATED ALWAYS AS columns are supported only for the ydb provider");
+}
+
+Y_UNIT_TEST(AlterTableAddGeneratedColumnNonYdbProviderFails) {
+ auto res = SqlToYql(R"sql(
+ USE plato;
+ ALTER TABLE tbl ADD COLUMN gen Int32 AS (5) STORED;
+ )sql");
+ UNIT_ASSERT(!res.IsOk());
+ UNIT_ASSERT_STRING_CONTAINS(Err2Str(res), "ALTER TABLE is not supported for yt provider");
+}
+
+Y_UNIT_TEST(CreateTableGeneratedStringFunction) {
+ auto res = SqlToYql(R"sql(
+ USE ydb;
+ CREATE TABLE tbl (
+ id Int32,
+ name String,
+ upper_name String GENERATED ALWAYS AS (String::AsciiToUpper(name)) STORED,
+ PRIMARY KEY (id)
+ );
+ )sql");
+ UNIT_ASSERT_C(res.IsOk(), Err2Str(res));
+
+ const auto program = GetPrettyPrint(res);
+ UNIT_ASSERT_STRING_CONTAINS(program, R"#('('generated '('"USE ydb;\n" '"String::AsciiToUpper(name)" 'stored)))#");
+ UNIT_ASSERT(!program.Contains("virtual"));
+}
+
+} // Y_UNIT_TEST_SUITE(GeneratedColumns)
+
// NOLINTEND(misc-definitions-in-headers)