aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVasily Gerasimov <UgnineSirdis@ydb.tech>2023-12-27 23:36:23 +0300
committerGitHub <noreply@github.com>2023-12-27 23:36:23 +0300
commit86a332affe89d8d8a629fa838adece9abdba183c (patch)
treef60d464425d08117b11cab707927ee27297b3757
parent909ffbad0e7d2269062b8a159827a7cc8c9aa74d (diff)
downloadydb-86a332affe89d8d8a629fa838adece9abdba183c.tar.gz
Support IF NOT EXISTS/IF EXISTS for tables, external tables and external data sources (#694)
-rw-r--r--ydb/core/kqp/gateway/behaviour/external_data_source/manager.cpp24
-rw-r--r--ydb/core/kqp/gateway/behaviour/external_data_source/manager.h8
-rw-r--r--ydb/core/kqp/gateway/kqp_ic_gateway.cpp11
-rw-r--r--ydb/core/kqp/host/kqp_gateway_proxy.cpp15
-rw-r--r--ydb/core/kqp/provider/yql_kikimr_datasink.cpp20
-rw-r--r--ydb/core/kqp/provider/yql_kikimr_exec.cpp20
-rw-r--r--ydb/core/kqp/provider/yql_kikimr_expr_nodes.json12
-rw-r--r--ydb/core/kqp/provider/yql_kikimr_gateway.h8
-rw-r--r--ydb/core/kqp/provider/yql_kikimr_gateway_ut.cpp4
-rw-r--r--ydb/core/kqp/provider/yql_kikimr_type_ann.cpp34
-rw-r--r--ydb/core/kqp/ut/federated_query/s3/kqp_federated_scheme_ut.cpp16
-rw-r--r--ydb/core/kqp/ut/service/kqp_qs_queries_ut.cpp127
-rw-r--r--ydb/core/kqp/ut/service/ya.make1
-rw-r--r--ydb/library/yql/sql/v1/SQLv1.g.in10
-rw-r--r--ydb/library/yql/sql/v1/format/sql_format.cpp31
-rw-r--r--ydb/library/yql/sql/v1/format/sql_format_ut.cpp69
-rw-r--r--ydb/library/yql/sql/v1/node.h6
-rw-r--r--ydb/library/yql/sql/v1/object_processing.h15
-rw-r--r--ydb/library/yql/sql/v1/query.cpp54
-rw-r--r--ydb/library/yql/sql/v1/source.h4
-rw-r--r--ydb/library/yql/sql/v1/sql_query.cpp155
-rw-r--r--ydb/library/yql/sql/v1/sql_ut.cpp154
-rw-r--r--ydb/services/metadata/abstract/parsing.h17
-rw-r--r--ydb/services/metadata/abstract/ya.make6
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()