diff options
author | Vasily Gerasimov <UgnineSirdis@ydb.tech> | 2023-12-27 23:36:23 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-27 23:36:23 +0300 |
commit | 86a332affe89d8d8a629fa838adece9abdba183c (patch) | |
tree | f60d464425d08117b11cab707927ee27297b3757 | |
parent | 909ffbad0e7d2269062b8a159827a7cc8c9aa74d (diff) | |
download | ydb-86a332affe89d8d8a629fa838adece9abdba183c.tar.gz |
Support IF NOT EXISTS/IF EXISTS for tables, external tables and external data sources (#694)
24 files changed, 576 insertions, 245 deletions
diff --git a/ydb/core/kqp/gateway/behaviour/external_data_source/manager.cpp b/ydb/core/kqp/gateway/behaviour/external_data_source/manager.cpp index 983562ee5d..10797a6b64 100644 --- a/ydb/core/kqp/gateway/behaviour/external_data_source/manager.cpp +++ b/ydb/core/kqp/gateway/behaviour/external_data_source/manager.cpp @@ -83,7 +83,7 @@ void FillCreateExternalDataSourceDesc(NKikimrSchemeOp::TExternalDataSourceDescri } } -void FillCreateExternalDataSourceCommand(NKikimrSchemeOp::TModifyScheme& modifyScheme, const NYql::TObjectSettingsImpl& settings, +void FillCreateExternalDataSourceCommand(NKikimrSchemeOp::TModifyScheme& modifyScheme, const NYql::TCreateObjectSettings& settings, TExternalDataSourceManager::TInternalModificationContext& context) { CheckFeatureFlag(context); @@ -97,12 +97,13 @@ void FillCreateExternalDataSourceCommand(NKikimrSchemeOp::TModifyScheme& modifyS modifyScheme.SetWorkingDir(pathPair.first); modifyScheme.SetOperationType(NKikimrSchemeOp::ESchemeOpCreateExternalDataSource); + modifyScheme.SetFailedOnAlreadyExists(!settings.GetExistingOk()); NKikimrSchemeOp::TExternalDataSourceDescription& dataSourceDesc = *modifyScheme.MutableCreateExternalDataSource(); FillCreateExternalDataSourceDesc(dataSourceDesc, pathPair.second, settings); } -void FillDropExternalDataSourceCommand(NKikimrSchemeOp::TModifyScheme& modifyScheme, const NYql::TObjectSettingsImpl& settings, +void FillDropExternalDataSourceCommand(NKikimrSchemeOp::TModifyScheme& modifyScheme, const NYql::TDropObjectSettings& settings, TExternalDataSourceManager::TInternalModificationContext& context) { CheckFeatureFlag(context); @@ -116,15 +117,16 @@ void FillDropExternalDataSourceCommand(NKikimrSchemeOp::TModifyScheme& modifySch modifyScheme.SetWorkingDir(pathPair.first); modifyScheme.SetOperationType(NKikimrSchemeOp::ESchemeOpDropExternalDataSource); + modifyScheme.SetSuccessOnNotExist(settings.GetMissingOk()); NKikimrSchemeOp::TDrop& drop = *modifyScheme.MutableDrop(); drop.SetName(pathPair.second); } -NThreading::TFuture<TExternalDataSourceManager::TYqlConclusionStatus> SendSchemeRequest(TEvTxUserProxy::TEvProposeTransaction* request, TActorSystem* actorSystem, bool failedOnAlreadyExists = false) +NThreading::TFuture<TExternalDataSourceManager::TYqlConclusionStatus> SendSchemeRequest(TEvTxUserProxy::TEvProposeTransaction* request, TActorSystem* actorSystem, bool failedOnAlreadyExists, bool successOnNotExist) { auto promiseScheme = NThreading::NewPromise<NKqp::TSchemeOpRequestHandler::TResult>(); - IActor* requestHandler = new TSchemeOpRequestHandler(request, promiseScheme, failedOnAlreadyExists); + IActor* requestHandler = new TSchemeOpRequestHandler(request, promiseScheme, failedOnAlreadyExists, successOnNotExist); actorSystem->Register(requestHandler); return promiseScheme.GetFuture().Apply([](const NThreading::TFuture<NKqp::TSchemeOpRequestHandler::TResult>& f) { if (f.HasValue() && !f.HasException() && f.GetValue().Success()) { @@ -159,7 +161,7 @@ NThreading::TFuture<TExternalDataSourceManager::TYqlConclusionStatus> TExternalD } } -NThreading::TFuture<TExternalDataSourceManager::TYqlConclusionStatus> TExternalDataSourceManager::CreateExternalDataSource(const NYql::TObjectSettingsImpl& settings, +NThreading::TFuture<TExternalDataSourceManager::TYqlConclusionStatus> TExternalDataSourceManager::CreateExternalDataSource(const NYql::TCreateObjectSettings& settings, TInternalModificationContext& context) const { using TRequest = TEvTxUserProxy::TEvProposeTransaction; @@ -172,10 +174,10 @@ NThreading::TFuture<TExternalDataSourceManager::TYqlConclusionStatus> TExternalD auto& schemeTx = *ev->Record.MutableTransaction()->MutableModifyScheme(); FillCreateExternalDataSourceCommand(schemeTx, settings, context); - return SendSchemeRequest(ev.Release(), context.GetExternalData().GetActorSystem(), true); + return SendSchemeRequest(ev.Release(), context.GetExternalData().GetActorSystem(), schemeTx.GetFailedOnAlreadyExists(), schemeTx.GetSuccessOnNotExist()); } -NThreading::TFuture<TExternalDataSourceManager::TYqlConclusionStatus> TExternalDataSourceManager::DropExternalDataSource(const NYql::TObjectSettingsImpl& settings, +NThreading::TFuture<TExternalDataSourceManager::TYqlConclusionStatus> TExternalDataSourceManager::DropExternalDataSource(const NYql::TDropObjectSettings& settings, TInternalModificationContext& context) const { using TRequest = TEvTxUserProxy::TEvProposeTransaction; @@ -188,7 +190,7 @@ NThreading::TFuture<TExternalDataSourceManager::TYqlConclusionStatus> TExternalD auto& schemeTx = *ev->Record.MutableTransaction()->MutableModifyScheme(); FillDropExternalDataSourceCommand(schemeTx, settings, context); - return SendSchemeRequest(ev.Release(), context.GetExternalData().GetActorSystem()); + return SendSchemeRequest(ev.Release(), context.GetExternalData().GetActorSystem(), schemeTx.GetFailedOnAlreadyExists(), schemeTx.GetSuccessOnNotExist()); } TExternalDataSourceManager::TYqlConclusionStatus TExternalDataSourceManager::DoPrepare(NKqpProto::TKqpSchemeOperation& schemeOperation, const NYql::TObjectSettingsImpl& settings, @@ -216,12 +218,12 @@ TExternalDataSourceManager::TYqlConclusionStatus TExternalDataSourceManager::DoP } } -void TExternalDataSourceManager::PrepareCreateExternalDataSource(NKqpProto::TKqpSchemeOperation& schemeOperation, const NYql::TObjectSettingsImpl& settings, +void TExternalDataSourceManager::PrepareCreateExternalDataSource(NKqpProto::TKqpSchemeOperation& schemeOperation, const NYql::TCreateObjectSettings& settings, TInternalModificationContext& context) const { FillCreateExternalDataSourceCommand(*schemeOperation.MutableCreateExternalDataSource(), settings, context); } -void TExternalDataSourceManager::PrepareDropExternalDataSource(NKqpProto::TKqpSchemeOperation& schemeOperation, const NYql::TObjectSettingsImpl& settings, +void TExternalDataSourceManager::PrepareDropExternalDataSource(NKqpProto::TKqpSchemeOperation& schemeOperation, const NYql::TDropObjectSettings& settings, TInternalModificationContext& context) const { FillDropExternalDataSourceCommand(*schemeOperation.MutableDropExternalDataSource(), settings, context); } @@ -252,7 +254,7 @@ NThreading::TFuture<NMetadata::NModifications::IOperationsManager::TYqlConclusio TStringBuilder() << "Execution of prepare operation for EXTERNAL_DATA_SOURCE object: unsupported operation: " << int(schemeOperation.GetOperationCase()))); } - return SendSchemeRequest(ev.Release(), context.GetActorSystem(), true); + return SendSchemeRequest(ev.Release(), context.GetActorSystem(), schemeTx.GetFailedOnAlreadyExists(), schemeTx.GetSuccessOnNotExist()); } } diff --git a/ydb/core/kqp/gateway/behaviour/external_data_source/manager.h b/ydb/core/kqp/gateway/behaviour/external_data_source/manager.h index 3007bab19c..fcf7e75828 100644 --- a/ydb/core/kqp/gateway/behaviour/external_data_source/manager.h +++ b/ydb/core/kqp/gateway/behaviour/external_data_source/manager.h @@ -5,16 +5,16 @@ namespace NKikimr::NKqp { class TExternalDataSourceManager: public NMetadata::NModifications::IOperationsManager { - NThreading::TFuture<TYqlConclusionStatus> CreateExternalDataSource(const NYql::TObjectSettingsImpl& settings, + NThreading::TFuture<TYqlConclusionStatus> CreateExternalDataSource(const NYql::TCreateObjectSettings& settings, TInternalModificationContext& context) const; - NThreading::TFuture<TYqlConclusionStatus> DropExternalDataSource(const NYql::TObjectSettingsImpl& settings, + NThreading::TFuture<TYqlConclusionStatus> DropExternalDataSource(const NYql::TDropObjectSettings& settings, TInternalModificationContext& context) const; - void PrepareCreateExternalDataSource(NKqpProto::TKqpSchemeOperation& schemeOperation, const NYql::TObjectSettingsImpl& settings, + void PrepareCreateExternalDataSource(NKqpProto::TKqpSchemeOperation& schemeOperation, const NYql::TCreateObjectSettings& settings, TInternalModificationContext& context) const; - void PrepareDropExternalDataSource(NKqpProto::TKqpSchemeOperation& schemeOperation, const NYql::TObjectSettingsImpl& settings, + void PrepareDropExternalDataSource(NKqpProto::TKqpSchemeOperation& schemeOperation, const NYql::TDropObjectSettings& settings, TInternalModificationContext& context) const; protected: diff --git a/ydb/core/kqp/gateway/kqp_ic_gateway.cpp b/ydb/core/kqp/gateway/kqp_ic_gateway.cpp index 4c5e53a533..4caa5e09f5 100644 --- a/ydb/core/kqp/gateway/kqp_ic_gateway.cpp +++ b/ydb/core/kqp/gateway/kqp_ic_gateway.cpp @@ -1187,7 +1187,7 @@ public: TFuture<TGenericResult> CreateExternalTable(const TString& cluster, const NYql::TCreateExternalTableSettings& settings, - bool createDir) override { + bool createDir, bool existingOk) override { using TRequest = TEvTxUserProxy::TEvProposeTransaction; try { @@ -1211,6 +1211,7 @@ public: auto& schemeTx = *ev->Record.MutableTransaction()->MutableModifyScheme(); schemeTx.SetWorkingDir(pathPair.first); schemeTx.SetOperationType(NKikimrSchemeOp::ESchemeOpCreateExternalTable); + schemeTx.SetFailedOnAlreadyExists(!existingOk); NKikimrSchemeOp::TExternalTableDescription& externalTableDesc = *schemeTx.MutableCreateExternalTable(); NSchemeHelpers::FillCreateExternalTableColumnDesc(externalTableDesc, pathPair.second, settings); @@ -1228,7 +1229,8 @@ public: } TFuture<TGenericResult> DropExternalTable(const TString& cluster, - const NYql::TDropExternalTableSettings& settings) override { + const NYql::TDropExternalTableSettings& settings, + bool missingOk) override { using TRequest = TEvTxUserProxy::TEvProposeTransaction; try { @@ -1253,6 +1255,7 @@ public: auto& schemeTx = *ev->Record.MutableTransaction()->MutableModifyScheme(); schemeTx.SetWorkingDir(pathPair.first); schemeTx.SetOperationType(NKikimrSchemeOp::ESchemeOpDropExternalTable); + schemeTx.SetSuccessOnNotExist(missingOk); NKikimrSchemeOp::TDrop& drop = *schemeTx.MutableDrop(); drop.SetName(pathPair.second); @@ -1489,7 +1492,7 @@ public: auto& dropUser = *schemeTx.MutableAlterLogin()->MutableRemoveUser(); dropUser.SetUser(settings.UserName); - dropUser.SetMissingOk(settings.Force); + dropUser.SetMissingOk(settings.MissingOk); SendSchemeRequest(ev.Release()).Apply( [dropUserPromise](const TFuture<TGenericResult>& future) mutable { @@ -1840,7 +1843,7 @@ public: auto& dropGroup = *schemeTx.MutableAlterLogin()->MutableRemoveGroup(); dropGroup.SetGroup(settings.GroupName); - dropGroup.SetMissingOk(settings.Force); + dropGroup.SetMissingOk(settings.MissingOk); SendSchemeRequest(ev.Release()).Apply( [dropGroupPromise](const TFuture<TGenericResult>& future) mutable { diff --git a/ydb/core/kqp/host/kqp_gateway_proxy.cpp b/ydb/core/kqp/host/kqp_gateway_proxy.cpp index 5c4bea0c0f..743c518c2c 100644 --- a/ydb/core/kqp/host/kqp_gateway_proxy.cpp +++ b/ydb/core/kqp/host/kqp_gateway_proxy.cpp @@ -958,7 +958,7 @@ public: schemeTx.SetOperationType(NKikimrSchemeOp::ESchemeOpAlterLogin); auto& dropUser = *schemeTx.MutableAlterLogin()->MutableRemoveUser(); dropUser.SetUser(settings.UserName); - dropUser.SetMissingOk(settings.Force); + dropUser.SetMissingOk(settings.MissingOk); auto& phyQuery = *SessionCtx->Query().PreparingQuery->MutablePhysicalQuery(); auto& phyTx = *phyQuery.AddTransactions(); @@ -1195,7 +1195,7 @@ public: schemeTx.SetOperationType(NKikimrSchemeOp::ESchemeOpAlterLogin); auto& dropGroup = *schemeTx.MutableAlterLogin()->MutableRemoveGroup(); dropGroup.SetGroup(settings.GroupName); - dropGroup.SetMissingOk(settings.Force); + dropGroup.SetMissingOk(settings.MissingOk); auto& phyQuery = *SessionCtx->Query().PreparingQuery->MutablePhysicalQuery(); auto& phyTx = *phyQuery.AddTransactions(); @@ -1240,7 +1240,7 @@ public: } TFuture<TGenericResult> CreateExternalTable(const TString& cluster, const TCreateExternalTableSettings& settings, - bool createDir) override + bool createDir, bool existingOk) override { CHECK_PREPARED_DDL(CreateExternalTable); @@ -1264,6 +1264,7 @@ public: auto& schemeTx = *phyTx.MutableSchemeOperation()->MutableCreateExternalTable(); schemeTx.SetWorkingDir(pathPair.first); schemeTx.SetOperationType(NKikimrSchemeOp::ESchemeOpCreateExternalTable); + schemeTx.SetFailedOnAlreadyExists(!existingOk); NKikimrSchemeOp::TExternalTableDescription& externalTableDesc = *schemeTx.MutableCreateExternalTable(); NSchemeHelpers::FillCreateExternalTableColumnDesc(externalTableDesc, pathPair.second, settings); @@ -1272,7 +1273,7 @@ public: phyTxRemover.Forget(); return MakeFuture(result); } else { - return Gateway->CreateExternalTable(cluster, settings, createDir); + return Gateway->CreateExternalTable(cluster, settings, createDir, existingOk); } } @@ -1283,7 +1284,8 @@ public: } TFuture<TGenericResult> DropExternalTable(const TString& cluster, - const TDropExternalTableSettings& settings) override + const TDropExternalTableSettings& settings, + bool missingOk) override { CHECK_PREPARED_DDL(DropExternalTable); @@ -1307,6 +1309,7 @@ public: auto& schemeTx = *phyTx.MutableSchemeOperation()->MutableDropExternalTable(); schemeTx.SetWorkingDir(pathPair.first); schemeTx.SetOperationType(NKikimrSchemeOp::ESchemeOpDropExternalTable); + schemeTx.SetSuccessOnNotExist(missingOk); NKikimrSchemeOp::TDrop& drop = *schemeTx.MutableDrop(); drop.SetName(pathPair.second); @@ -1316,7 +1319,7 @@ public: phyTxRemover.Forget(); return MakeFuture(result); } else { - return Gateway->DropExternalTable(cluster, settings); + return Gateway->DropExternalTable(cluster, settings, missingOk); } } diff --git a/ydb/core/kqp/provider/yql_kikimr_datasink.cpp b/ydb/core/kqp/provider/yql_kikimr_datasink.cpp index 31f59c2b47..ad14f7a681 100644 --- a/ydb/core/kqp/provider/yql_kikimr_datasink.cpp +++ b/ydb/core/kqp/provider/yql_kikimr_datasink.cpp @@ -856,13 +856,16 @@ public: .Features(settings.Features) .Done() .Ptr(); - } else if (mode == "createObject") { + } else if (mode == "createObject" || mode == "createObjectIfNotExists") { return Build<TKiCreateObject>(ctx, node->Pos()) .World(node->Child(0)) .DataSink(node->Child(1)) .ObjectId().Build(key.GetObjectId()) .TypeId().Build(key.GetObjectType()) .Features(settings.Features) + .ExistingOk<TCoAtom>() + .Value(mode == "createObjectIfNotExists") + .Build() .Done() .Ptr(); } else if (mode == "alterObject") { @@ -874,13 +877,16 @@ public: .Features(settings.Features) .Done() .Ptr(); - } else if (mode == "dropObject") { + } else if (mode == "dropObject" || mode == "dropObjectIfExists") { return Build<TKiDropObject>(ctx, node->Pos()) .World(node->Child(0)) .DataSink(node->Child(1)) .ObjectId().Build(key.GetObjectId()) .TypeId().Build(key.GetObjectType()) .Features(settings.Features) + .MissingOk<TCoAtom>() + .Value(mode == "dropObjectIfExists") + .Build() .Done() .Ptr(); } else { @@ -910,12 +916,15 @@ public: .Settings(settings.Other) .Done() .Ptr(); - } else if (mode == "dropUser") { + } else if (mode == "dropUser" || mode == "dropUserIfExists") { return Build<TKiDropUser>(ctx, node->Pos()) .World(node->Child(0)) .DataSink(node->Child(1)) .UserName().Build(key.GetRoleName()) .Settings(settings.Other) + .MissingOk<TCoAtom>() + .Value(mode == "dropUserIfExists") + .Build() .Done() .Ptr(); } else if (mode == "createGroup") { @@ -943,12 +952,15 @@ public: .NewName(settings.NewName.Cast()) .Done() .Ptr(); - } else if (mode == "dropGroup") { + } else if (mode == "dropGroup" || mode == "dropGroupIfExists") { return Build<TKiDropGroup>(ctx, node->Pos()) .World(node->Child(0)) .DataSink(node->Child(1)) .GroupName().Build(key.GetRoleName()) .Settings(settings.Other) + .MissingOk<TCoAtom>() + .Value(mode == "dropGroupIfExists") + .Build() .Done() .Ptr(); } else { diff --git a/ydb/core/kqp/provider/yql_kikimr_exec.cpp b/ydb/core/kqp/provider/yql_kikimr_exec.cpp index 738b4c2313..14d28479da 100644 --- a/ydb/core/kqp/provider/yql_kikimr_exec.cpp +++ b/ydb/core/kqp/provider/yql_kikimr_exec.cpp @@ -135,13 +135,7 @@ namespace { TDropUserSettings ParseDropUserSettings(TKiDropUser dropUser) { TDropUserSettings dropUserSettings; dropUserSettings.UserName = TString(dropUser.UserName()); - - for (auto setting : dropUser.Settings()) { - auto name = setting.Name().Value(); - if (name == "force") { - dropUserSettings.Force = true; - } - } + dropUserSettings.MissingOk = (dropUser.MissingOk().Value() == "1"); return dropUserSettings; } @@ -182,13 +176,7 @@ namespace { TDropGroupSettings ParseDropGroupSettings(TKiDropGroup dropGroup) { TDropGroupSettings dropGroupSettings; dropGroupSettings.GroupName = TString(dropGroup.GroupName()); - - for (auto setting : dropGroup.Settings()) { - auto name = setting.Name().Value(); - if (name == "force") { - dropGroupSettings.Force = true; - } - } + dropGroupSettings.MissingOk = (dropGroup.MissingOk().Value() == "1"); return dropGroupSettings; } @@ -949,7 +937,7 @@ public: switch (tableTypeItem) { case ETableType::ExternalTable: { future = Gateway->CreateExternalTable(cluster, - ParseCreateExternalTableSettings(maybeCreate.Cast(), table.Metadata->TableSettings), false); + ParseCreateExternalTableSettings(maybeCreate.Cast(), table.Metadata->TableSettings), true, existingOk); break; } case ETableType::TableStore: { @@ -1031,7 +1019,7 @@ public: future = Gateway->DropTableStore(cluster, ParseDropTableStoreSettings(maybeDrop.Cast())); break; case ETableType::ExternalTable: - future = Gateway->DropExternalTable(cluster, ParseDropExternalTableSettings(maybeDrop.Cast())); + future = Gateway->DropExternalTable(cluster, ParseDropExternalTableSettings(maybeDrop.Cast()), missingOk); break; case ETableType::Unknown: ctx.AddError(TIssue(ctx.GetPosition(input->Pos()), TStringBuilder() << "Unsupported table type " << tableTypeString)); diff --git a/ydb/core/kqp/provider/yql_kikimr_expr_nodes.json b/ydb/core/kqp/provider/yql_kikimr_expr_nodes.json index bb6a60883e..bb19b1d669 100644 --- a/ydb/core/kqp/provider/yql_kikimr_expr_nodes.json +++ b/ydb/core/kqp/provider/yql_kikimr_expr_nodes.json @@ -221,7 +221,8 @@ {"Index": 0, "Name": "World", "Type": "TExprBase"}, {"Index": 1, "Name": "DataSink", "Type": "TKiDataSink"}, {"Index": 2, "Name": "UserName", "Type": "TCoAtom"}, - {"Index": 3, "Name": "Settings", "Type": "TCoNameValueTupleList"} + {"Index": 3, "Name": "Settings", "Type": "TCoNameValueTupleList"}, + {"Index": 4, "Name": "MissingOk", "Type": "TCoAtom"} ] }, { @@ -245,7 +246,8 @@ {"Index": 1, "Name": "DataSink", "Type": "TKiDataSink"}, {"Index": 2, "Name": "ObjectId", "Type": "TCoAtom"}, {"Index": 3, "Name": "TypeId", "Type": "TCoAtom"}, - {"Index": 4, "Name": "Features", "Type": "TCoNameValueTupleList"} + {"Index": 4, "Name": "Features", "Type": "TCoNameValueTupleList"}, + {"Index": 5, "Name": "ExistingOk", "Type": "TCoAtom"} ] }, { @@ -269,7 +271,8 @@ {"Index": 1, "Name": "DataSink", "Type": "TKiDataSink"}, {"Index": 2, "Name": "ObjectId", "Type": "TCoAtom"}, {"Index": 3, "Name": "TypeId", "Type": "TCoAtom"}, - {"Index": 4, "Name": "Features", "Type": "TCoNameValueTupleList"} + {"Index": 4, "Name": "Features", "Type": "TCoNameValueTupleList"}, + {"Index": 5, "Name": "MissingOk", "Type": "TCoAtom"} ] }, { @@ -314,7 +317,8 @@ {"Index": 0, "Name": "World", "Type": "TExprBase"}, {"Index": 1, "Name": "DataSink", "Type": "TKiDataSink"}, {"Index": 2, "Name": "GroupName", "Type": "TCoAtom"}, - {"Index": 3, "Name": "Settings", "Type": "TCoNameValueTupleList"} + {"Index": 3, "Name": "Settings", "Type": "TCoNameValueTupleList"}, + {"Index": 4, "Name": "MissingOk", "Type": "TCoAtom"} ] }, { diff --git a/ydb/core/kqp/provider/yql_kikimr_gateway.h b/ydb/core/kqp/provider/yql_kikimr_gateway.h index 3ed993545f..e2dae670bf 100644 --- a/ydb/core/kqp/provider/yql_kikimr_gateway.h +++ b/ydb/core/kqp/provider/yql_kikimr_gateway.h @@ -582,7 +582,7 @@ struct TAlterUserSettings { struct TDropUserSettings { TString UserName; - bool Force = false; + bool MissingOk = false; }; struct TCreateGroupSettings { @@ -608,7 +608,7 @@ struct TRenameGroupSettings { struct TDropGroupSettings { TString GroupName; - bool Force = false; + bool MissingOk = false; }; struct TAlterColumnTableSettings { @@ -837,11 +837,11 @@ public: virtual NThreading::TFuture<TGenericResult> DropTableStore(const TString& cluster, const TDropTableStoreSettings& settings) = 0; - virtual NThreading::TFuture<TGenericResult> CreateExternalTable(const TString& cluster, const TCreateExternalTableSettings& settings, bool createDir) = 0; + virtual NThreading::TFuture<TGenericResult> CreateExternalTable(const TString& cluster, const TCreateExternalTableSettings& settings, bool createDir, bool existingOk) = 0; virtual NThreading::TFuture<TGenericResult> AlterExternalTable(const TString& cluster, const TAlterExternalTableSettings& settings) = 0; - virtual NThreading::TFuture<TGenericResult> DropExternalTable(const TString& cluster, const TDropExternalTableSettings& settings) = 0; + virtual NThreading::TFuture<TGenericResult> DropExternalTable(const TString& cluster, const TDropExternalTableSettings& settings, bool missingOk) = 0; virtual TVector<NKikimrKqp::TKqpTableMetadataProto> GetCollectedSchemeData() = 0; diff --git a/ydb/core/kqp/provider/yql_kikimr_gateway_ut.cpp b/ydb/core/kqp/provider/yql_kikimr_gateway_ut.cpp index 1e4aadfd6a..f55ac55afd 100644 --- a/ydb/core/kqp/provider/yql_kikimr_gateway_ut.cpp +++ b/ydb/core/kqp/provider/yql_kikimr_gateway_ut.cpp @@ -170,7 +170,7 @@ void TestCreateExternalTable(TTestActorRuntime& runtime, TIntrusivePtr<IKikimrGa settings.Columns.insert(std::make_pair("Column2", TKikimrColumnMetadata{"Column2", 0, "String", false})); settings.ColumnOrder.push_back("Column2"); - auto responseFuture = gateway->CreateExternalTable(TestCluster, settings, true); + auto responseFuture = gateway->CreateExternalTable(TestCluster, settings, true, false); responseFuture.Wait(); auto response = responseFuture.GetValue(); response.Issues().PrintTo(Cerr); @@ -190,7 +190,7 @@ void TestCreateExternalTable(TTestActorRuntime& runtime, TIntrusivePtr<IKikimrGa } void TestDropExternalTable(TTestActorRuntime& runtime, TIntrusivePtr<IKikimrGateway> gateway, const TString& path) { - auto responseFuture = gateway->DropExternalTable(TestCluster, TDropExternalTableSettings{.ExternalTable=path}); + auto responseFuture = gateway->DropExternalTable(TestCluster, TDropExternalTableSettings{.ExternalTable=path}, false); responseFuture.Wait(); auto response = responseFuture.GetValue(); response.Issues().PrintTo(Cerr); diff --git a/ydb/core/kqp/provider/yql_kikimr_type_ann.cpp b/ydb/core/kqp/provider/yql_kikimr_type_ann.cpp index 6ac78264aa..836b9d4472 100644 --- a/ydb/core/kqp/provider/yql_kikimr_type_ann.cpp +++ b/ydb/core/kqp/provider/yql_kikimr_type_ann.cpp @@ -761,7 +761,7 @@ virtual TStatus HandleCreateTable(TKiCreateTable create, TExprContext& ctx) over if (defaultType->GetKind() != actualType->GetKind()) { ctx.AddError(TIssue(ctx.GetPosition(constraint.Pos()), TStringBuilder() << "Default expr " << columnName << " type mismatch, expected: " << (*actualType) << ", actual: " << *(defaultType))); - + return TStatus::Error; } @@ -812,7 +812,7 @@ virtual TStatus HandleCreateTable(TKiCreateTable create, TExprContext& ctx) over 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>()) { @@ -822,7 +822,7 @@ virtual TStatus HandleCreateTable(TKiCreateTable create, TExprContext& ctx) over TStringBuilder() << "Unsupported type of default value " << constraint.Value().Cast().Ptr()->Content())); return TStatus::Error; } - + } else if (constraint.Name().Value() == "serial") { if (columnMeta.IsDefaultKindDefined()) { @@ -1263,7 +1263,7 @@ virtual TStatus HandleCreateTable(TKiCreateTable create, TExprContext& ctx) over } } } - + if (columnTuple.Size() > 3) { auto families = columnTuple.Item(3); if (families.Cast<TCoAtomList>().Size() > 1) { @@ -1564,16 +1564,9 @@ virtual TStatus HandleCreateTable(TKiCreateTable create, TExprContext& ctx) over virtual TStatus HandleDropUser(TKiDropUser node, TExprContext& ctx) override { for (const auto& setting : node.Settings()) { auto name = setting.Name().Value(); - if (name == "force") { - if (setting.Value()) { - ctx.AddError(TIssue(ctx.GetPosition(setting.Value().Ref().Pos()), - TStringBuilder() << "force node shouldn't have value" << name)); - } - } else { - ctx.AddError(TIssue(ctx.GetPosition(setting.Name().Pos()), - TStringBuilder() << "Unknown drop user setting: " << name)); - return TStatus::Error; - } + ctx.AddError(TIssue(ctx.GetPosition(setting.Name().Pos()), + TStringBuilder() << "Unknown drop user setting: " << name)); + return TStatus::Error; } node.Ptr()->SetTypeAnn(node.World().Ref().GetTypeAnn()); @@ -1621,16 +1614,9 @@ virtual TStatus HandleCreateTable(TKiCreateTable create, TExprContext& ctx) over virtual TStatus HandleDropGroup(TKiDropGroup node, TExprContext& ctx) override { for (const auto& setting : node.Settings()) { auto name = setting.Name().Value(); - if (name == "force") { - if (setting.Value()) { - ctx.AddError(TIssue(ctx.GetPosition(setting.Value().Ref().Pos()), - TStringBuilder() << "force node shouldn't have value" << name)); - } - } else { - ctx.AddError(TIssue(ctx.GetPosition(setting.Name().Pos()), - TStringBuilder() << "Unknown drop group setting: " << name)); - return TStatus::Error; - } + ctx.AddError(TIssue(ctx.GetPosition(setting.Name().Pos()), + TStringBuilder() << "Unknown drop group setting: " << name)); + return TStatus::Error; } node.Ptr()->SetTypeAnn(node.World().Ref().GetTypeAnn()); diff --git a/ydb/core/kqp/ut/federated_query/s3/kqp_federated_scheme_ut.cpp b/ydb/core/kqp/ut/federated_query/s3/kqp_federated_scheme_ut.cpp index 2b9ebf0dbd..6cc0f2a894 100644 --- a/ydb/core/kqp/ut/federated_query/s3/kqp_federated_scheme_ut.cpp +++ b/ydb/core/kqp/ut/federated_query/s3/kqp_federated_scheme_ut.cpp @@ -120,7 +120,7 @@ Y_UNIT_TEST_SUITE(KqpFederatedSchemeTest) { // create already existing table checkCreate(false, EEx::Empty, 0); // already - //checkCreate(true, EEx::IfNotExists, 0); + checkCreate(true, EEx::IfNotExists, 0); checkTableExists(true, 0); // usual drop @@ -129,21 +129,21 @@ Y_UNIT_TEST_SUITE(KqpFederatedSchemeTest) { checkDrop(false, EEx::Empty, 0); // no such table // drop if exists - //checkDrop(true, EEx::IfExists, 0); + checkDrop(true, EEx::IfExists, 0); checkTableExists(false, 0); // failed attempt to drop nonexisting table checkDrop(false, EEx::Empty, 0); // create with if not exists - //checkCreate(true, EEx::IfNotExists, 1); // real creation - //checkTableExists(true, 1); - //checkCreate(true, EEx::IfNotExists, 1); + checkCreate(true, EEx::IfNotExists, 1); // real creation + checkTableExists(true, 1); + checkCreate(true, EEx::IfNotExists, 1); // drop if exists - //checkDrop(true, EEx::IfExists, 1); // real drop - //checkTableExists(false, 1); - //checkDrop(true, EEx::IfExists, 1); + checkDrop(true, EEx::IfExists, 1); // real drop + checkTableExists(false, 1); + checkDrop(true, EEx::IfExists, 1); } } diff --git a/ydb/core/kqp/ut/service/kqp_qs_queries_ut.cpp b/ydb/core/kqp/ut/service/kqp_qs_queries_ut.cpp index dd257eac3a..0874095214 100644 --- a/ydb/core/kqp/ut/service/kqp_qs_queries_ut.cpp +++ b/ydb/core/kqp/ut/service/kqp_qs_queries_ut.cpp @@ -8,11 +8,14 @@ #include <ydb/core/kqp/counters/kqp_counters.h> +#include <fmt/format.h> + namespace NKikimr { namespace NKqp { using namespace NYdb; using namespace NYdb::NQuery; +using namespace fmt::literals; Y_UNIT_TEST_SUITE(KqpQueryService) { Y_UNIT_TEST(SessionFromPoolError) { @@ -557,33 +560,113 @@ Y_UNIT_TEST_SUITE(KqpQueryService) { TKikimrRunner kikimr(serverSettings); auto db = kikimr.GetQueryClient(); - auto result = db.ExecuteQuery(R"( - CREATE TABLE TestDdl ( - Key Uint64, - Value String, - PRIMARY KEY (Key) + enum EEx { + Empty, + IfExists, + IfNotExists, + }; + + auto checkCreate = [&](bool expectSuccess, EEx exMode, int nameSuffix) { + UNIT_ASSERT_UNEQUAL(exMode, EEx::IfExists); + const TString ifNotExistsStatement = exMode == EEx::IfNotExists ? "IF NOT EXISTS" : ""; + const TString sql = fmt::format(R"sql( + CREATE TABLE {if_not_exists} TestDdl_{name_suffix} ( + Key Uint64, + Value String, + PRIMARY KEY (Key) + ); + )sql", + "if_not_exists"_a = ifNotExistsStatement, + "name_suffix"_a = nameSuffix ); - )", TTxControl::NoTx()).ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); - UNIT_ASSERT(result.GetResultSets().empty()); - result = db.ExecuteQuery(R"( - UPSERT INTO TestDdl (Key, Value) VALUES (1, "One"); - SELECT * FROM TestDdl; - )", TTxControl::BeginTx().CommitTx()).ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + auto result = db.ExecuteQuery(sql, TTxControl::NoTx()).ExtractValueSync(); + if (expectSuccess) { + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } else { + UNIT_ASSERT_VALUES_UNEQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + UNIT_ASSERT(result.GetResultSets().empty()); + }; - CompareYson(R"([[[1u];["One"]]])", FormatResultSetYson(result.GetResultSet(0))); + auto checkDrop = [&](bool expectSuccess, EEx exMode, int nameSuffix) { + UNIT_ASSERT_UNEQUAL(exMode, EEx::IfNotExists); + const TString ifExistsStatement = exMode == EEx::IfExists ? "IF EXISTS" : ""; + const TString sql = fmt::format(R"sql( + DROP TABLE {if_exists} TestDdl_{name_suffix}; + )sql", + "if_exists"_a = ifExistsStatement, + "name_suffix"_a = nameSuffix + ); - result = db.ExecuteQuery(R"( - DROP TABLE TestDdl; - )", TTxControl::NoTx()).ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + auto result = db.ExecuteQuery(sql, TTxControl::NoTx()).ExtractValueSync(); + if (expectSuccess) { + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } else { + UNIT_ASSERT_VALUES_UNEQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + UNIT_ASSERT(result.GetResultSets().empty()); + }; - result = db.ExecuteQuery(R"( - SELECT * FROM TestDdl; - )", TTxControl::BeginTx().CommitTx()).ExtractValueSync(); - UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SCHEME_ERROR, result.GetIssues().ToString()); + auto checkUpsert = [&](int nameSuffix) { + const TString sql = fmt::format(R"sql( + UPSERT INTO TestDdl_{name_suffix} (Key, Value) VALUES (1, "One"); + )sql", + "name_suffix"_a = nameSuffix + ); + + auto result = db.ExecuteQuery(sql, TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + }; + + auto checkExists = [&](bool expectSuccess, int nameSuffix) { + const TString sql = fmt::format(R"sql( + SELECT * FROM TestDdl_{name_suffix}; + )sql", + "name_suffix"_a = nameSuffix + ); + auto result = db.ExecuteQuery(sql, TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + + if (expectSuccess) { + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + CompareYson(R"([[[1u];["One"]]])", FormatResultSetYson(result.GetResultSet(0))); + } else { + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SCHEME_ERROR, result.GetIssues().ToString()); + } + }; + + // usual create + checkCreate(true, EEx::Empty, 0); + checkUpsert(0); + checkExists(true, 0); + + // create already existing table + checkCreate(false, EEx::Empty, 0); // already exists + checkCreate(true, EEx::IfNotExists, 0); + checkExists(true, 0); + + // usual drop + checkDrop(true, EEx::Empty, 0); + checkExists(false, 0); + checkDrop(false, EEx::Empty, 0); // no such table + + // drop if exists + checkDrop(true, EEx::IfExists, 0); + checkExists(false, 0); + + // failed attempt to drop nonexisting table + checkDrop(false, EEx::Empty, 0); + + // create with if not exists + checkCreate(true, EEx::IfNotExists, 1); // real creation + checkUpsert(1); + checkExists(true, 1); + checkCreate(true, EEx::IfNotExists, 1); + + // drop if exists + checkDrop(true, EEx::IfExists, 1); // real drop + checkExists(false, 1); + checkDrop(true, EEx::IfExists, 1); } Y_UNIT_TEST(DdlUser) { diff --git a/ydb/core/kqp/ut/service/ya.make b/ydb/core/kqp/ut/service/ya.make index 92d7725ab1..646e1b16ed 100644 --- a/ydb/core/kqp/ut/service/ya.make +++ b/ydb/core/kqp/ut/service/ya.make @@ -20,6 +20,7 @@ SRCS( ) PEERDIR( + contrib/libs/fmt library/cpp/threading/local_executor ydb/core/kqp ydb/core/kqp/ut/common diff --git a/ydb/library/yql/sql/v1/SQLv1.g.in b/ydb/library/yql/sql/v1/SQLv1.g.in index 2ed953f0d4..2e844b4646 100644 --- a/ydb/library/yql/sql/v1/SQLv1.g.in +++ b/ydb/library/yql/sql/v1/SQLv1.g.in @@ -568,11 +568,11 @@ values_source_row: LPAREN expr_list RPAREN; simple_values_source: expr_list | select_stmt; -create_external_data_source_stmt: CREATE EXTERNAL DATA SOURCE object_ref +create_external_data_source_stmt: CREATE EXTERNAL DATA SOURCE (IF NOT EXISTS)? object_ref with_table_settings ; -drop_external_data_source_stmt: DROP EXTERNAL DATA SOURCE object_ref; +drop_external_data_source_stmt: DROP EXTERNAL DATA SOURCE (IF EXISTS)? object_ref; create_view_stmt: CREATE VIEW object_ref with_table_settings @@ -585,7 +585,7 @@ upsert_object_stmt: UPSERT OBJECT object_ref LPAREN TYPE object_type_ref RPAREN create_object_features? ; -create_object_stmt: CREATE OBJECT object_ref +create_object_stmt: CREATE OBJECT (IF NOT EXISTS)? object_ref LPAREN TYPE object_type_ref RPAREN create_object_features? ; @@ -597,7 +597,7 @@ alter_object_stmt: ALTER OBJECT object_ref ; alter_object_features: SET object_features; -drop_object_stmt: DROP OBJECT object_ref +drop_object_stmt: DROP OBJECT (IF EXISTS)? object_ref LPAREN TYPE object_type_ref RPAREN drop_object_features? ; @@ -611,7 +611,7 @@ object_features: object_feature | LPAREN object_feature (COMMA object_feature)* object_type_ref: an_id_or_type; -create_table_stmt: CREATE (TABLE | TABLESTORE | EXTERNAL TABLE) simple_table_ref LPAREN create_table_entry (COMMA create_table_entry)* COMMA? RPAREN +create_table_stmt: CREATE (TABLE | TABLESTORE | EXTERNAL TABLE) (IF NOT EXISTS)? simple_table_ref LPAREN create_table_entry (COMMA create_table_entry)* COMMA? RPAREN table_inherits? table_partition_by? with_table_settings? diff --git a/ydb/library/yql/sql/v1/format/sql_format.cpp b/ydb/library/yql/sql/v1/format/sql_format.cpp index e92fa59527..9294fd1f67 100644 --- a/ydb/library/yql/sql/v1/format/sql_format.cpp +++ b/ydb/library/yql/sql/v1/format/sql_format.cpp @@ -421,7 +421,7 @@ private: } else { VisitAllFields(descr, msg); } - + if (scopePtr) { Scopes.pop_back(); } @@ -890,27 +890,24 @@ private: NewLine(); Visit(msg.GetToken1()); Visit(msg.GetBlock2()); - Visit(msg.GetRule_simple_table_ref3()); - Visit(msg.GetToken4()); + Visit(msg.GetBlock3()); + Visit(msg.GetRule_simple_table_ref4()); + Visit(msg.GetToken5()); PushCurrentIndent(); NewLine(); - Visit(msg.GetRule_create_table_entry5()); - for (const auto& b : msg.GetBlock6()) { + Visit(msg.GetRule_create_table_entry6()); + for (const auto& b : msg.GetBlock7()) { Visit(b.GetToken1()); NewLine(); Visit(b.GetRule_create_table_entry2()); } - if (msg.HasBlock7()) { - Visit(msg.GetBlock7()); + if (msg.HasBlock8()) { + Visit(msg.GetBlock8()); } PopCurrentIndent(); NewLine(); - Visit(msg.GetToken8()); - if (msg.HasBlock9()) { - NewLine(); - Visit(msg.GetBlock9()); - } + Visit(msg.GetToken9()); if (msg.HasBlock10()) { NewLine(); Visit(msg.GetBlock10()); @@ -923,6 +920,10 @@ private: NewLine(); Visit(msg.GetBlock12()); } + if (msg.HasBlock13()) { + NewLine(); + Visit(msg.GetBlock13()); + } } void VisitDropTable(const TRule_drop_table_stmt& msg) { @@ -1556,7 +1557,7 @@ private: } if (str == "," && !MarkTokenStack.empty()) { - const bool addNewline = + const bool addNewline = (TokenIndex + 1 < ParsedTokens.size() && ParsedTokens[TokenIndex].Line != ParsedTokens[TokenIndex + 1].Line) || (TokenIndex > 0 && ParsedTokens[TokenIndex - 1].Line != ParsedTokens[TokenIndex].Line); // add line for trailing comma @@ -2102,7 +2103,7 @@ private: NewLine(); PushCurrentIndent(); } - + if (details.HasBlock1()) { NewLine(); Visit(details.GetBlock1()); @@ -2767,7 +2768,7 @@ public: finalFormattedQuery << currentFormattedQuery; if (parsedTokens.back().Name != "SEMICOLON") { - if (hasTrailingComments + if (hasTrailingComments && !comments.back().Content.EndsWith("\n") && comments.back().Content.StartsWith("--")) { finalFormattedQuery << "\n"; diff --git a/ydb/library/yql/sql/v1/format/sql_format_ut.cpp b/ydb/library/yql/sql/v1/format/sql_format_ut.cpp index 8b2dd40882..5c16924f96 100644 --- a/ydb/library/yql/sql/v1/format/sql_format_ut.cpp +++ b/ydb/library/yql/sql/v1/format/sql_format_ut.cpp @@ -306,7 +306,8 @@ Y_UNIT_TEST_SUITE(CheckSqlFormatter) { "\tbar bool?\n" ")\n" "PARTITION BY HASH (a, b, hash)\n" - "WITH (tiering = 'some');\n"} + "WITH (tiering = 'some');\n"}, + {"create table if not exists user(user int32)", "CREATE TABLE IF NOT EXISTS user (\n\tuser int32\n);\n"} }; TSetup setup; @@ -319,10 +320,14 @@ Y_UNIT_TEST_SUITE(CheckSqlFormatter) { "ALTER OBJECT usEr (TYPE abcde) SET (a = b);\n"}, {"creAte oBject usEr (tYpe abcde) With (a = b)", "CREATE OBJECT usEr (TYPE abcde) WITH (a = b);\n"}, + {"creAte oBject if not exIstS usEr (tYpe abcde) With (a = b)", + "CREATE OBJECT IF NOT EXISTS usEr (TYPE abcde) WITH (a = b);\n"}, {"creAte oBject usEr (tYpe abcde) With a = b", "CREATE OBJECT usEr (TYPE abcde) WITH a = b;\n"}, {"dRop oBject usEr (tYpe abcde) With (aeEE)", "DROP OBJECT usEr (TYPE abcde) WITH (aeEE);\n"}, + {"dRop oBject If ExistS usEr (tYpe abcde) With (aeEE)", + "DROP OBJECT IF EXISTS usEr (TYPE abcde) WITH (aeEE);\n"}, {"dRop oBject usEr (tYpe abcde) With aeEE", "DROP OBJECT usEr (TYPE abcde) WITH aeEE;\n"} }; @@ -330,7 +335,7 @@ Y_UNIT_TEST_SUITE(CheckSqlFormatter) { TSetup setup; setup.Run(cases); } - + Y_UNIT_TEST(TableStoreOperations) { TCases cases = { {"alter tableStore uSer aDd column usEr int32", @@ -347,8 +352,12 @@ Y_UNIT_TEST_SUITE(CheckSqlFormatter) { TCases cases = { {"creAte exTernAl daTa SouRce usEr With (a = \"b\")", "CREATE EXTERNAL DATA SOURCE usEr WITH (a = \"b\");\n"}, + {"creAte exTernAl daTa SouRce if not exists usEr With (a = \"b\")", + "CREATE EXTERNAL DATA SOURCE IF NOT EXISTS usEr WITH (a = \"b\");\n"}, {"dRop exTerNal Data SouRce usEr", "DROP EXTERNAL DATA SOURCE usEr;\n"}, + {"dRop exTerNal Data SouRce if exists usEr", + "DROP EXTERNAL DATA SOURCE IF EXISTS usEr;\n"}, }; TSetup setup; @@ -373,8 +382,12 @@ Y_UNIT_TEST_SUITE(CheckSqlFormatter) { TCases cases = { {"creAte exTernAl TabLe usEr (a int) With (a = \"b\")", "CREATE EXTERNAL TABLE usEr (\n\ta int\n)\nWITH (a = \"b\");\n"}, + {"creAte exTernAl TabLe iF NOt Exists usEr (a int) With (a = \"b\")", + "CREATE EXTERNAL TABLE IF NOT EXISTS usEr (\n\ta int\n)\nWITH (a = \"b\");\n"}, {"dRop exTerNal taBlE usEr", "DROP EXTERNAL TABLE usEr;\n"}, + {"dRop exTerNal taBlE iF eXiStS usEr", + "DROP EXTERNAL TABLE IF EXISTS usEr;\n"}, }; TSetup setup; @@ -1349,7 +1362,7 @@ FROM Input MATCH_RECOGNIZE (PATTERN (A) DEFINE A AS A); Y_UNIT_TEST(Union) { TCases cases = { - {"select 1 union all select 2 union select 3 union all select 4 union select 5", + {"select 1 union all select 2 union select 3 union all select 4 union select 5", "SELECT\n\t1\nUNION ALL\nSELECT\n\t2\nUNION\nSELECT\n\t3\nUNION ALL\nSELECT\n\t4\nUNION\nSELECT\n\t5;\n"}, }; @@ -1375,11 +1388,11 @@ FROM Input MATCH_RECOGNIZE (PATTERN (A) DEFINE A AS A); Y_UNIT_TEST(WindowFunctionInsideExpr) { TCases cases = { - {"SELECT CAST(ROW_NUMBER() OVER () AS String) AS x,\nFROM Input;", + {"SELECT CAST(ROW_NUMBER() OVER () AS String) AS x,\nFROM Input;", "SELECT\n\tCAST(ROW_NUMBER() OVER () AS String) AS x,\nFROM Input;\n"}, - {"SELECT CAST(ROW_NUMBER() OVER (PARTITION BY key) AS String) AS x,\nFROM Input;", + {"SELECT CAST(ROW_NUMBER() OVER (PARTITION BY key) AS String) AS x,\nFROM Input;", "SELECT\n\tCAST(\n\t\tROW_NUMBER() OVER (\n\t\t\tPARTITION BY\n\t\t\t\tkey\n\t\t) AS String\n\t) AS x,\nFROM Input;\n"}, - {"SELECT CAST(ROW_NUMBER() OVER (users) AS String) AS x,\nFROM Input;", + {"SELECT CAST(ROW_NUMBER() OVER (users) AS String) AS x,\nFROM Input;", "SELECT\n\tCAST(\n\t\tROW_NUMBER() OVER (\n\t\t\tusers\n\t\t) AS String\n\t) AS x,\nFROM Input;\n"}, }; @@ -1389,9 +1402,9 @@ FROM Input MATCH_RECOGNIZE (PATTERN (A) DEFINE A AS A); Y_UNIT_TEST(ExistsExpr) { TCases cases = { - {"SELECT EXISTS (SELECT 1);", + {"SELECT EXISTS (SELECT 1);", "SELECT\n\tEXISTS (\n\t\tSELECT\n\t\t\t1\n\t);\n"}, - {"SELECT CAST(EXISTS(SELECT 1) AS Int) AS x,\nFROM Input;", + {"SELECT CAST(EXISTS(SELECT 1) AS Int) AS x,\nFROM Input;", "SELECT\n\tCAST(\n\t\tEXISTS (\n\t\t\tSELECT\n\t\t\t\t1\n\t\t) AS Int\n\t) AS x,\nFROM Input;\n"}, }; @@ -1401,7 +1414,7 @@ FROM Input MATCH_RECOGNIZE (PATTERN (A) DEFINE A AS A); Y_UNIT_TEST(LambdaInsideExpr) { TCases cases = { - {"SELECT ListMap(AsList(1,2),($x)->{return $x+1});", + {"SELECT ListMap(AsList(1,2),($x)->{return $x+1});", "SELECT\n\tListMap(\n\t\tAsList(1, 2), ($x) -> {\n\t\t\tRETURN $x + 1\n\t\t}\n\t);\n"}, }; @@ -1411,11 +1424,11 @@ FROM Input MATCH_RECOGNIZE (PATTERN (A) DEFINE A AS A); Y_UNIT_TEST(CaseExpr) { TCases cases = { - {"SELECT CASE WHEN 1 == 2 THEN 3 WHEN 4 == 5 THEN 6 WHEN 7 == 8 THEN 9 ELSE 10 END;", + {"SELECT CASE WHEN 1 == 2 THEN 3 WHEN 4 == 5 THEN 6 WHEN 7 == 8 THEN 9 ELSE 10 END;", "SELECT\n\tCASE\n\t\tWHEN 1 == 2\n\t\t\tTHEN 3\n\t\tWHEN 4 == 5\n\t\t\tTHEN 6\n\t\tWHEN 7 == 8\n\t\t\tTHEN 9\n\t\tELSE 10\n\tEND;\n"}, - {"SELECT CAST(CASE WHEN 1 == 2 THEN 3 WHEN 4 == 5 THEN 6 ELSE 10 END AS String);", + {"SELECT CAST(CASE WHEN 1 == 2 THEN 3 WHEN 4 == 5 THEN 6 ELSE 10 END AS String);", "SELECT\n\tCAST(\n\t\tCASE\n\t\t\tWHEN 1 == 2\n\t\t\t\tTHEN 3\n\t\t\tWHEN 4 == 5\n\t\t\t\tTHEN 6\n\t\t\tELSE 10\n\t\tEND AS String\n\t);\n"}, - {"SELECT CASE x WHEN 1 THEN 2 WHEN 3 THEN 4 WHEN 5 THEN 6 ELSE 10 END;", + {"SELECT CASE x WHEN 1 THEN 2 WHEN 3 THEN 4 WHEN 5 THEN 6 ELSE 10 END;", "SELECT\n\tCASE x\n\t\tWHEN 1\n\t\t\tTHEN 2\n\t\tWHEN 3\n\t\t\tTHEN 4\n\t\tWHEN 5\n\t\t\tTHEN 6\n\t\tELSE 10\n\tEND;\n"}, }; @@ -1425,13 +1438,13 @@ FROM Input MATCH_RECOGNIZE (PATTERN (A) DEFINE A AS A); Y_UNIT_TEST(MultiTokenOperations) { TCases cases = { - {"$x = 1 >>| 2;", + {"$x = 1 >>| 2;", "$x = 1 >>| 2;\n"}, - {"$x = 1 >> 2;", + {"$x = 1 >> 2;", "$x = 1 >> 2;\n"}, - {"$x = 1 ?? 2;", + {"$x = 1 ?? 2;", "$x = 1 ?? 2;\n"}, - {"$x = 1 > /*comment*/ > /*comment*/ | 2;", + {"$x = 1 > /*comment*/ > /*comment*/ | 2;", "$x = 1 >/*comment*/>/*comment*/| 2;\n"}, }; @@ -1441,29 +1454,29 @@ FROM Input MATCH_RECOGNIZE (PATTERN (A) DEFINE A AS A); Y_UNIT_TEST(OperatorNewlines) { TCases cases = { - {"$x = TRUE\nOR\nFALSE;", + {"$x = TRUE\nOR\nFALSE;", "$x = TRUE\n\tOR\n\tFALSE;\n"}, - {"$x = TRUE OR\nFALSE;", + {"$x = TRUE OR\nFALSE;", "$x = TRUE OR\n\tFALSE;\n"}, - {"$x = TRUE\nOR FALSE;", + {"$x = TRUE\nOR FALSE;", "$x = TRUE OR\n\tFALSE;\n"}, - {"$x = 1\n+2\n*3;", + {"$x = 1\n+2\n*3;", "$x = 1 +\n\t2 *\n\t\t3;\n"}, - {"$x = 1\n+\n2\n*3\n*5\n+\n4;", + {"$x = 1\n+\n2\n*3\n*5\n+\n4;", "$x = 1\n\t+\n\t2 *\n\t\t3 *\n\t\t5\n\t+\n\t4;\n"}, - {"$x = 1\n+2+3+4\n+5+6+7+\n\n8+9+10;", + {"$x = 1\n+2+3+4\n+5+6+7+\n\n8+9+10;", "$x = 1 +\n\t2 + 3 + 4 +\n\t5 + 6 + 7 +\n\t8 + 9 + 10;\n"}, - {"$x = TRUE\nAND\nTRUE OR\nFALSE\nAND TRUE\nOR FALSE\nAND TRUE\nOR FALSE;", + {"$x = TRUE\nAND\nTRUE OR\nFALSE\nAND TRUE\nOR FALSE\nAND TRUE\nOR FALSE;", "$x = TRUE\n\tAND\n\tTRUE OR\n\tFALSE AND\n\t\tTRUE OR\n\tFALSE AND\n\t\tTRUE OR\n\tFALSE;\n"}, - {"$x = 1 -- comment\n+ 2;", + {"$x = 1 -- comment\n+ 2;", "$x = 1-- comment\n\t+\n\t2;\n"}, - {"$x = 1 -- comment\n+ -- comment\n2;", + {"$x = 1 -- comment\n+ -- comment\n2;", "$x = 1-- comment\n\t+-- comment\n\t2;\n"}, - {"$x = 1 + -- comment\n2;", + {"$x = 1 + -- comment\n2;", "$x = 1 +-- comment\n\t2;\n"}, - {"$x = 1\n>\n>\n|\n2;", + {"$x = 1\n>\n>\n|\n2;", "$x = 1\n\t>>|\n\t2;\n"}, - {"$x = 1\n?? 2 ??\n3\n??\n4 +\n5\n*\n6 +\n7 ??\n8;", + {"$x = 1\n?? 2 ??\n3\n??\n4 +\n5\n*\n6 +\n7 ??\n8;", "$x = 1 ??\n\t2 ??\n\t3\n\t??\n\t4 +\n\t\t5\n\t\t\t*\n\t\t\t6 +\n\t\t7 ??\n\t8;\n"}, }; diff --git a/ydb/library/yql/sql/v1/node.h b/ydb/library/yql/sql/v1/node.h index 7147948cfb..63aa0e3cd5 100644 --- a/ydb/library/yql/sql/v1/node.h +++ b/ydb/library/yql/sql/v1/node.h @@ -1225,17 +1225,17 @@ namespace NSQLTranslationV1 { TNodePtr BuildAlterGroup(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TVector<TDeferredAtom>& toChange, bool isDrop, TScopedStatePtr scoped); TNodePtr BuildRenameGroup(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TDeferredAtom& newName, TScopedStatePtr scoped); - TNodePtr BuildDropRoles(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TVector<TDeferredAtom>& toDrop, bool isUser, bool force, TScopedStatePtr scoped); + TNodePtr BuildDropRoles(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TVector<TDeferredAtom>& toDrop, bool isUser, bool missingOk, TScopedStatePtr scoped); TNodePtr BuildGrantPermissions(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TVector<TDeferredAtom>& permissions, const TVector<TDeferredAtom>& schemaPaths, const TVector<TDeferredAtom>& roleName, TScopedStatePtr scoped); TNodePtr BuildRevokePermissions(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TVector<TDeferredAtom>& permissions, const TVector<TDeferredAtom>& schemaPaths, const TVector<TDeferredAtom>& roleName, TScopedStatePtr scoped); TNodePtr BuildUpsertObjectOperation(TPosition pos, const TString& objectId, const TString& typeId, std::map<TString, TDeferredAtom>&& features, const TObjectOperatorContext& context); TNodePtr BuildCreateObjectOperation(TPosition pos, const TString& objectId, const TString& typeId, - std::map<TString, TDeferredAtom>&& features, const TObjectOperatorContext& context); + bool existingOk, std::map<TString, TDeferredAtom>&& features, const TObjectOperatorContext& context); TNodePtr BuildAlterObjectOperation(TPosition pos, const TString& secretId, const TString& typeId, std::map<TString, TDeferredAtom>&& features, const TObjectOperatorContext& context); TNodePtr BuildDropObjectOperation(TPosition pos, const TString& secretId, const TString& typeId, - std::map<TString, TDeferredAtom>&& options, const TObjectOperatorContext& context); + bool missingOk, std::map<TString, TDeferredAtom>&& options, const TObjectOperatorContext& context); TNodePtr BuildCreateAsyncReplication(TPosition pos, const TString& id, std::vector<std::pair<TString, TString>>&& targets, std::map<TString, TNodePtr>&& settings, diff --git a/ydb/library/yql/sql/v1/object_processing.h b/ydb/library/yql/sql/v1/object_processing.h index 50631c0c09..257e98bf81 100644 --- a/ydb/library/yql/sql/v1/object_processing.h +++ b/ydb/library/yql/sql/v1/object_processing.h @@ -38,16 +38,18 @@ private: using TBase = TObjectProcessorImpl; std::map<TString, TDeferredAtom> Features; protected: + bool ExistingOk = false; +protected: virtual INode::TPtr BuildOptions() const override { - return Y(Q(Y(Q("mode"), Q("createObject")))); + return Y(Q(Y(Q("mode"), Q(ExistingOk ? "createObjectIfNotExists" : "createObject")))); } virtual INode::TPtr FillFeatures(INode::TPtr options) const override; public: TCreateObject(TPosition pos, const TString& objectId, - const TString& typeId, std::map<TString, TDeferredAtom>&& features, const TObjectOperatorContext& context) + const TString& typeId, bool existingOk, std::map<TString, TDeferredAtom>&& features, const TObjectOperatorContext& context) : TBase(pos, objectId, typeId, context) - , Features(std::move(features)) { - + , Features(std::move(features)) + , ExistingOk(existingOk) { } }; @@ -76,9 +78,12 @@ public: class TDropObject final: public TCreateObject { private: using TBase = TCreateObject; + bool MissingOk() const { + return ExistingOk; // Because we were derived from TCreateObject + } protected: virtual INode::TPtr BuildOptions() const override { - return Y(Q(Y(Q("mode"), Q("dropObject")))); + return Y(Q(Y(Q("mode"), Q(MissingOk() ? "dropObjectIfExists" : "dropObject")))); } public: using TBase::TBase; diff --git a/ydb/library/yql/sql/v1/query.cpp b/ydb/library/yql/sql/v1/query.cpp index 68258837a7..08a7289330 100644 --- a/ydb/library/yql/sql/v1/query.cpp +++ b/ydb/library/yql/sql/v1/query.cpp @@ -543,7 +543,7 @@ public: postHandler = arg.Expr; } else { - ctx.Warning(Pos, DEFAULT_ERROR) << "Unsupported named argument: " + ctx.Warning(Pos, DEFAULT_ERROR) << "Unsupported named argument: " << label << " in " << Func; } } @@ -568,7 +568,7 @@ public: if (postHandler == nullptr) { postHandler = Y("Void"); } - + const auto makeResolveDiveHandlerType = BuildBind(Pos, walkFoldersModuleName, "MakeResolveDiveHandlersType"); const auto resolveDiveHandlerType = Y("EvaluateType", Y("TypeHandle", Y("Apply", makeResolveDiveHandlerType, Y("TypeOf", initState)))); if (resolveHandler == nullptr) { @@ -808,10 +808,11 @@ TNodePtr BuildInputTables(TPosition pos, const TTableList& tables, bool inSubque class TCreateTableNode final: public TAstListNode { public: - TCreateTableNode(TPosition pos, const TTableRef& tr, const TCreateTableParameters& params, TScopedStatePtr scoped) + TCreateTableNode(TPosition pos, const TTableRef& tr, bool existingOk, const TCreateTableParameters& params, TScopedStatePtr scoped) : TAstListNode(pos) , Table(tr) , Params(params) + , ExistingOk(existingOk) , Scoped(scoped) { scoped->UseCluster(Table.Service, Table.Cluster); @@ -897,7 +898,7 @@ public: opts = Table.Options; } - opts = L(opts, Q(Y(Q("mode"), Q("create")))); + opts = L(opts, Q(Y(Q("mode"), Q(ExistingOk ? "create_if_not_exists" : "create")))); THashSet<TString> columnFamilyNames; @@ -1146,12 +1147,13 @@ public: private: const TTableRef Table; const TCreateTableParameters Params; + const bool ExistingOk; TScopedStatePtr Scoped; }; -TNodePtr BuildCreateTable(TPosition pos, const TTableRef& tr, const TCreateTableParameters& params, TScopedStatePtr scoped) +TNodePtr BuildCreateTable(TPosition pos, const TTableRef& tr, bool existingOk, const TCreateTableParameters& params, TScopedStatePtr scoped) { - return new TCreateTableNode(pos, tr, params, scoped); + return new TCreateTableNode(pos, tr, existingOk, params, scoped); } class TAlterTableNode final: public TAstListNode { @@ -1405,11 +1407,12 @@ TNodePtr BuildAlterTable(TPosition pos, const TTableRef& tr, const TAlterTablePa class TDropTableNode final: public TAstListNode { public: - TDropTableNode(TPosition pos, const TTableRef& tr, ETableType tableType, TScopedStatePtr scoped) + TDropTableNode(TPosition pos, const TTableRef& tr, bool missingOk, ETableType tableType, TScopedStatePtr scoped) : TAstListNode(pos) , Table(tr) , TableType(tableType) , Scoped(scoped) + , MissingOk(missingOk) { FakeSource = BuildFakeSource(pos); scoped->UseCluster(Table.Service, Table.Cluster); @@ -1424,7 +1427,7 @@ public: auto opts = Y(); - opts = L(opts, Q(Y(Q("mode"), Q("drop")))); + opts = L(opts, Q(Y(Q("mode"), Q(MissingOk ? "drop_if_exists" : "drop")))); switch (TableType) { case ETableType::TableStore: @@ -1454,10 +1457,11 @@ private: ETableType TableType; TScopedStatePtr Scoped; TSourcePtr FakeSource; + const bool MissingOk; }; -TNodePtr BuildDropTable(TPosition pos, const TTableRef& tr, ETableType tableType, TScopedStatePtr scoped) { - return new TDropTableNode(pos, tr, tableType, scoped); +TNodePtr BuildDropTable(TPosition pos, const TTableRef& tr, bool missingOk, ETableType tableType, TScopedStatePtr scoped) { + return new TDropTableNode(pos, tr, missingOk, tableType, scoped); } @@ -2021,13 +2025,13 @@ TNodePtr BuildAlterGroup(TPosition pos, const TString& service, const TDeferredA class TDropRoles final: public TAstListNode { public: - TDropRoles(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TVector<TDeferredAtom>& toDrop, bool isUser, bool force, TScopedStatePtr scoped) + TDropRoles(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TVector<TDeferredAtom>& toDrop, bool isUser, bool missingOk, TScopedStatePtr scoped) : TAstListNode(pos) , Service(service) , Cluster(cluster) , ToDrop(toDrop) , IsUser(isUser) - , Force(force) + , MissingOk(missingOk) , Scoped(scoped) { FakeSource = BuildFakeSource(pos); @@ -2042,11 +2046,11 @@ public: return false; } - auto options = Y(Q(Y(Q("mode"), Q(IsUser ? "dropUser" : "dropGroup")))); - if (Force) { - options = L(options, Q(Y(Q("force")))); - } + const char* mode = IsUser ? + (MissingOk ? "dropUserIfExists" : "dropUser") : + (MissingOk ? "dropGroupIfExists" : "dropGroup"); + auto options = Y(Q(Y(Q("mode"), Q(mode)))); auto block = Y(Y("let", "sink", Y("DataSink", BuildQuotedAtom(Pos, Service), cluster))); for (auto& item : ToDrop) { @@ -2071,32 +2075,32 @@ private: TDeferredAtom Cluster; TVector<TDeferredAtom> ToDrop; const bool IsUser; - const bool Force; + const bool MissingOk; TScopedStatePtr Scoped; TSourcePtr FakeSource; }; TNodePtr BuildUpsertObjectOperation(TPosition pos, const TString& objectId, const TString& typeId, std::map<TString, TDeferredAtom>&& features, const TObjectOperatorContext& context) { - return new TUpsertObject(pos, objectId, typeId, std::move(features), context); + return new TUpsertObject(pos, objectId, typeId, false, std::move(features), context); } TNodePtr BuildCreateObjectOperation(TPosition pos, const TString& objectId, const TString& typeId, - std::map<TString, TDeferredAtom>&& features, const TObjectOperatorContext& context) { - return new TCreateObject(pos, objectId, typeId, std::move(features), context); + bool existingOk, std::map<TString, TDeferredAtom>&& features, const TObjectOperatorContext& context) { + return new TCreateObject(pos, objectId, typeId, existingOk, std::move(features), context); } TNodePtr BuildAlterObjectOperation(TPosition pos, const TString& secretId, const TString& typeId, std::map<TString, TDeferredAtom>&& features, const TObjectOperatorContext& context) { - return new TAlterObject(pos, secretId, typeId, std::move(features), context); + return new TAlterObject(pos, secretId, typeId, false, std::move(features), context); } TNodePtr BuildDropObjectOperation(TPosition pos, const TString& secretId, const TString& typeId, - std::map<TString, TDeferredAtom>&& options, const TObjectOperatorContext& context) + bool missingOk, std::map<TString, TDeferredAtom>&& options, const TObjectOperatorContext& context) { - return new TDropObject(pos, secretId, typeId, std::move(options), context); + return new TDropObject(pos, secretId, typeId, missingOk, std::move(options), context); } -TNodePtr BuildDropRoles(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TVector<TDeferredAtom>& toDrop, bool isUser, bool force, TScopedStatePtr scoped) { - return new TDropRoles(pos, service, cluster, toDrop, isUser, force, scoped); +TNodePtr BuildDropRoles(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TVector<TDeferredAtom>& toDrop, bool isUser, bool missingOk, TScopedStatePtr scoped) { + return new TDropRoles(pos, service, cluster, toDrop, isUser, missingOk, scoped); } class TPermissionsAction final : public TAstListNode { diff --git a/ydb/library/yql/sql/v1/source.h b/ydb/library/yql/sql/v1/source.h index 516c93e10a..4e2489d976 100644 --- a/ydb/library/yql/sql/v1/source.h +++ b/ydb/library/yql/sql/v1/source.h @@ -295,9 +295,9 @@ namespace NSQLTranslationV1 { TNodePtr BuildTopicKey(TPosition pos, const TDeferredAtom& cluster, const TDeferredAtom& name); TNodePtr BuildInputOptions(TPosition pos, const TTableHints& hints); TNodePtr BuildInputTables(TPosition pos, const TTableList& tables, bool inSubquery, TScopedStatePtr scoped); - TNodePtr BuildCreateTable(TPosition pos, const TTableRef& tr, const TCreateTableParameters& params, TScopedStatePtr scoped); + TNodePtr BuildCreateTable(TPosition pos, const TTableRef& tr, bool existingOk, const TCreateTableParameters& params, TScopedStatePtr scoped); TNodePtr BuildAlterTable(TPosition pos, const TTableRef& tr, const TAlterTableParameters& params, TScopedStatePtr scoped); - TNodePtr BuildDropTable(TPosition pos, const TTableRef& table, ETableType tableType, TScopedStatePtr scoped); + TNodePtr BuildDropTable(TPosition pos, const TTableRef& table, bool missingOk, ETableType tableType, TScopedStatePtr scoped); TNodePtr BuildWriteTable(TPosition pos, const TString& label, const TTableRef& table, EWriteColumnMode mode, TNodePtr options, TScopedStatePtr scoped); TSourcePtr TryMakeSourceFromExpression(TContext& ctx, const TString& currService, const TDeferredAtom& currCluster, diff --git a/ydb/library/yql/sql/v1/sql_query.cpp b/ydb/library/yql/sql/v1/sql_query.cpp index 95539775d9..fda7ad43a3 100644 --- a/ydb/library/yql/sql/v1/sql_query.cpp +++ b/ydb/library/yql/sql/v1/sql_query.cpp @@ -162,48 +162,58 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& tableType = ETableType::ExternalTable; } + bool existingOk = false; + if (rule.HasBlock3()) { // IF NOT EXISTS + existingOk = true; + Y_DEBUG_ABORT_UNLESS( + rule.GetBlock3().GetToken1().GetId() == SQLv1LexerTokens::TOKEN_IF && + rule.GetBlock3().GetToken2().GetId() == SQLv1LexerTokens::TOKEN_NOT && + rule.GetBlock3().GetToken3().GetId() == SQLv1LexerTokens::TOKEN_EXISTS + ); + } + TTableRef tr; - if (!SimpleTableRefImpl(rule.GetRule_simple_table_ref3(), tr)) { + if (!SimpleTableRefImpl(rule.GetRule_simple_table_ref4(), tr)) { return false; } TCreateTableParameters params{.TableType=tableType}; - if (!CreateTableEntry(rule.GetRule_create_table_entry5(), params)) { + if (!CreateTableEntry(rule.GetRule_create_table_entry6(), params)) { return false; } - for (auto& block: rule.GetBlock6()) { + for (auto& block: rule.GetBlock7()) { if (!CreateTableEntry(block.GetRule_create_table_entry2(), params)) { return false; } } - if (rule.HasBlock9()) { - Context().Error(GetPos(rule.GetBlock9().GetRule_table_inherits1().GetToken1())) + if (rule.HasBlock10()) { + Context().Error(GetPos(rule.GetBlock10().GetRule_table_inherits1().GetToken1())) << "INHERITS clause is not supported yet"; return false; } - if (rule.HasBlock10()) { + if (rule.HasBlock11()) { if (tableType == ETableType::TableStore) { - Context().Error(GetPos(rule.GetBlock10().GetRule_table_partition_by1().GetToken1())) + Context().Error(GetPos(rule.GetBlock11().GetRule_table_partition_by1().GetToken1())) << "PARTITION BY is not supported for TABLESTORE"; return false; } - const auto list = rule.GetBlock10().GetRule_table_partition_by1().GetRule_pure_column_list4(); + const auto list = rule.GetBlock11().GetRule_table_partition_by1().GetRule_pure_column_list4(); params.PartitionByColumns.push_back(IdEx(list.GetRule_an_id2(), *this)); for (auto& node : list.GetBlock3()) { params.PartitionByColumns.push_back(IdEx(node.GetRule_an_id2(), *this)); } } - if (rule.HasBlock11()) { - if (!CreateTableSettings(rule.GetBlock11().GetRule_with_table_settings1(), params)) { + if (rule.HasBlock12()) { + if (!CreateTableSettings(rule.GetBlock12().GetRule_with_table_settings1(), params)) { return false; } } - if (rule.HasBlock12()) { - Context().Error(GetPos(rule.GetBlock12().GetRule_table_tablestore1().GetToken1())) + if (rule.HasBlock13()) { + Context().Error(GetPos(rule.GetBlock13().GetRule_table_tablestore1().GetToken1())) << "TABLESTORE clause is not supported yet"; return false; } @@ -212,7 +222,7 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& return false; } - AddStatementToBlocks(blocks, BuildCreateTable(Ctx.Pos(), tr, params, Ctx.Scoped)); + AddStatementToBlocks(blocks, BuildCreateTable(Ctx.Pos(), tr, existingOk, params, Ctx.Scoped)); break; } case TRule_sql_stmt_core::kAltSqlStmtCore5: { @@ -227,17 +237,21 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& tableType = ETableType::ExternalTable; } - if (rule.HasBlock3()) { - Context().Error(GetPos(rule.GetToken1())) << "IF EXISTS in " << humanStatementName - << " is not supported."; - return false; + bool missingOk = false; + if (rule.HasBlock3()) { // IF EXISTS + missingOk = true; + Y_DEBUG_ABORT_UNLESS( + rule.GetBlock3().GetToken1().GetId() == SQLv1LexerTokens::TOKEN_IF && + rule.GetBlock3().GetToken2().GetId() == SQLv1LexerTokens::TOKEN_EXISTS + ); } + TTableRef tr; if (!SimpleTableRefImpl(rule.GetRule_simple_table_ref4(), tr)) { return false; } - AddStatementToBlocks(blocks, BuildDropTable(Ctx.Pos(), tr, tableType, Ctx.Scoped)); + AddStatementToBlocks(blocks, BuildDropTable(Ctx.Pos(), tr, missingOk, tableType, Ctx.Scoped)); break; } case TRule_sql_stmt_core::kAltSqlStmtCore6: { @@ -583,7 +597,14 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& } const bool isUser = node.GetToken2().GetId() == SQLv1LexerTokens::TOKEN_USER; - const bool force = node.HasBlock3(); + bool missingOk = false; + if (node.HasBlock3()) { // IF EXISTS + missingOk = true; + Y_DEBUG_ABORT_UNLESS( + node.GetBlock3().GetToken1().GetId() == SQLv1LexerTokens::TOKEN_IF && + node.GetBlock3().GetToken2().GetId() == SQLv1LexerTokens::TOKEN_EXISTS + ); + } TVector<TDeferredAtom> roles; bool allowSystemRoles = true; @@ -599,30 +620,40 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& } } - AddStatementToBlocks(blocks, BuildDropRoles(pos, service, cluster, roles, isUser, force, Ctx.Scoped)); + AddStatementToBlocks(blocks, BuildDropRoles(pos, service, cluster, roles, isUser, missingOk, Ctx.Scoped)); break; } case TRule_sql_stmt_core::kAltSqlStmtCore26: { - // create_object_stmt: CREATE OBJECT name (TYPE type [WITH k=v,...]); + // create_object_stmt: CREATE OBJECT (IF NOT EXISTS)? name (TYPE type [WITH k=v,...]); auto& node = core.GetAlt_sql_stmt_core26().GetRule_create_object_stmt1(); TObjectOperatorContext context(Ctx.Scoped); - if (node.GetRule_object_ref3().HasBlock1()) { - if (!ClusterExpr(node.GetRule_object_ref3().GetBlock1().GetRule_cluster_expr1(), + if (node.GetRule_object_ref4().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref4().GetBlock1().GetRule_cluster_expr1(), false, context.ServiceId, context.Cluster)) { return false; } } - const TString& objectId = Id(node.GetRule_object_ref3().GetRule_id_or_at2(), *this).second; - const TString& typeId = Id(node.GetRule_object_type_ref6().GetRule_an_id_or_type1(), *this); + bool existingOk = false; + if (node.HasBlock3()) { // IF NOT EXISTS + existingOk = true; + Y_DEBUG_ABORT_UNLESS( + node.GetBlock3().GetToken1().GetId() == SQLv1LexerTokens::TOKEN_IF && + node.GetBlock3().GetToken2().GetId() == SQLv1LexerTokens::TOKEN_NOT && + node.GetBlock3().GetToken3().GetId() == SQLv1LexerTokens::TOKEN_EXISTS + ); + } + + const TString& objectId = Id(node.GetRule_object_ref4().GetRule_id_or_at2(), *this).second; + const TString& typeId = Id(node.GetRule_object_type_ref7().GetRule_an_id_or_type1(), *this); std::map<TString, TDeferredAtom> kv; - if (node.HasBlock8()) { - if (!ParseObjectFeatures(kv, node.GetBlock8().GetRule_create_object_features1().GetRule_object_features2())) { + if (node.HasBlock9()) { + if (!ParseObjectFeatures(kv, node.GetBlock9().GetRule_create_object_features1().GetRule_object_features2())) { return false; } } - AddStatementToBlocks(blocks, BuildCreateObjectOperation(Ctx.Pos(), objectId, typeId, std::move(kv), context)); + AddStatementToBlocks(blocks, BuildCreateObjectOperation(Ctx.Pos(), objectId, typeId, existingOk, std::move(kv), context)); break; } case TRule_sql_stmt_core::kAltSqlStmtCore27: { @@ -647,61 +678,89 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& break; } case TRule_sql_stmt_core::kAltSqlStmtCore28: { - // drop_object_stmt: DROP OBJECT name (TYPE type [WITH k=v,...]); + // drop_object_stmt: DROP OBJECT (IF EXISTS)? name (TYPE type [WITH k=v,...]); auto& node = core.GetAlt_sql_stmt_core28().GetRule_drop_object_stmt1(); TObjectOperatorContext context(Ctx.Scoped); - if (node.GetRule_object_ref3().HasBlock1()) { - if (!ClusterExpr(node.GetRule_object_ref3().GetBlock1().GetRule_cluster_expr1(), + if (node.GetRule_object_ref4().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref4().GetBlock1().GetRule_cluster_expr1(), false, context.ServiceId, context.Cluster)) { return false; } } - const TString& objectId = Id(node.GetRule_object_ref3().GetRule_id_or_at2(), *this).second; - const TString& typeId = Id(node.GetRule_object_type_ref6().GetRule_an_id_or_type1(), *this); + bool missingOk = false; + if (node.HasBlock3()) { // IF EXISTS + missingOk = true; + Y_DEBUG_ABORT_UNLESS( + node.GetBlock3().GetToken1().GetId() == SQLv1LexerTokens::TOKEN_IF && + node.GetBlock3().GetToken2().GetId() == SQLv1LexerTokens::TOKEN_EXISTS + ); + } + + const TString& objectId = Id(node.GetRule_object_ref4().GetRule_id_or_at2(), *this).second; + const TString& typeId = Id(node.GetRule_object_type_ref7().GetRule_an_id_or_type1(), *this); std::map<TString, TDeferredAtom> kv; - if (node.HasBlock8()) { - if (!ParseObjectFeatures(kv, node.GetBlock8().GetRule_drop_object_features1().GetRule_object_features2())) { + if (node.HasBlock9()) { + if (!ParseObjectFeatures(kv, node.GetBlock9().GetRule_drop_object_features1().GetRule_object_features2())) { return false; } } - AddStatementToBlocks(blocks, BuildDropObjectOperation(Ctx.Pos(), objectId, typeId, std::move(kv), context)); + AddStatementToBlocks(blocks, BuildDropObjectOperation(Ctx.Pos(), objectId, typeId, missingOk, std::move(kv), context)); break; } case TRule_sql_stmt_core::kAltSqlStmtCore29: { - // create_external_data_source_stmt: CREATE EXTERNAL DATA SOURCE name WITH (k=v,...); + // create_external_data_source_stmt: CREATE EXTERNAL DATA SOURCE (IF NOT EXISTS)? name WITH (k=v,...); auto& node = core.GetAlt_sql_stmt_core29().GetRule_create_external_data_source_stmt1(); TObjectOperatorContext context(Ctx.Scoped); - if (node.GetRule_object_ref5().HasBlock1()) { - if (!ClusterExpr(node.GetRule_object_ref5().GetBlock1().GetRule_cluster_expr1(), + if (node.GetRule_object_ref6().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref6().GetBlock1().GetRule_cluster_expr1(), false, context.ServiceId, context.Cluster)) { return false; } } - const TString& objectId = Id(node.GetRule_object_ref5().GetRule_id_or_at2(), *this).second; + bool existingOk = false; + if (node.HasBlock5()) { // IF NOT EXISTS + existingOk = true; + Y_DEBUG_ABORT_UNLESS( + node.GetBlock5().GetToken1().GetId() == SQLv1LexerTokens::TOKEN_IF && + node.GetBlock5().GetToken2().GetId() == SQLv1LexerTokens::TOKEN_NOT && + node.GetBlock5().GetToken3().GetId() == SQLv1LexerTokens::TOKEN_EXISTS + ); + } + + const TString& objectId = Id(node.GetRule_object_ref6().GetRule_id_or_at2(), *this).second; std::map<TString, TDeferredAtom> kv; - if (!ParseExternalDataSourceSettings(kv, node.GetRule_with_table_settings6())) { + if (!ParseExternalDataSourceSettings(kv, node.GetRule_with_table_settings7())) { return false; } - AddStatementToBlocks(blocks, BuildCreateObjectOperation(Ctx.Pos(), BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), objectId), "EXTERNAL_DATA_SOURCE", std::move(kv), context)); + AddStatementToBlocks(blocks, BuildCreateObjectOperation(Ctx.Pos(), BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), objectId), "EXTERNAL_DATA_SOURCE", existingOk, std::move(kv), context)); break; } case TRule_sql_stmt_core::kAltSqlStmtCore30: { - // drop_external_data_source_stmt: DROP EXTERNAL DATA SOURCE name; + // drop_external_data_source_stmt: DROP EXTERNAL DATA SOURCE (IF EXISTS)? name; auto& node = core.GetAlt_sql_stmt_core30().GetRule_drop_external_data_source_stmt1(); TObjectOperatorContext context(Ctx.Scoped); - if (node.GetRule_object_ref5().HasBlock1()) { - if (!ClusterExpr(node.GetRule_object_ref5().GetBlock1().GetRule_cluster_expr1(), + if (node.GetRule_object_ref6().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref6().GetBlock1().GetRule_cluster_expr1(), false, context.ServiceId, context.Cluster)) { return false; } } - const TString& objectId = Id(node.GetRule_object_ref5().GetRule_id_or_at2(), *this).second; - AddStatementToBlocks(blocks, BuildDropObjectOperation(Ctx.Pos(), BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), objectId), "EXTERNAL_DATA_SOURCE", {}, context)); + bool missingOk = false; + if (node.HasBlock5()) { // IF EXISTS + missingOk = true; + Y_DEBUG_ABORT_UNLESS( + node.GetBlock5().GetToken1().GetId() == SQLv1LexerTokens::TOKEN_IF && + node.GetBlock5().GetToken2().GetId() == SQLv1LexerTokens::TOKEN_EXISTS + ); + } + + const TString& objectId = Id(node.GetRule_object_ref6().GetRule_id_or_at2(), *this).second; + AddStatementToBlocks(blocks, BuildDropObjectOperation(Ctx.Pos(), BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), objectId), "EXTERNAL_DATA_SOURCE", missingOk, {}, context)); break; } case TRule_sql_stmt_core::kAltSqlStmtCore31: { @@ -973,6 +1032,7 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& BuildCreateObjectOperation(Ctx.Pos(), BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), objectId), TypeId, + false, std::move(features), context)); break; @@ -996,6 +1056,7 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& BuildDropObjectOperation(Ctx.Pos(), BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), objectId), TypeId, + false, {}, context)); break; diff --git a/ydb/library/yql/sql/v1/sql_ut.cpp b/ydb/library/yql/sql/v1/sql_ut.cpp index 6f25db71a3..9edfb7b8d6 100644 --- a/ydb/library/yql/sql/v1/sql_ut.cpp +++ b/ydb/library/yql/sql/v1/sql_ut.cpp @@ -670,6 +670,23 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]); } + Y_UNIT_TEST(CreateObjectIfNotExists) { + NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT IF NOT EXISTS secretId (TYPE SECRET) WITH (Key1=Value1, K2=V2);"); + UNIT_ASSERT(res.Root); + + TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) { + if (word == "Write") { + UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObjectIfNotExists")); + } + }; + + TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} }; + VerifyProgram(res, elementStat, verifyLine); + + UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); + UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]); + } + Y_UNIT_TEST(CreateObjectWithFeaturesStrings) { NYql::TAstParseResult res = SqlToYql("USE plato; CREATE OBJECT secretId (TYPE SECRET) WITH (Key1=\"Value1\", K2='V2', K3=V3, K4='', K5=`aaa`, K6='a\\'aa');"); UNIT_ASSERT(res.Root); @@ -834,6 +851,23 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]); } + Y_UNIT_TEST(DropObjectIfExists) { + NYql::TAstParseResult res = SqlToYql("USE plato; DROP OBJECT IF EXISTS secretId (TYPE SECRET);"); + UNIT_ASSERT(res.Root); + + TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) { + if (word == "Write") { + UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObjectIfExists")); + } + }; + + TWordCountHive elementStat = { {TString("Write"), 0}, {TString("SECRET"), 0} }; + VerifyProgram(res, elementStat, verifyLine); + + UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); + UNIT_ASSERT_VALUES_EQUAL(1, elementStat["SECRET"]); + } + Y_UNIT_TEST(PrimaryKeyParseCorrect) { NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE tableName (Key Uint32, Subkey Int64, Value String, PRIMARY KEY (Key, Subkey));"); UNIT_ASSERT(res.Root); @@ -956,6 +990,23 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]); } + Y_UNIT_TEST(CreateTableWithIfNotExists) { + NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE IF NOT EXISTS t (a int32, primary key(a));"); + UNIT_ASSERT(res.Root); + + TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) { + if (word == "Write!") { + UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, + line.find(R"__((Write! world sink (Key '('tablescheme (String '"t"))) (Void) '('('mode 'create_if_not_exists) '('columns '('('"a" (AsOptionalType (DataType 'Int32)) '('columnConstrains '()) '()))) '('primarykey '('"a")))))__")); + } + }; + + TWordCountHive elementStat = {{TString("Write!"), 0}}; + VerifyProgram(res, elementStat, verifyLine); + + UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write!"]); + } + Y_UNIT_TEST(CreateTableDuplicatedPkColumnsFail) { NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32 not null, primary key(a, a));"); UNIT_ASSERT(!res.Root); @@ -3435,8 +3486,18 @@ Y_UNIT_TEST_SUITE(SqlToYQLErrors) { Y_UNIT_TEST(DropTableWithIfExists) { NYql::TAstParseResult res = SqlToYql("DROP TABLE IF EXISTS plato.foo;"); - UNIT_ASSERT(!res.Root); - UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:0: Error: IF EXISTS in DROP TABLE is not supported.\n"); + UNIT_ASSERT(res.Root); + + TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) { + if (word == "Write") { + UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("drop_if_exists")); + } + }; + + TWordCountHive elementStat = { {TString("Write"), 0}}; + VerifyProgram(res, elementStat, verifyLine); + + UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); } Y_UNIT_TEST(TooManyErrors) { @@ -5468,6 +5529,30 @@ Y_UNIT_TEST_SUITE(ExternalDataSource) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); } + Y_UNIT_TEST(CreateExternalDataSourceIfNotExists) { + NYql::TAstParseResult res = SqlToYql(R"( + USE plato; + CREATE EXTERNAL DATA SOURCE IF NOT EXISTS MyDataSource WITH ( + SOURCE_TYPE="ObjectStorage", + LOCATION="my-bucket", + AUTH_METHOD="NONE" + ); + )"); + UNIT_ASSERT(res.Root); + + TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) { + if (word == "Write") { + UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('"auth_method" '"NONE") '('"location" '"my-bucket") '('"source_type" '"ObjectStorage"))#"); + UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("createObjectIfNotExists")); + } + }; + + TWordCountHive elementStat = { {TString("Write"), 0} }; + VerifyProgram(res, elementStat, verifyLine); + + UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); + } + Y_UNIT_TEST(CreateExternalDataSourceWithBadArguments) { ExpectFailWithError(R"( USE plato; @@ -5672,6 +5757,26 @@ Y_UNIT_TEST_SUITE(ExternalDataSource) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); } + + Y_UNIT_TEST(DropExternalDataSourceIfExists) { + NYql::TAstParseResult res = SqlToYql(R"( + USE plato; + DROP EXTERNAL DATA SOURCE IF EXISTS MyDataSource; + )"); + UNIT_ASSERT(res.Root); + + TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) { + if (word == "Write") { + UNIT_ASSERT_STRING_CONTAINS(line, "MyDataSource"); + UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("dropObjectIfExists")); + } + }; + + TWordCountHive elementStat = { {TString("Write"), 0}}; + VerifyProgram(res, elementStat, verifyLine); + + UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); + } } Y_UNIT_TEST_SUITE(ExternalTable) { @@ -5754,6 +5859,31 @@ Y_UNIT_TEST_SUITE(ExternalTable) { UNIT_ASSERT_C(res.IsOk(), res.Issues.ToString()); } + Y_UNIT_TEST(CreateExternalTableIfNotExists) { + NYql::TAstParseResult res = SqlToYql(R"( + USE plato; + CREATE EXTERNAL TABLE IF NOT EXISTS mytable ( + a int + ) WITH ( + DATA_SOURCE="/Root/mydatasource", + LOCATION="/folder1/*" + ); + )"); + UNIT_ASSERT_C(res.Root, res.Issues.ToOneLineString()); + + TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) { + if (word == "Write") { + UNIT_ASSERT_STRING_CONTAINS(line, R"#('('('data_source_path (String '"/Root/mydatasource")) '('location (String '"/folder1/*")))) '('tableType 'externalTable)))))#"); + UNIT_ASSERT_STRING_CONTAINS(line, "create_if_not_exists"); + } + }; + + TWordCountHive elementStat = { {TString("Write"), 0} }; + VerifyProgram(res, elementStat, verifyLine); + + UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); + } + Y_UNIT_TEST(CreateExternalTableWithBadArguments) { ExpectFailWithError(R"( USE plato; @@ -5836,6 +5966,26 @@ Y_UNIT_TEST_SUITE(ExternalTable) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); } + + Y_UNIT_TEST(DropExternalTableIfExists) { + NYql::TAstParseResult res = SqlToYql(R"( + USE plato; + DROP EXTERNAL TABLE IF EXISTS MyExternalTable; + )"); + UNIT_ASSERT(res.Root); + + TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) { + if (word == "Write") { + UNIT_ASSERT_VALUES_EQUAL(TString::npos, line.find("tablescheme")); + UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("drop_if_exists")); + } + }; + + TWordCountHive elementStat = { {TString("Write"), 0}}; + VerifyProgram(res, elementStat, verifyLine); + + UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); + } } Y_UNIT_TEST_SUITE(TopicsDDL) { diff --git a/ydb/services/metadata/abstract/parsing.h b/ydb/services/metadata/abstract/parsing.h index 891a7c96ef..49c882a2ab 100644 --- a/ydb/services/metadata/abstract/parsing.h +++ b/ydb/services/metadata/abstract/parsing.h @@ -4,10 +4,17 @@ #include <ydb/library/yql/core/expr_nodes/yql_expr_nodes.h> #include <util/generic/string.h> +#include <util/generic/typetraits.h> #include <map> #include <optional> namespace NYql { + +namespace NObjectOptionsParsing { +Y_HAS_MEMBER(ExistingOk); // for create +Y_HAS_MEMBER(MissingOk); // for drop +} // namespace NObjectOptionsParsing + class TObjectSettingsImpl { public: using TFeaturesExtractor = NYql::TFeaturesExtractor; @@ -15,6 +22,8 @@ private: using TFeatures = THashMap<TString, TString>; YDB_READONLY_DEF(TString, TypeId); YDB_READONLY_DEF(TString, ObjectId); + YDB_READONLY_DEF(bool, ExistingOk); // for create + YDB_READONLY_DEF(bool, MissingOk); // for drop TFeatures Features; std::shared_ptr<TFeaturesExtractor> FeaturesExtractor; public: @@ -36,6 +45,12 @@ public: bool DeserializeFromKi(const TKiObject& data) { ObjectId = data.ObjectId(); TypeId = data.TypeId(); + if constexpr (NObjectOptionsParsing::THasExistingOk<TKiObject>::value) { + ExistingOk = (data.ExistingOk().Value() == "1"); + } + if constexpr (NObjectOptionsParsing::THasMissingOk<TKiObject>::value) { + MissingOk = (data.MissingOk().Value() == "1"); + } for (auto&& i : data.Features()) { if (auto maybeAtom = i.template Maybe<NYql::NNodes::TCoAtom>()) { Features.emplace(maybeAtom.Cast().StringValue(), ""); @@ -51,8 +66,8 @@ public: } }; -using TUpsertObjectSettings = TObjectSettingsImpl; using TCreateObjectSettings = TObjectSettingsImpl; +using TUpsertObjectSettings = TObjectSettingsImpl; using TAlterObjectSettings = TObjectSettingsImpl; using TDropObjectSettings = TObjectSettingsImpl; diff --git a/ydb/services/metadata/abstract/ya.make b/ydb/services/metadata/abstract/ya.make index 80fc76559a..1058cebaa7 100644 --- a/ydb/services/metadata/abstract/ya.make +++ b/ydb/services/metadata/abstract/ya.make @@ -14,12 +14,12 @@ SRCS( GENERATE_ENUM_SERIALIZATION(kqp_common.h) PEERDIR( + ydb/core/base ydb/library/accessor ydb/library/actors/core - ydb/services/metadata/request - ydb/public/api/protos - ydb/core/base ydb/library/yql/core/expr_nodes + ydb/public/api/protos + ydb/services/metadata/request ) END() |