diff options
author | Vitalii Gridnev <gridnevvvit@gmail.com> | 2024-02-09 18:59:24 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-09 18:59:24 +0300 |
commit | 449285f5cf1b4b145fba1237bb7bc5170cc31db9 (patch) | |
tree | 6e84a039c2e9eff937f3237ec4b7c1729f194381 | |
parent | c0e24ba523a992c8c397f1af60dcdad2675f2ab2 (diff) | |
download | ydb-449285f5cf1b4b145fba1237bb7bc5170cc31db9.tar.gz |
support cast in default values (#1754)
-rw-r--r-- | ydb/core/kqp/provider/yql_kikimr_datasink.cpp | 45 | ||||
-rw-r--r-- | ydb/core/kqp/provider/yql_kikimr_type_ann.cpp | 45 | ||||
-rw-r--r-- | ydb/core/kqp/ut/pg/kqp_pg_ut.cpp | 43 |
3 files changed, 117 insertions, 16 deletions
diff --git a/ydb/core/kqp/provider/yql_kikimr_datasink.cpp b/ydb/core/kqp/provider/yql_kikimr_datasink.cpp index 9c6243f35c..ff58db6e9f 100644 --- a/ydb/core/kqp/provider/yql_kikimr_datasink.cpp +++ b/ydb/core/kqp/provider/yql_kikimr_datasink.cpp @@ -787,7 +787,7 @@ public: auto replaceIfExists = (settings.Mode.Cast().Value() == "create_or_replace"); auto existringOk = (settings.Mode.Cast().Value() == "create_if_not_exists"); - return Build<TKiCreateTable>(ctx, node->Pos()) + auto createTable = Build<TKiCreateTable>(ctx, node->Pos()) .World(node->Child(0)) .DataSink(node->Child(1)) .Table().Build(key.GetTablePath()) @@ -807,8 +807,47 @@ public: .ExistingOk<TCoAtom>() .Value(existringOk) .Build() - .Done() - .Ptr(); + .Done(); + + bool exprEvalNeeded = false; + + for(auto item: createTable.Cast<TKiCreateTable>().Columns()) { + auto columnTuple = item.Cast<TExprList>(); + if (columnTuple.Size() > 2) { + const auto& columnConstraints = columnTuple.Item(2).Cast<TCoNameValueTuple>(); + for(const auto& constraint: columnConstraints.Value().Cast<TCoNameValueTupleList>()) { + 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 e2bbc5db7c..9db48a960e 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<TCoPgConst>()) { + 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<TCoPgConst>() || isPgNull) { auto actualPgType = actualType->Cast<TPgExprType>(); YQL_ENSURE(actualPgType); @@ -819,25 +825,38 @@ 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) { + if (columnMeta.NotNull) { + ctx.AddError(TIssue(ctx.GetPosition(constraint.Pos()), TStringBuilder() << "Default expr " << columnName + << " is nullable or optional, but column has not null constraint. ")); + return TStatus::Error; + } + + columnMeta.DefaultFromLiteral.mutable_value()->set_null_flag_value(NProtoBuf::NULL_VALUE); + + } else { + YQL_ENSURE(constrValue.Maybe<TCoPgConst>()); + auto pgConst = constrValue.Cast<TCoPgConst>(); + 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<TCoDataCtor>()) { - FillLiteralProto(constraint.Value().Cast<TCoDataCtor>(), columnMeta.DefaultFromLiteral); + } else if (auto literal = constrValue.Maybe<TCoDataCtor>()) { + 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 6eaabcfc43..1066a40bc5 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); |