From 37bd9dbdd11082cbc3d693e6cebcea6f7728993a Mon Sep 17 00:00:00 2001 From: Vitalii Gridnev Date: Fri, 9 Feb 2024 00:50:21 +0300 Subject: [PATCH] support cast in default values --- ydb/core/kqp/provider/yql_kikimr_datasink.cpp | 45 +++++++++++++++++-- ydb/core/kqp/provider/yql_kikimr_type_ann.cpp | 38 ++++++++++------ ydb/core/kqp/ut/pg/kqp_pg_ut.cpp | 43 ++++++++++++++++++ 3 files changed, 110 insertions(+), 16 deletions(-) diff --git a/ydb/core/kqp/provider/yql_kikimr_datasink.cpp b/ydb/core/kqp/provider/yql_kikimr_datasink.cpp index 9c6243f35c64..ff58db6e9f7e 100644 --- a/ydb/core/kqp/provider/yql_kikimr_datasink.cpp +++ b/ydb/core/kqp/provider/yql_kikimr_datasink.cpp @@ -787,7 +787,7 @@ class TKikimrDataSink : public TDataProviderBase auto replaceIfExists = (settings.Mode.Cast().Value() == "create_or_replace"); auto existringOk = (settings.Mode.Cast().Value() == "create_if_not_exists"); - return Build(ctx, node->Pos()) + auto createTable = Build(ctx, node->Pos()) .World(node->Child(0)) .DataSink(node->Child(1)) .Table().Build(key.GetTablePath()) @@ -807,8 +807,47 @@ class TKikimrDataSink : public TDataProviderBase .ExistingOk() .Value(existringOk) .Build() - .Done() - .Ptr(); + .Done(); + + bool exprEvalNeeded = false; + + for(auto item: createTable.Cast().Columns()) { + auto columnTuple = item.Cast(); + if (columnTuple.Size() > 2) { + const auto& columnConstraints = columnTuple.Item(2).Cast(); + for(const auto& constraint: columnConstraints.Value().Cast()) { + if (constraint.Name().Value() != "default") + continue; + + YQL_ENSURE(constraint.Value().IsValid()); + bool shouldEvaluate = ( + constraint.Value().Cast().Ptr()->IsCallable() && + (constraint.Value().Cast().Ptr()->Content() == "PgCast") && + (constraint.Value().Cast().Ptr()->ChildrenSize() >= 1) && + (constraint.Value().Cast().Ptr()->Child(0)->IsCallable()) && + (constraint.Value().Cast().Ptr()->Child(0)->Content() == "PgConst") + ); + + if (shouldEvaluate) { + auto evaluatedExpr = ctx.Builder(constraint.Value().Cast().Ptr()->Pos()) + .Callable("EvaluateExpr") + .Add(0, constraint.Value().Cast().Ptr()) + .Seal() + .Build(); + + constraint.Ptr()->ChildRef(TCoNameValueTuple::idx_Value) = evaluatedExpr; + exprEvalNeeded = true; + } + } + } + } + + if (exprEvalNeeded) { + ctx.Step.Repeat(TExprStep::ExprEval); + } + + return createTable.Ptr(); + } else if (mode == "alter") { for (auto setting : settings.Other) { if (setting.Name().Value() == "intent") { diff --git a/ydb/core/kqp/provider/yql_kikimr_type_ann.cpp b/ydb/core/kqp/provider/yql_kikimr_type_ann.cpp index e2bbc5db7cb1..0d9b8f58d281 100644 --- a/ydb/core/kqp/provider/yql_kikimr_type_ann.cpp +++ b/ydb/core/kqp/provider/yql_kikimr_type_ann.cpp @@ -808,7 +808,13 @@ virtual TStatus HandleCreateTable(TKiCreateTable create, TExprContext& ctx) over columnMeta.SetDefaultFromLiteral(); - if (auto pgConst = constraint.Value().Maybe()) { + YQL_ENSURE(constraint.Value().IsValid()); + const auto& constrValue = constraint.Value().Cast(); + bool isPgNull = constrValue.Ptr()->IsCallable() && + constrValue.Ptr()->Content() == "PgCast" && constrValue.Ptr()->ChildrenSize() >= 1 && + constrValue.Ptr()->Child(0)->IsCallable() && constrValue.Ptr()->Child(0)->Content() == "Null"; + + if (constrValue.Maybe() || isPgNull) { auto actualPgType = actualType->Cast(); YQL_ENSURE(actualPgType); @@ -819,25 +825,31 @@ virtual TStatus HandleCreateTable(TKiCreateTable create, TExprContext& ctx) over return TStatus::Error; } - TString content = TString(pgConst.Cast().Value().Value()); - auto parseResult = NKikimr::NPg::PgNativeBinaryFromNativeText(content, typeDesc); - if (parseResult.Error) { - ctx.AddError(TIssue(ctx.GetPosition(constraint.Pos()), - TStringBuilder() << "Failed to parse default expr for typename " << actualPgType->GetName() - << ", error reason: " << *parseResult.Error)); - return TStatus::Error; + if (isPgNull) { + columnMeta.DefaultFromLiteral.mutable_value()->set_null_flag_value(NProtoBuf::NULL_VALUE); + } else { + YQL_ENSURE(constrValue.Maybe()); + auto pgConst = constrValue.Cast(); + TString content = TString(pgConst.Value().Value()); + auto parseResult = NKikimr::NPg::PgNativeBinaryFromNativeText(content, typeDesc); + if (parseResult.Error) { + ctx.AddError(TIssue(ctx.GetPosition(constraint.Pos()), + TStringBuilder() << "Failed to parse default expr for typename " << actualPgType->GetName() + << ", error reason: " << *parseResult.Error)); + return TStatus::Error; + } + + columnMeta.DefaultFromLiteral.mutable_value()->set_bytes_value(parseResult.Str); } - columnMeta.DefaultFromLiteral.mutable_value()->set_bytes_value(parseResult.Str); auto* pg = columnMeta.DefaultFromLiteral.mutable_type()->mutable_pg_type(); - pg->set_type_name(NKikimr::NPg::PgTypeNameFromTypeDesc(typeDesc)); pg->set_oid(NKikimr::NPg::PgTypeIdFromTypeDesc(typeDesc)); - } else if (auto literal = constraint.Value().Maybe()) { - FillLiteralProto(constraint.Value().Cast(), columnMeta.DefaultFromLiteral); + } else if (auto literal = constrValue.Maybe()) { + FillLiteralProto(literal.Cast(), columnMeta.DefaultFromLiteral); } else { ctx.AddError(TIssue(ctx.GetPosition(constraint.Pos()), - TStringBuilder() << "Unsupported type of default value " << constraint.Value().Cast().Ptr()->Content())); + TStringBuilder() << "Unsupported type of default value")); return TStatus::Error; } diff --git a/ydb/core/kqp/ut/pg/kqp_pg_ut.cpp b/ydb/core/kqp/ut/pg/kqp_pg_ut.cpp index f981d60e2961..ab752b74062b 100644 --- a/ydb/core/kqp/ut/pg/kqp_pg_ut.cpp +++ b/ydb/core/kqp/ut/pg/kqp_pg_ut.cpp @@ -3401,6 +3401,49 @@ Y_UNIT_TEST_SUITE(KqpPg) { } } + Y_UNIT_TEST(InsertValuesFromTableWithDefaultAndCast) { + NKikimrConfig::TAppConfig appConfig; + appConfig.MutableTableServiceConfig()->SetEnablePreparedDdl(true); + auto setting = NKikimrKqp::TKqpSetting(); + auto serverSettings = TKikimrSettings() + .SetAppConfig(appConfig) + .SetKqpSettings({setting}); + TKikimrRunner kikimr(serverSettings.SetWithSampleTables(false)); + auto db = kikimr.GetQueryClient(); + auto settings = NYdb::NQuery::TExecuteQuerySettings().Syntax(NYdb::NQuery::ESyntax::Pg); + { + auto result = db.ExecuteQuery(R"( + CREATE TABLE t ( + a INT, + b int DEFAULT 5::int4, + c int DEFAULT '7'::int4, + d varchar(20) DEFAULT 'foo'::varchar(2), + e int DEFAULT NULL, + f bit varying(5) DEFAULT '1001', + PRIMARY KEY(a) + ); + )", NYdb::NQuery::TTxControl::NoTx(), settings).ExtractValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + } + { + auto result = db.ExecuteQuery(R"( + INSERT INTO t VALUES(1); + )", NYdb::NQuery::TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + { + auto result = db.ExecuteQuery(R"( + SELECT * FROM t; + )", NYdb::NQuery::TTxControl::BeginTx().CommitTx(), settings).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + + UNIT_ASSERT_C(!result.GetResultSets().empty(), "results are empty"); + CompareYson(R"( + [["1";"5";"7";"fo";#;"1001"]] + )", FormatResultSetYson(result.GetResultSet(0))); + } + } + Y_UNIT_TEST(InsertValuesFromTableWithDefaultBool) { NKikimrConfig::TAppConfig appConfig; appConfig.MutableTableServiceConfig()->SetEnablePreparedDdl(true);