diff options
| author | Daniil Timižev <[email protected]> | 2026-07-01 13:12:00 +0300 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-07-01 10:12:00 +0000 |
| commit | d61eaf8a44444a54145492941973a4773ea987a5 (patch) | |
| tree | 5f075162732ea3c139f5ac296ec206dd183c323c | |
| parent | 336c5b67bf6ffc5d6c721191c918d8850a3dfbee (diff) | |
Inline literal DEFAULT column values into the query plan (#45049)
| -rw-r--r-- | ydb/core/kqp/opt/kqp_opt_kql.cpp | 127 | ||||
| -rw-r--r-- | ydb/core/kqp/opt/ya.make | 2 | ||||
| -rw-r--r-- | ydb/core/kqp/ut/scheme/kqp_constraints_ut.cpp | 1699 | ||||
| -rw-r--r-- | ydb/core/protos/table_service_config.proto | 3 |
4 files changed, 1794 insertions, 37 deletions
diff --git a/ydb/core/kqp/opt/kqp_opt_kql.cpp b/ydb/core/kqp/opt/kqp_opt_kql.cpp index 97207777041..a3e4a396be5 100644 --- a/ydb/core/kqp/opt/kqp_opt_kql.cpp +++ b/ydb/core/kqp/opt/kqp_opt_kql.cpp @@ -5,10 +5,16 @@ #include <ydb/core/kqp/common/kqp_yql.h> #include <ydb/core/kqp/provider/yql_kikimr_provider_impl.h> #include <ydb/core/kqp/provider/yql_kikimr_settings.h> +#include <ydb/library/mkql_proto/mkql_proto.h> #include <ydb/library/yql/providers/dq/common/yql_dq_settings.h> #include <yql/essentials/core/dq_integration/yql_dq_integration.h> #include <yql/essentials/core/yql_opt_utils.h> +#include <yql/essentials/minikql/computation/mkql_computation_node_holders.h> +#include <yql/essentials/minikql/mkql_alloc.h> +#include <yql/essentials/minikql/mkql_mem_info.h> +#include <yql/essentials/providers/common/codec/yql_codec.h> +#include <yql/essentials/providers/common/mkql/yql_type_mkql.h> #include <yql/essentials/providers/common/provider/yql_provider.h> #include <library/cpp/containers/absl_flat_hash/flat_hash_set.h> @@ -129,6 +135,86 @@ std::pair<TExprBase, TCoAtomList> ExtendInputRowsWithAbsentNullColumns(const TKi return {writeData, columnList}; } +TExprBase BuildYqlLiteralFromTypedValue(const Ydb::TypedValue& proto, TPositionHandle pos, TExprContext& ctx) { + NMiniKQL::TScopedAlloc alloc(__LOCATION__); + NMiniKQL::TTypeEnvironment typeEnv(alloc); + NMiniKQL::TMemoryUsageInfo memInfo("BuildYqlLiteralFromTypedValue"); + NMiniKQL::THolderFactory holderFactory(alloc.Ref(), memInfo); + + auto guard = typeEnv.BindAllocator(); + auto [mkqlType, unboxedValue] = NMiniKQL::ImportValueFromProto(proto.type(), proto.value(), typeEnv, holderFactory); + const TTypeAnnotationNode* typeAnnotation = NYql::NCommon::ConvertMiniKQLType(ctx.GetPosition(pos), mkqlType, ctx); + return TExprBase(NYql::NCommon::ValueToExprLiteral(typeAnnotation, unboxedValue, ctx, pos)); +} + +// Appends DEFAULT_KIND_LITERAL column values to every row in the input stream. +std::pair<TExprBase, TCoAtomList> ExtendInputRowsWithDefaultLiteralColumns(const TExprBase& input, const TCoAtomList& inputColumns, + const TCoAtomList& literalDefaultColumns, const TKikimrTableDescription& tableDesc, TPositionHandle pos, TExprContext& ctx) +{ + if (literalDefaultColumns.Ref().ChildrenSize() == 0) { + return {input, inputColumns}; + } + + auto rowArg = Build<TCoArgument>(ctx, pos).Name("row").Done(); + + TVector<TCoAtom> allColumns; + TVector<TExprBase> allMembers; + + for (const auto& col : inputColumns) { + allColumns.push_back(col); + allMembers.push_back( + Build<TCoNameValueTuple>(ctx, pos) + .Name(col) + .Value<TCoMember>() + .Struct(rowArg) + .Name().Build(col.Value()) + .Build() + .Done()); + } + + for (const auto& col : literalDefaultColumns) { + TString colName = TString(col.Value()); + const auto* colMeta = tableDesc.Metadata->Columns.FindPtr(colName); + YQL_ENSURE(colMeta && colMeta->IsDefaultFromLiteral()); + + TExprBase literalExpr = BuildYqlLiteralFromTypedValue(colMeta->DefaultFromLiteral, pos, ctx); + + const auto* colType = tableDesc.GetColumnType(colName); + YQL_ENSURE(colType); + + TExprBase memberValue = literalExpr; + if (colType->IsOptionalOrNull()) { + memberValue = Build<TCoJust>(ctx, pos) + .Input(literalExpr) + .Done(); + } + + auto nameAtom = TCoAtom(ctx.NewAtom(pos, colName)); + allColumns.push_back(nameAtom); + allMembers.push_back( + Build<TCoNameValueTuple>(ctx, pos) + .Name(nameAtom) + .Value(memberValue) + .Done()); + } + + auto writeData = Build<TCoMap>(ctx, pos) + .Input(input) + .Lambda() + .Args({rowArg}) + .Body<TCoAsStruct>() + .Add(allMembers) + .Build() + .Build() + .Done(); + + auto columnList = Build<TCoAtomList>(ctx, pos) + .Add(allColumns) + .Done(); + + return {writeData, columnList}; +} + bool HasIndexesToWrite(const TKikimrTableDescription& tableData) { bool hasIndexesToWrite = false; YQL_ENSURE(tableData.Metadata->Indexes.size() == tableData.Metadata->ImplTables.size()); @@ -315,22 +401,51 @@ std::pair<TExprBase, TCoAtomList> BuildWriteInput(const TKiWriteTable& write, co const TCoAtomList& inputColumns, const TCoAtomList& autoIncrement, const bool /*isSink*/, TPositionHandle pos, TExprContext& ctx, TKqpOptimizeContext& kqpCtx) { - auto input = write.Input(); + TExprBase input = write.Input(); + std::optional<TCoAtomList> inputCols; + + // Split autoIncrement into sequence columns (handled at runtime by TKqlSequencer) and + // literal defaults (inlined at compile time into the row struct via TCoMap/TCoAsStruct). + if (kqpCtx.Config->GetEnableCompileTimeDefaults()) { + TVector<TExprNode::TPtr> sequenceColNodes; + TVector<TExprNode::TPtr> literalDefaultColNodes; + + for (const auto& colAtom : autoIncrement) { + const auto* colMeta = table.Metadata->Columns.FindPtr(TString(colAtom.Value())); + if (colMeta && colMeta->IsDefaultFromLiteral() && !colMeta->DefaultFromLiteral.type().has_pg_type()) { + literalDefaultColNodes.push_back(colAtom.Ptr()); + } else { + sequenceColNodes.push_back(colAtom.Ptr()); + } + } - TCoAtomList inputCols = BuildUpsertInputColumns(inputColumns, autoIncrement, pos, ctx); + auto sequenceCols = Build<TCoAtomList>(ctx, pos).Add(sequenceColNodes).Done(); + auto literalDefaultCols = Build<TCoAtomList>(ctx, pos).Add(literalDefaultColNodes).Done(); - if (autoIncrement.Ref().ChildrenSize() > 0) { - input = BuildKqlSequencer(input, table, inputCols, autoIncrement, pos, ctx); + TCoAtomList inputColsAfterSequencer = BuildUpsertInputColumns(inputColumns, sequenceCols, pos, ctx); + + if (sequenceCols.Ref().ChildrenSize() > 0) { + input = BuildKqlSequencer(input, table, inputColsAfterSequencer, sequenceCols, pos, ctx); + } + + std::tie(input, inputCols) = ExtendInputRowsWithDefaultLiteralColumns(input, inputColsAfterSequencer, + literalDefaultCols, table, pos, ctx); + } else { + inputCols = BuildUpsertInputColumns(inputColumns, autoIncrement, pos, ctx); + if (autoIncrement.Ref().ChildrenSize() > 0) { + input = BuildKqlSequencer(input, table, inputCols.value(), autoIncrement, pos, ctx); + } } - std::tie(input, inputCols) = ExtendInputRowsWithAbsentNullColumns(write, input, inputCols, table, write.Pos(), ctx, kqpCtx); + YQL_ENSURE(inputCols.has_value()); + std::tie(input, inputCols) = ExtendInputRowsWithAbsentNullColumns(write, input, inputCols.value(), table, write.Pos(), ctx, kqpCtx); auto baseInput = Build<TKqpWriteConstraint>(ctx, pos) .Input(input) .Columns(GetPgNotNullColumns(table, pos, ctx)) .Done(); - return {baseInput, inputCols}; + return {baseInput, inputCols.value()}; } diff --git a/ydb/core/kqp/opt/ya.make b/ydb/core/kqp/opt/ya.make index 17206198003..0564f0e11af 100644 --- a/ydb/core/kqp/opt/ya.make +++ b/ydb/core/kqp/opt/ya.make @@ -28,6 +28,7 @@ PEERDIR( ydb/core/kqp/opt/peephole ydb/core/kqp/opt/physical ydb/core/kqp/opt/rbo + ydb/library/mkql_proto ydb/library/yql/dq/common ydb/library/yql/dq/opt ydb/library/yql/dq/type_ann @@ -37,6 +38,7 @@ PEERDIR( ydb/core/kqp/provider ydb/library/formats/arrow/protos ydb/library/json_index + yql/essentials/providers/common/mkql ) YQL_LAST_ABI_VERSION() diff --git a/ydb/core/kqp/ut/scheme/kqp_constraints_ut.cpp b/ydb/core/kqp/ut/scheme/kqp_constraints_ut.cpp index 1d79de6107c..ba864d7ec50 100644 --- a/ydb/core/kqp/ut/scheme/kqp_constraints_ut.cpp +++ b/ydb/core/kqp/ut/scheme/kqp_constraints_ut.cpp @@ -12,6 +12,64 @@ using namespace NYdb; using namespace NYdb::NQuery; using namespace NYdb::NTopic; +namespace { + +void TestCompileTimeDefaultsPlan(const std::string& operation, bool enabled) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(enabled); + + TKikimrRunner kikimr(TKikimrSettings(appConfig).SetWithSampleTables(false)); + + auto db = kikimr.GetQueryClient(); + auto session = db.GetSession().GetValueSync().GetSession(); + auto settings = TExecuteQuerySettings().ExecMode(EExecMode::Explain); + + { + const std::string query = R"( + CREATE TABLE TestTable ( + Key Int32, + Value String DEFAULT "first_default", + PRIMARY KEY (Key) + ); + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + } + + { + // Implicit default value + const std::string query = std::format(R"( + {} INTO TestTable (Key) VALUES (1); + )", operation); + auto result = session.ExecuteQuery(query, TTxControl::NoTx(), settings).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + const auto ast = result.GetStats()->GetAst(); + if (enabled) { + // DEFAULT value is inlined into the plan + UNIT_ASSERT_C(!ast->contains("KqpCnSequencer"), ast); + } else { + // Runtime substitution of DEFAULT value + UNIT_ASSERT_C(ast->contains("KqpCnSequencer"), ast); + } + } + + { + // Explicit default value -> without sequencer + const std::string query = std::format(R"( + {} INTO TestTable (Key, Value) VALUES (2, "value2"); + )", operation); + auto result = session.ExecuteQuery(query, TTxControl::NoTx(), settings).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + const auto ast = result.GetStats()->GetAst(); + // DEFAULT value is inlined into the plan + UNIT_ASSERT_C(!ast->contains("KqpCnSequencer"), ast); + } +} + +} // namespace + Y_UNIT_TEST_SUITE(KqpConstraints) { Y_UNIT_TEST(SerialTypeNegative1) { TKikimrRunner kikimr(TKikimrSettings() @@ -321,8 +379,10 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(DefaultsAndDeleteAndUpdate) { - TKikimrRunner kikimr(TKikimrSettings() + Y_UNIT_TEST_TWIN(DefaultsAndDeleteAndUpdate, EnableCompileTimeDefaults) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); + TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); auto db = kikimr.GetQueryClient(); @@ -378,8 +438,10 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(AlterTableAddColumnWithDefaultValue) { - TKikimrRunner kikimr(TKikimrSettings() + Y_UNIT_TEST_TWIN(AlterTableAddColumnWithDefaultValue, EnableCompileTimeDefaults) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); + TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); auto db = kikimr.GetQueryClient(); @@ -468,8 +530,10 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(DefaultValuesForTable) { - TKikimrRunner kikimr(TKikimrSettings() + Y_UNIT_TEST_TWIN(DefaultValuesForTable, EnableCompileTimeDefaults) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); + TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); auto db = kikimr.GetQueryClient(); @@ -1037,8 +1101,10 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { ])"); } - Y_UNIT_TEST(IndexedTableAndNotNullColumnAddNotNullColumn) { - TKikimrRunner kikimr(TKikimrSettings() + Y_UNIT_TEST_TWIN(IndexedTableAndNotNullColumnAddNotNullColumn, EnableCompileTimeDefaults) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); + TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetEnableParameterizedDecimal(true) .SetWithSampleTables(false)); @@ -1264,8 +1330,10 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(DefaultAndIndexesTestDefaultColumnNotIncludedInIndex) { - TKikimrRunner kikimr(TKikimrSettings() + Y_UNIT_TEST_TWIN(DefaultAndIndexesTestDefaultColumnNotIncludedInIndex, EnableCompileTimeDefaults) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); + TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); auto db = kikimr.GetQueryClient(); @@ -1319,8 +1387,10 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { )")); } - Y_UNIT_TEST(Utf8AndDefault) { - TKikimrRunner kikimr(TKikimrSettings() + Y_UNIT_TEST_TWIN(Utf8AndDefault, EnableCompileTimeDefaults) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); + TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); auto db = kikimr.GetQueryClient(); @@ -1397,8 +1467,10 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(AlterTableAddNotNullWithDefault) { - TKikimrRunner kikimr(TKikimrSettings() + Y_UNIT_TEST_TWIN(AlterTableAddNotNullWithDefault, EnableCompileTimeDefaults) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); + TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); auto db = kikimr.GetQueryClient(); @@ -1606,8 +1678,10 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(DefaultColumnAndBulkUpsert) { - TKikimrRunner kikimr(TKikimrSettings() + Y_UNIT_TEST_TWIN(DefaultColumnAndBulkUpsert, EnableCompileTimeDefaults) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); + TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); auto queryClient = kikimr.GetQueryClient(); @@ -1829,8 +1903,9 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::PRECONDITION_FAILED, result.GetIssues().ToString()); } - Y_UNIT_TEST(AlterTableSetDefaultLiteral) { + Y_UNIT_TEST_TWIN(AlterTableSetDefaultLiteral, EnableCompileTimeDefaults) { NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); @@ -1878,8 +1953,9 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(AlterTableSetDefaultInt) { + Y_UNIT_TEST_TWIN(AlterTableSetDefaultInt, EnableCompileTimeDefaults) { NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); @@ -1927,8 +2003,9 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(AlterTableDropDefault) { + Y_UNIT_TEST_TWIN(AlterTableDropDefault, EnableCompileTimeDefaults) { NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); @@ -2179,8 +2256,9 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(AlterTableSetAndDropDefaultOnNotNullColumn) { + Y_UNIT_TEST_TWIN(AlterTableSetAndDropDefaultOnNotNullColumn, EnableCompileTimeDefaults) { NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); @@ -2315,8 +2393,9 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(AlterTableSetDropDefaultMultipleColumns) { + Y_UNIT_TEST_TWIN(AlterTableSetDropDefaultMultipleColumns, EnableCompileTimeDefaults) { NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); @@ -2566,8 +2645,9 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(AlterTableSetDefaultOnPK) { + Y_UNIT_TEST_TWIN(AlterTableSetDefaultOnPK, EnableCompileTimeDefaults) { NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); @@ -2619,8 +2699,9 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(AlterTableDropDefaultOnPK) { + Y_UNIT_TEST_TWIN(AlterTableDropDefaultOnPK, EnableCompileTimeDefaults) { NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); @@ -2736,8 +2817,9 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(AlterTableSetDropDefaultBulkUpsert) { + Y_UNIT_TEST_TWIN(AlterTableSetDropDefaultBulkUpsert, EnableCompileTimeDefaults) { NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); @@ -2810,8 +2892,9 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(AlterTableSetDropDefaultAsyncIndexOnColumn) { + Y_UNIT_TEST_TWIN(AlterTableSetDropDefaultAsyncIndexOnColumn, EnableCompileTimeDefaults) { NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); @@ -2904,8 +2987,9 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(AlterTableSetDropDefaultAsyncIndexCoverColumn) { + Y_UNIT_TEST_TWIN(AlterTableSetDropDefaultAsyncIndexCoverColumn, EnableCompileTimeDefaults) { NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); @@ -2998,8 +3082,9 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(AlterTableDefaultConstantExpression) { + Y_UNIT_TEST_TWIN(AlterTableDefaultConstantExpression, EnableCompileTimeDefaults) { NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); @@ -3123,8 +3208,10 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(AlterTableAddColumnDefaultWithChangefeed) { - TKikimrRunner kikimr(TKikimrSettings() + Y_UNIT_TEST_TWIN(AlterTableAddColumnDefaultWithChangefeed, EnableCompileTimeDefaults) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); + TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); auto db = kikimr.GetQueryClient(); @@ -3246,8 +3333,9 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(AlterTableSetDropDefaultWithChangefeed) { + Y_UNIT_TEST_TWIN(AlterTableSetDropDefaultWithChangefeed, EnableCompileTimeDefaults) { NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); @@ -3880,8 +3968,9 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { } } - Y_UNIT_TEST(SetDefaultMultipleTimes) { + Y_UNIT_TEST_TWIN(SetDefaultMultipleTimes, EnableCompileTimeDefaults) { NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); TKikimrRunner kikimr(TKikimrSettings(appConfig) .SetWithSampleTables(false)); @@ -3980,6 +4069,1554 @@ Y_UNIT_TEST_SUITE(KqpConstraints) { ])"); } } + + Y_UNIT_TEST_TWIN(CompileTimeDefaults_Ast_Upsert, EnableCompileTimeDefaults) { + TestCompileTimeDefaultsPlan("UPSERT", EnableCompileTimeDefaults); + } + + Y_UNIT_TEST_TWIN(CompileTimeDefaults_Ast_Replace, EnableCompileTimeDefaults) { + TestCompileTimeDefaultsPlan("REPLACE", EnableCompileTimeDefaults); + } + + Y_UNIT_TEST_TWIN(CompileTimeDefaults_Ast_Insert, EnableCompileTimeDefaults) { + TestCompileTimeDefaultsPlan("INSERT", EnableCompileTimeDefaults); + } + + Y_UNIT_TEST_TWIN(CompileTimeDefaults_Write_Upsert, EnableCompileTimeDefaults) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); + + TKikimrRunner kikimr(TKikimrSettings(appConfig).SetWithSampleTables(false)); + + auto db = kikimr.GetQueryClient(); + auto session = db.GetSession().GetValueSync().GetSession(); + + { + const std::string query = R"( + CREATE TABLE TestTable ( + Key Int32, + Value String DEFAULT "default_value", + Value2 String, + PRIMARY KEY (Key) + ); + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + } + + const auto validateTable = [&](const TString& expected) { + const std::string query = R"( + SELECT Key, Value, Value2 FROM TestTable ORDER BY Key; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + CompareYson(expected, FormatResultSetYson(result.GetResultSet(0))); + }; + + // Upsert new row, without default, without nullable + { + const std::string query = R"( + UPSERT INTO TestTable (Key) VALUES (1) RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[1];["default_value"];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#] + ])"); + } + + // Upsert new row, with default, without nullable + { + const std::string query = R"( + UPSERT INTO TestTable (Key, Value) VALUES (2, "explicit") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[2];["explicit"];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#] + ])"); + } + + // Upsert new row, without default, with nullable + { + const std::string query = R"( + UPSERT INTO TestTable (Key, Value2) VALUES (3, "explicit") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[3];["default_value"];["explicit"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["default_value"];["explicit"]] + ])"); + } + + // Upsert new row, with default, with nullable + { + const std::string query = R"( + UPSERT INTO TestTable (Key, Value, Value2) VALUES (4, "explicit", "explicit") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[4];["explicit"];["explicit"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["default_value"];["explicit"]]; + [[4];["explicit"];["explicit"]] + ])"); + } + + // Upsert old row, without default, without nullable + { + const std::string query = R"( + UPSERT INTO TestTable (Key) VALUES (4) RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[4];["explicit"];["explicit"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["default_value"];["explicit"]]; + [[4];["explicit"];["explicit"]] + ])"); + } + + // Upsert old row, with default, without nullable + { + const std::string query = R"( + UPSERT INTO TestTable (Key, Value) VALUES (4, "updated") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[4];["updated"];["explicit"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["default_value"];["explicit"]]; + [[4];["updated"];["explicit"]] + ])"); + } + + // Upsert old row, without default, with nullable + { + const std::string query = R"( + UPSERT INTO TestTable (Key, Value2) VALUES (4, "updated") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[4];["updated"];["updated"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["default_value"];["explicit"]]; + [[4];["updated"];["updated"]] + ])"); + } + + // Upsert old row, with default, with nullable + { + const std::string query = R"( + UPSERT INTO TestTable (Key, Value, Value2) VALUES (3, "updated", "updated") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[3];["updated"];["updated"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["updated"];["updated"]]; + [[4];["updated"];["updated"]] + ])"); + } + + // Upsert batch, without default, without nullable + { + const std::string query = R"( + UPSERT INTO TestTable (Key) VALUES (5), (6), (7), (8) + RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[5];["default_value"];#]; + [[6];["default_value"];#]; + [[7];["default_value"];#]; + [[8];["default_value"];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["updated"];["updated"]]; + [[4];["updated"];["updated"]]; + [[5];["default_value"];#]; + [[6];["default_value"];#]; + [[7];["default_value"];#]; + [[8];["default_value"];#] + ])"); + } + } + + Y_UNIT_TEST_TWIN(CompileTimeDefaults_Write_Replace, EnableCompileTimeDefaults) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); + + TKikimrRunner kikimr(TKikimrSettings(appConfig).SetWithSampleTables(false)); + + auto db = kikimr.GetQueryClient(); + auto session = db.GetSession().GetValueSync().GetSession(); + + { + const std::string query = R"( + CREATE TABLE TestTable ( + Key Int32, + Value String DEFAULT "default_value", + Value2 String, + PRIMARY KEY (Key) + ); + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + } + + const auto validateTable = [&](const TString& expected) { + const std::string query = R"( + SELECT Key, Value, Value2 FROM TestTable ORDER BY Key; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + CompareYson(expected, FormatResultSetYson(result.GetResultSet(0))); + }; + + // Replace new row, without default, without nullable + { + const std::string query = R"( + REPLACE INTO TestTable (Key) VALUES (1) RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[1];["default_value"];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#] + ])"); + } + + // Replace new row, with default, without nullable + { + const std::string query = R"( + REPLACE INTO TestTable (Key, Value) VALUES (2, "explicit") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[2];["explicit"];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#] + ])"); + } + + // Replace new row, without default, with nullable + { + const std::string query = R"( + REPLACE INTO TestTable (Key, Value2) VALUES (3, "explicit") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[3];["default_value"];["explicit"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["default_value"];["explicit"]] + ])"); + } + + // Replace new row, with default, with nullable + { + const std::string query = R"( + REPLACE INTO TestTable (Key, Value, Value2) VALUES (4, "explicit", "explicit") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[4];["explicit"];["explicit"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["default_value"];["explicit"]]; + [[4];["explicit"];["explicit"]] + ])"); + } + + // Replace old row, without default, without nullable + { + const std::string query = R"( + REPLACE INTO TestTable (Key) VALUES (4) RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[4];["default_value"];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["default_value"];["explicit"]]; + [[4];["default_value"];#] + ])"); + } + + // Replace old row, with default, without nullable + { + const std::string query = R"( + REPLACE INTO TestTable (Key, Value) VALUES (4, "updated") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[4];["updated"];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["default_value"];["explicit"]]; + [[4];["updated"];#] + ])"); + } + + // Replace old row, without default, with nullable + { + const std::string query = R"( + REPLACE INTO TestTable (Key, Value2) VALUES (4, "updated") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[4];["default_value"];["updated"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["default_value"];["explicit"]]; + [[4];["default_value"];["updated"]] + ])"); + } + + // Replace old row, with default, with nullable + { + const std::string query = R"( + REPLACE INTO TestTable (Key, Value, Value2) VALUES (3, "updated", "updated") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[3];["updated"];["updated"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["updated"];["updated"]]; + [[4];["default_value"];["updated"]] + ])"); + } + + // Replace batch, without default, without nullable + { + const std::string query = R"( + REPLACE INTO TestTable (Key) VALUES (5), (6), (7), (8) + RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[5];["default_value"];#]; + [[6];["default_value"];#]; + [[7];["default_value"];#]; + [[8];["default_value"];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["updated"];["updated"]]; + [[4];["default_value"];["updated"]]; + [[5];["default_value"];#]; + [[6];["default_value"];#]; + [[7];["default_value"];#]; + [[8];["default_value"];#] + ])"); + } + } + + Y_UNIT_TEST_TWIN(CompileTimeDefaults_Write_Insert, EnableCompileTimeDefaults) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); + + TKikimrRunner kikimr(TKikimrSettings(appConfig).SetWithSampleTables(false)); + + auto db = kikimr.GetQueryClient(); + auto session = db.GetSession().GetValueSync().GetSession(); + + { + const std::string query = R"( + CREATE TABLE TestTable ( + Key Int32, + Value String DEFAULT "default_value", + Value2 String, + PRIMARY KEY (Key) + ); + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + } + + const auto validateTable = [&](const TString& expected) { + const std::string query = R"( + SELECT Key, Value, Value2 FROM TestTable ORDER BY Key; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + CompareYson(expected, FormatResultSetYson(result.GetResultSet(0))); + }; + + // Insert new row, without default, without nullable + { + const std::string query = R"( + INSERT INTO TestTable (Key) VALUES (1) RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[1];["default_value"];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#] + ])"); + } + + // Insert new row, with default, without nullable + { + const std::string query = R"( + INSERT INTO TestTable (Key, Value) VALUES (2, "explicit") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[2];["explicit"];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#] + ])"); + } + + // Insert new row, without default, with nullable + { + const std::string query = R"( + INSERT INTO TestTable (Key, Value2) VALUES (3, "explicit") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[3];["default_value"];["explicit"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["default_value"];["explicit"]] + ])"); + } + + // Insert new row, with default, with nullable + { + const std::string query = R"( + INSERT INTO TestTable (Key, Value, Value2) VALUES (4, "explicit", "explicit") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[4];["explicit"];["explicit"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["default_value"];["explicit"]]; + [[4];["explicit"];["explicit"]] + ])"); + } + + // Insert old row, without default, without nullable + { + const std::string query = R"( + INSERT INTO TestTable (Key) VALUES (4) RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(!result.IsSuccess(), result.GetIssues().ToString()); + } + + // Insert old row, with default, without nullable + { + const std::string query = R"( + INSERT INTO TestTable (Key, Value) VALUES (4, "updated") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(!result.IsSuccess(), result.GetIssues().ToString()); + } + + // Insert old row, without default, with nullable + { + const std::string query = R"( + INSERT INTO TestTable (Key, Value2) VALUES (4, "updated") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(!result.IsSuccess(), result.GetIssues().ToString()); + } + + // Insert old row, with default, with nullable + { + const std::string query = R"( + INSERT INTO TestTable (Key, Value, Value2) VALUES (3, "updated", "updated") RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(!result.IsSuccess(), result.GetIssues().ToString()); + } + + // Insert batch, without default, without nullable + { + const std::string query = R"( + INSERT INTO TestTable (Key) VALUES (5), (6), (7), (8) + RETURNING Key, Value, Value2; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[5];["default_value"];#]; + [[6];["default_value"];#]; + [[7];["default_value"];#]; + [[8];["default_value"];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"];#]; + [[2];["explicit"];#]; + [[3];["default_value"];["explicit"]]; + [[4];["explicit"];["explicit"]]; + [[5];["default_value"];#]; + [[6];["default_value"];#]; + [[7];["default_value"];#]; + [[8];["default_value"];#] + ])"); + } + } + + Y_UNIT_TEST(CompileTimeDefaults_AllTypes) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(true); + + TKikimrRunner kikimr(TKikimrSettings(appConfig).SetWithSampleTables(false)); + + auto db = kikimr.GetQueryClient(); + auto session = db.GetSession().GetValueSync().GetSession(); + + { + const std::string query = R"( + CREATE TABLE AllTypesDefaultTest ( + Key Int32, + BoolCol Bool DEFAULT True, + Int8Col Int8 DEFAULT -100, + Int16Col Int16 DEFAULT -200, + Int32Col Int32 DEFAULT -300, + Int64Col Int64 DEFAULT -400, + Uint8Col Uint8 DEFAULT 10, + Uint16Col Uint16 DEFAULT 20, + Uint32Col Uint32 DEFAULT 30, + Uint64Col Uint64 DEFAULT 40, + FloatCol Float DEFAULT 1.5f, + DoubleCol Double DEFAULT 2.5, + StringCol String DEFAULT "hello", + Utf8Col Utf8 DEFAULT Utf8("world"), + DateCol Date DEFAULT Date("2021-01-01"), + DatetimeCol Datetime DEFAULT Datetime("2021-01-01T00:00:00Z"), + TimestampCol Timestamp DEFAULT Timestamp("2021-01-01T00:00:00.000000Z"), + IntervalCol Interval DEFAULT Interval("P1D"), + Date32Col Date32 DEFAULT Date32("2021-01-01"), + Datetime64Col Datetime64 DEFAULT Datetime64("2021-01-01T00:00:00Z"), + Timestamp64Col Timestamp64 DEFAULT Timestamp64("2021-01-01T00:00:00.000000Z"), + Interval64Col Interval64 DEFAULT Interval64("P1D"), + JsonCol Json DEFAULT Json("[0]"), + YsonCol Yson DEFAULT Yson("[1]"), + JsonDocumentCol JsonDocument DEFAULT JsonDocument("[2]"), + UuidCol Uuid DEFAULT Uuid("65df9ecc-a97d-47b2-ae56-3c023da6ee8c"), + DecimalCol Decimal(22,9) DEFAULT Decimal("1.11", 22, 9), + DyNumberCol DyNumber DEFAULT DyNumber("3.14"), + PRIMARY KEY (Key) + ); + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + } + + { + const std::string query = R"( + UPSERT INTO AllTypesDefaultTest (Key) VALUES (1) + RETURNING + Key, BoolCol, + Int8Col, Int16Col, Int32Col, Int64Col, + Uint8Col, Uint16Col, Uint32Col, Uint64Col, + FloatCol, DoubleCol, + StringCol, Utf8Col, + DateCol, DatetimeCol, TimestampCol, IntervalCol, + Date32Col, Datetime64Col, Timestamp64Col, Interval64Col, + JsonCol, YsonCol, JsonDocumentCol, UuidCol, + DecimalCol, DyNumberCol; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[1];[%true]; + [-100];[-200];[-300];[-400]; + [10u];[20u];[30u];[40u]; + [1.5];[2.5]; + ["hello"];["world"]; + [18628u];[1609459200u];[1609459200000000u];[86400000000]; + [18628];[1609459200];[1609459200000000];[86400000000]; + ["[0]"];["[1]"];["[2]"]; + ["65df9ecc-a97d-47b2-ae56-3c023da6ee8c"]; + ["1.11"]; + [".314e1"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + } + + { + const std::string query = R"( + SELECT + Key, BoolCol, + Int8Col, Int16Col, Int32Col, Int64Col, + Uint8Col, Uint16Col, Uint32Col, Uint64Col, + FloatCol, DoubleCol, + StringCol, Utf8Col, + DateCol, DatetimeCol, TimestampCol, IntervalCol, + Date32Col, Datetime64Col, Timestamp64Col, Interval64Col, + JsonCol, YsonCol, JsonDocumentCol, UuidCol, + DecimalCol, DyNumberCol + FROM AllTypesDefaultTest; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[1];[%true]; + [-100];[-200];[-300];[-400]; + [10u];[20u];[30u];[40u]; + [1.5];[2.5]; + ["hello"];["world"]; + [18628u];[1609459200u];[1609459200000000u];[86400000000]; + [18628];[1609459200];[1609459200000000];[86400000000]; + ["[0]"];["[1]"];["[2]"]; + ["65df9ecc-a97d-47b2-ae56-3c023da6ee8c"]; + ["1.11"]; + [".314e1"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + } + } + + Y_UNIT_TEST_TWIN(CompileTimeDefaults_NotNullDefault, EnableCompileTimeDefaults) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); + + TKikimrRunner kikimr(TKikimrSettings(appConfig).SetWithSampleTables(false)); + + auto db = kikimr.GetQueryClient(); + auto session = db.GetSession().GetValueSync().GetSession(); + + { + const std::string query = R"( + CREATE TABLE TestTable ( + Key Int32, + Value String NOT NULL DEFAULT "default_value", + PRIMARY KEY (Key) + ); + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + } + + const auto validateTable = [&](const TString& expected) { + const std::string query = R"( + SELECT Key, Value FROM TestTable ORDER BY Key; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + CompareYson(expected, FormatResultSetYson(result.GetResultSet(0))); + }; + + // UPSERT new row without default + { + const std::string query = R"( + UPSERT INTO TestTable (Key) VALUES (1) RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[1];"default_value"] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];"default_value"] + ])"); + } + + // UPSERT new row with default + { + const std::string query = R"( + UPSERT INTO TestTable (Key, Value) VALUES (2, "explicit") RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[2];"explicit"] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];"default_value"]; + [[2];"explicit"] + ])"); + } + + // UPSERT old row without default + { + const std::string query = R"( + UPSERT INTO TestTable (Key) VALUES (2) RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[2];"explicit"] + ])", FormatResultSetYson(result.GetResultSet(0))); + + // Storage is always correct regardless of RETURNING + validateTable(R"([ + [[1];"default_value"]; + [[2];"explicit"] + ])"); + } + + // UPSERT old row with default + { + const std::string query = R"( + UPSERT INTO TestTable (Key, Value) VALUES (2, "updated") RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[2];"updated"] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];"default_value"]; + [[2];"updated"] + ])"); + } + + // REPLACE old row without default + { + const std::string query = R"( + REPLACE INTO TestTable (Key) VALUES (2) RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[2];"default_value"] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];"default_value"]; + [[2];"default_value"] + ])"); + } + + // REPLACE old row with default + { + const std::string query = R"( + REPLACE INTO TestTable (Key, Value) VALUES (2, "explicit") RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[2];"explicit"] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];"default_value"]; + [[2];"explicit"] + ])"); + } + + // INSERT new row without default + { + const std::string query = R"( + INSERT INTO TestTable (Key) VALUES (3) RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[3];"default_value"] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];"default_value"]; + [[2];"explicit"]; + [[3];"default_value"] + ])"); + } + + // INSERT new row with default + { + const std::string query = R"( + INSERT INTO TestTable (Key, Value) VALUES (4, "explicit") RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[4];"explicit"] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];"default_value"]; + [[2];"explicit"]; + [[3];"default_value"]; + [[4];"explicit"] + ])"); + } + + // INSERT duplicate key + { + const std::string query = R"( + INSERT INTO TestTable (Key) VALUES (4); + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(!result.IsSuccess(), result.GetIssues().ToString()); + + validateTable(R"([ + [[1];"default_value"]; + [[2];"explicit"]; + [[3];"default_value"]; + [[4];"explicit"] + ])"); + } + } + + Y_UNIT_TEST_TWIN(CompileTimeDefaults_NullableDefault, EnableCompileTimeDefaults) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(EnableCompileTimeDefaults); + + TKikimrRunner kikimr(TKikimrSettings(appConfig).SetWithSampleTables(false)); + + auto db = kikimr.GetQueryClient(); + auto session = db.GetSession().GetValueSync().GetSession(); + + { + const std::string query = R"( + CREATE TABLE TestTable ( + Key Int32, + Value String DEFAULT "default_value", + PRIMARY KEY (Key) + ); + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + } + + const auto validateTable = [&](const TString& expected) { + const std::string query = R"( + SELECT Key, Value FROM TestTable ORDER BY Key; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + CompareYson(expected, FormatResultSetYson(result.GetResultSet(0))); + }; + + // UPSERT new row without default + { + const std::string query = R"( + UPSERT INTO TestTable (Key) VALUES (1) RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[1];["default_value"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"]] + ])"); + } + + // UPSERT new row with default + { + const std::string query = R"( + UPSERT INTO TestTable (Key, Value) VALUES (2, "explicit") RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[2];["explicit"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"]]; + [[2];["explicit"]] + ])"); + } + + // UPSERT new row with NULL + { + const std::string query = R"( + UPSERT INTO TestTable (Key, Value) VALUES (3, NULL) RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[3];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"]]; + [[2];["explicit"]]; + [[3];#] + ])"); + } + + // UPSERT old row without default + { + const std::string query = R"( + UPSERT INTO TestTable (Key) VALUES (3) RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[3];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + // Storage is always correct regardless of RETURNING + validateTable(R"([ + [[1];["default_value"]]; + [[2];["explicit"]]; + [[3];#] + ])"); + } + + // UPSERT old row with default + { + const std::string query = R"( + UPSERT INTO TestTable (Key, Value) VALUES (3, "updated") RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[3];["updated"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"]]; + [[2];["explicit"]]; + [[3];["updated"]] + ])"); + } + + // UPSERT old row with NULL + { + const std::string query = R"( + UPSERT INTO TestTable (Key, Value) VALUES (3, NULL) RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[3];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"]]; + [[2];["explicit"]]; + [[3];#] + ])"); + } + + // REPLACE old row without default + { + const std::string query = R"( + REPLACE INTO TestTable (Key) VALUES (3) RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[3];["default_value"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"]]; + [[2];["explicit"]]; + [[3];["default_value"]] + ])"); + } + + // REPLACE old row with NULL + { + const std::string query = R"( + REPLACE INTO TestTable (Key, Value) VALUES (3, NULL) RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[3];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"]]; + [[2];["explicit"]]; + [[3];#] + ])"); + } + + // INSERT new row without default + { + const std::string query = R"( + INSERT INTO TestTable (Key) VALUES (4) RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[4];["default_value"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"]]; + [[2];["explicit"]]; + [[3];#]; + [[4];["default_value"]] + ])"); + } + + // INSERT new row with NULL + { + const std::string query = R"( + INSERT INTO TestTable (Key, Value) VALUES (5, NULL) RETURNING Key, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[5];#] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateTable(R"([ + [[1];["default_value"]]; + [[2];["explicit"]]; + [[3];#]; + [[4];["default_value"]]; + [[5];#] + ])"); + } + + // INSERT duplicate key + { + const std::string query = R"( + INSERT INTO TestTable (Key) VALUES (5); + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(!result.IsSuccess(), result.GetIssues().ToString()); + + validateTable(R"([ + [[1];["default_value"]]; + [[2];["explicit"]]; + [[3];#]; + [[4];["default_value"]]; + [[5];#] + ])"); + } + } + + Y_UNIT_TEST_TWIN(CompileTimeDefaults_WithGlobalSyncIndex, EnableIndexStreamWrite) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(true); + appConfig.MutableTableServiceConfig()->SetEnableIndexStreamWrite(EnableIndexStreamWrite); + + TKikimrRunner kikimr(TKikimrSettings(appConfig).SetWithSampleTables(false)); + + auto db = kikimr.GetQueryClient(); + auto session = db.GetSession().GetValueSync().GetSession(); + + { + const std::string query = R"( + CREATE TABLE TestTable ( + Key Int32, + IndexCol String, + Value String DEFAULT "default_value", + PRIMARY KEY (Key), + INDEX ByIndexCol GLOBAL ON (IndexCol) COVER (Value) + ); + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + } + + const auto validateMain = [&](const TString& expected) { + const std::string query = R"( + SELECT Key, IndexCol, Value FROM TestTable ORDER BY Key; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + CompareYson(expected, FormatResultSetYson(result.GetResultSet(0))); + }; + + const auto validateIndex = [&](const std::string& indexColVal, const TString& expected) { + const std::string query = "SELECT Key, Value FROM TestTable VIEW ByIndexCol WHERE IndexCol = \"" + indexColVal + "\" ORDER BY Key;"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + CompareYson(expected, FormatResultSetYson(result.GetResultSet(0))); + }; + + // UPSERT without default + { + const std::string query = R"( + UPSERT INTO TestTable (Key, IndexCol) VALUES (1, "idx1") RETURNING Key, IndexCol, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[1];["idx1"];["default_value"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateMain(R"([ + [[1];["idx1"];["default_value"]] + ])"); + validateIndex("idx1", R"([ + [[1];["default_value"]] + ])"); + } + + // UPSERT with default + { + const std::string query = R"( + UPSERT INTO TestTable (Key, IndexCol, Value) VALUES (2, "idx2", "explicit") RETURNING Key, IndexCol, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[2];["idx2"];["explicit"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateMain(R"([ + [[1];["idx1"];["default_value"]]; + [[2];["idx2"];["explicit"]] + ])"); + validateIndex("idx2", R"([ + [[2];["explicit"]] + ])"); + } + + // REPLACE without default + { + const std::string query = R"( + REPLACE INTO TestTable (Key, IndexCol) VALUES (2, "idx2") RETURNING Key, IndexCol, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[2];["idx2"];["default_value"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateMain(R"([ + [[1];["idx1"];["default_value"]]; + [[2];["idx2"];["default_value"]] + ])"); + validateIndex("idx2", R"([ + [[2];["default_value"]] + ])"); + } + + // INSERT without default + { + const std::string query = R"( + INSERT INTO TestTable (Key, IndexCol) VALUES (3, "idx3") RETURNING Key, IndexCol, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[3];["idx3"];["default_value"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateMain(R"([ + [[1];["idx1"];["default_value"]]; + [[2];["idx2"];["default_value"]]; + [[3];["idx3"];["default_value"]] + ])"); + validateIndex("idx3", R"([ + [[3];["default_value"]] + ])"); + } + } + + Y_UNIT_TEST_TWIN(CompileTimeDefaults_WithGlobalAsyncIndex, EnableIndexStreamWrite) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(true); + appConfig.MutableTableServiceConfig()->SetEnableIndexStreamWrite(EnableIndexStreamWrite); + + TKikimrRunner kikimr(TKikimrSettings(appConfig).SetWithSampleTables(false)); + + auto db = kikimr.GetQueryClient(); + auto session = db.GetSession().GetValueSync().GetSession(); + + { + const std::string query = R"( + CREATE TABLE TestTable ( + Key Int32, + IndexCol String, + Value String DEFAULT "default_value", + PRIMARY KEY (Key), + INDEX ByIndexCol GLOBAL ASYNC ON (IndexCol) COVER (Value) + ); + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + } + + const auto validateMain = [&](const TString& expected) { + const std::string query = R"( + SELECT Key, IndexCol, Value FROM TestTable ORDER BY Key; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + CompareYson(expected, FormatResultSetYson(result.GetResultSet(0))); + }; + + const auto validateIndex = [&](const std::string& indexColVal, const TString& expected) { + const std::string query = "SELECT Key, Value FROM TestTable VIEW ByIndexCol WHERE IndexCol = \"" + indexColVal + "\" ORDER BY Key;"; + auto result = session.ExecuteQuery(query, TTxControl::BeginTx(TTxSettings::StaleRO()).CommitTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + CompareYson(expected, FormatResultSetYson(result.GetResultSet(0))); + }; + + // UPSERT without default + { + const std::string query = R"( + UPSERT INTO TestTable (Key, IndexCol) VALUES (1, "idx1") RETURNING Key, IndexCol, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[1];["idx1"];["default_value"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateMain(R"([ + [[1];["idx1"];["default_value"]] + ])"); + validateIndex("idx1", R"([ + [[1];["default_value"]] + ])"); + } + + // UPSERT with default + { + const std::string query = R"( + UPSERT INTO TestTable (Key, IndexCol, Value) VALUES (2, "idx2", "explicit") RETURNING Key, IndexCol, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[2];["idx2"];["explicit"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateMain(R"([ + [[1];["idx1"];["default_value"]]; + [[2];["idx2"];["explicit"]] + ])"); + validateIndex("idx2", R"([ + [[2];["explicit"]] + ])"); + } + + // REPLACE without default + { + const std::string query = R"( + REPLACE INTO TestTable (Key, IndexCol) VALUES (2, "idx2") RETURNING Key, IndexCol, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[2];["idx2"];["default_value"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateMain(R"([ + [[1];["idx1"];["default_value"]]; + [[2];["idx2"];["default_value"]] + ])"); + validateIndex("idx2", R"([ + [[2];["default_value"]] + ])"); + } + + // INSERT without default + { + const std::string query = R"( + INSERT INTO TestTable (Key, IndexCol) VALUES (3, "idx3") RETURNING Key, IndexCol, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[3];["idx3"];["default_value"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateMain(R"([ + [[1];["idx1"];["default_value"]]; + [[2];["idx2"];["default_value"]]; + [[3];["idx3"];["default_value"]] + ])"); + validateIndex("idx3", R"([ + [[3];["default_value"]] + ])"); + } + } + + Y_UNIT_TEST_TWIN(CompileTimeDefaults_WithGlobalSyncUniqueIndex, EnableIndexStreamWrite) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnableCompileTimeDefaults(true); + appConfig.MutableTableServiceConfig()->SetEnableIndexStreamWrite(EnableIndexStreamWrite); + + TKikimrRunner kikimr(TKikimrSettings(appConfig).SetWithSampleTables(false)); + + auto db = kikimr.GetQueryClient(); + auto session = db.GetSession().GetValueSync().GetSession(); + + { + const std::string query = R"( + CREATE TABLE TestTable ( + Key Int32, + IndexCol String, + Value String DEFAULT "default_value", + PRIMARY KEY (Key), + INDEX ByIndexCol GLOBAL UNIQUE SYNC ON (IndexCol) COVER (Value) + ); + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + } + + const auto validateMain = [&](const TString& expected) { + const std::string query = R"( + SELECT Key, IndexCol, Value FROM TestTable ORDER BY Key; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + CompareYson(expected, FormatResultSetYson(result.GetResultSet(0))); + }; + + const auto validateIndex = [&](const std::string& indexColVal, const TString& expected) { + const std::string query = "SELECT Key, Value FROM TestTable VIEW ByIndexCol WHERE IndexCol = \"" + indexColVal + "\" ORDER BY Key;"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + CompareYson(expected, FormatResultSetYson(result.GetResultSet(0))); + }; + + // UPSERT without default + { + const std::string query = R"( + UPSERT INTO TestTable (Key, IndexCol) VALUES (1, "idx1") RETURNING Key, IndexCol, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[1];["idx1"];["default_value"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateMain(R"([ + [[1];["idx1"];["default_value"]] + ])"); + validateIndex("idx1", R"([ + [[1];["default_value"]] + ])"); + } + + // UPSERT with default + { + const std::string query = R"( + UPSERT INTO TestTable (Key, IndexCol, Value) VALUES (2, "idx2", "explicit") RETURNING Key, IndexCol, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[2];["idx2"];["explicit"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateMain(R"([ + [[1];["idx1"];["default_value"]]; + [[2];["idx2"];["explicit"]] + ])"); + validateIndex("idx2", R"([ + [[2];["explicit"]] + ])"); + } + + // REPLACE without default + { + const std::string query = R"( + REPLACE INTO TestTable (Key, IndexCol) VALUES (2, "idx2") RETURNING Key, IndexCol, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[2];["idx2"];["default_value"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateMain(R"([ + [[1];["idx1"];["default_value"]]; + [[2];["idx2"];["default_value"]] + ])"); + validateIndex("idx2", R"([ + [[2];["default_value"]] + ])"); + } + + // INSERT without default + { + const std::string query = R"( + INSERT INTO TestTable (Key, IndexCol) VALUES (3, "idx3") RETURNING Key, IndexCol, Value; + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + CompareYson(R"([ + [[3];["idx3"];["default_value"]] + ])", FormatResultSetYson(result.GetResultSet(0))); + + validateMain(R"([ + [[1];["idx1"];["default_value"]]; + [[2];["idx2"];["default_value"]]; + [[3];["idx3"];["default_value"]] + ])"); + validateIndex("idx3", R"([ + [[3];["default_value"]] + ])"); + } + + // INSERT duplicate key + { + const std::string query = R"( + INSERT INTO TestTable (Key, IndexCol) VALUES (4, "idx1"); + )"; + auto result = session.ExecuteQuery(query, TTxControl::NoTx()).GetValueSync(); + UNIT_ASSERT_C(!result.IsSuccess(), result.GetIssues().ToString()); + + validateMain(R"([ + [[1];["idx1"];["default_value"]]; + [[2];["idx2"];["default_value"]]; + [[3];["idx3"];["default_value"]] + ])"); + validateIndex("idx1", R"([ + [[1];["default_value"]] + ])"); + } + } } } // namespace NKikimr::NKqp diff --git a/ydb/core/protos/table_service_config.proto b/ydb/core/protos/table_service_config.proto index b0844537c13..8b5402fe254 100644 --- a/ydb/core/protos/table_service_config.proto +++ b/ydb/core/protos/table_service_config.proto @@ -539,4 +539,7 @@ message TTableServiceConfig { optional bool EnableHybridSearch = 135 [default = true, (InvalidateCompileCache) = true]; optional bool EnableStrictSerializableIsolation = 136 [default = false, (InvalidateCompileCache) = true]; + + // Remove KqpSequencer from query plan for tables with DEFAULT columns during write operations. + optional bool EnableCompileTimeDefaults = 137 [default = false, (InvalidateCompileCache) = true]; }; |
