@@ -69,6 +69,14 @@ TString ReadAsyncReplicationQuery(const TFsPath& fsDirPath, const TLog* log) {
69
69
return ReadFromFile (fsDirPath, log, NFiles::CreateAsyncReplication ());
70
70
}
71
71
72
+ TString ReadExternalDataSourceQuery (const TFsPath& fsDirPath, const TLog* log) {
73
+ return ReadFromFile (fsDirPath, log, NFiles::CreateExternalDataSource ());
74
+ }
75
+
76
+ TString ReadExternalTableQuery (const TFsPath& fsDirPath, const TLog* log) {
77
+ return ReadFromFile (fsDirPath, log, NFiles::CreateExternalTable ());
78
+ }
79
+
72
80
template <typename TProtoType>
73
81
TProtoType ReadProtoFromFile (const TFsPath& fsDirPath, const TLog* log, const NFiles::TFileInfo& fileInfo) {
74
82
TProtoType proto;
@@ -340,6 +348,16 @@ TRestoreResult TRestoreClient::RetryViewRestoration() {
340
348
return result;
341
349
}
342
350
351
+ TRestoreResult TRestoreClient::RestoreExternalTables () {
352
+ for (const auto & [fsPath, dbPath, settings, isAlreadyExisting] : ExternalTableRestorationCalls) {
353
+ auto result = RestoreExternalTable (fsPath, dbPath, settings, isAlreadyExisting);
354
+ if (!result.IsSuccess ()) {
355
+ return result;
356
+ }
357
+ }
358
+ return Result<TRestoreResult>();
359
+ }
360
+
343
361
TRestoreResult TRestoreClient::Restore (const TString& fsPath, const TString& dbPath, const TRestoreSettings& settings) {
344
362
LOG_I (" Restore " << fsPath.Quote () << " to " << dbPath.Quote ());
345
363
@@ -376,8 +394,11 @@ TRestoreResult TRestoreClient::Restore(const TString& fsPath, const TString& dbP
376
394
377
395
// restore
378
396
auto restoreResult = RestoreFolder (fsPath, dbPath, " " , settings, oldEntries);
379
- if (auto retryViewResult = RetryViewRestoration (); !retryViewResult.IsSuccess ()) {
380
- restoreResult = retryViewResult;
397
+ if (auto result = RetryViewRestoration (); !result.IsSuccess ()) {
398
+ restoreResult = result;
399
+ }
400
+ if (auto result = RestoreExternalTables (); !result.IsSuccess ()) {
401
+ restoreResult = result;
381
402
}
382
403
383
404
if (restoreResult.IsSuccess ()) {
@@ -399,6 +420,8 @@ TRestoreResult TRestoreClient::Restore(const TString& fsPath, const TString& dbP
399
420
return restoreResult;
400
421
}
401
422
423
+ TVector<const TSchemeEntry*> entriesToDropInSecondPass;
424
+
402
425
for (const auto & entry : newDirectoryList.Entries ) {
403
426
if (oldEntries.contains (entry.Name )) {
404
427
continue ;
@@ -419,7 +442,7 @@ TRestoreResult TRestoreClient::Restore(const TString& fsPath, const TString& dbP
419
442
break ;
420
443
case ESchemeEntryType::View:
421
444
result = QueryClient.RetryQuerySync ([&path = fullPath](NQuery::TSession session) {
422
- return session.ExecuteQuery (std::format (" DROP VIEW IF EXISTS `{}`;" , path),
445
+ return session.ExecuteQuery (std::format (" DROP VIEW `{}`;" , path),
423
446
NQuery::TTxControl::NoTx ()).ExtractValueSync ();
424
447
});
425
448
break ;
@@ -433,6 +456,15 @@ TRestoreResult TRestoreClient::Restore(const TString& fsPath, const TString& dbP
433
456
return client.DropNode (path).ExtractValueSync ();
434
457
});
435
458
break ;
459
+ case ESchemeEntryType::ExternalDataSource:
460
+ entriesToDropInSecondPass.emplace_back (&entry);
461
+ continue ;
462
+ case ESchemeEntryType::ExternalTable:
463
+ result = QueryClient.RetryQuerySync ([&path = fullPath](NQuery::TSession session) {
464
+ return session.ExecuteQuery (std::format (" DROP EXTERNAL TABLE `{}`;" , path),
465
+ NQuery::TTxControl::NoTx ()).ExtractValueSync ();
466
+ });
467
+ break ;
436
468
default :
437
469
break ;
438
470
}
@@ -442,7 +474,27 @@ TRestoreResult TRestoreClient::Restore(const TString& fsPath, const TString& dbP
442
474
return restoreResult;
443
475
} else if (!result->IsSuccess ()) {
444
476
LOG_E (" Error removing " << entry.Type << " : " << TString{fullPath}.Quote ()
445
- << " : " << result->GetIssues ().ToOneLineString ());
477
+ << " , issues: " << result->GetIssues ().ToOneLineString ());
478
+ return restoreResult;
479
+ }
480
+ }
481
+
482
+ for (const auto * entry : entriesToDropInSecondPass) {
483
+ TMaybe<TStatus> result;
484
+ switch (entry->Type ) {
485
+ case ESchemeEntryType::ExternalDataSource:
486
+ result = QueryClient.RetryQuerySync ([&path = entry->Name ](NQuery::TSession session) {
487
+ return session.ExecuteQuery (std::format (" DROP EXTERNAL DATA SOURCE `{}`;" , path),
488
+ NQuery::TTxControl::NoTx ()).ExtractValueSync ();
489
+ });
490
+ break ;
491
+ default :
492
+ break ;
493
+ }
494
+ Y_ENSURE (result, " Unexpected entry to drop in the second pass" );
495
+ if (!result->IsSuccess ()) {
496
+ LOG_E (" Error removing " << entry->Type << " : " << TString{entry->Name }.Quote ()
497
+ << " , issues: " << result->GetIssues ().ToOneLineString ());
446
498
return restoreResult;
447
499
}
448
500
}
@@ -476,7 +528,7 @@ TRestoreResult TRestoreClient::RestoreClusterRoot(const TFsPath& fsPath) {
476
528
}
477
529
478
530
LOG_I (" Restore cluster root " << ClusterRootPath.Quote () << " from " << fsPath.GetPath ().Quote ());
479
-
531
+
480
532
if (!fsPath.Exists ()) {
481
533
return Result<TRestoreResult>(EStatus::BAD_REQUEST,
482
534
TStringBuilder () << " Specified folder does not exist: " << fsPath.GetPath ());
@@ -508,7 +560,7 @@ TRestoreResult TRestoreClient::RestoreClusterRoot(const TFsPath& fsPath) {
508
560
if (auto result = RestoreGroupMembers (rootTableClient, fsPath, ClusterRootPath); !result.IsSuccess ()) {
509
561
return result;
510
562
}
511
-
563
+
512
564
if (auto result = RestorePermissionsImpl (rootSchemeClient, fsPath, ClusterRootPath); !result.IsSuccess ()) {
513
565
return result;
514
566
}
@@ -521,7 +573,7 @@ TRestoreResult TRestoreClient::WaitForAvailableNodes(const TString& database, TD
521
573
dbDriverConfig.SetDatabase (database);
522
574
523
575
THPTimer timer;
524
-
576
+
525
577
NDiscovery::TDiscoveryClient client (dbDriverConfig);
526
578
TDuration retrySleep = TDuration::MilliSeconds (1000 );
527
579
while (true ) {
@@ -561,13 +613,13 @@ TRestoreResult TRestoreClient::RestoreUsers(TTableClient& client, const TFsPath&
561
613
auto statementResult = client.RetryOperationSync ([&](TSession session) {
562
614
return session.ExecuteSchemeQuery (statement).ExtractValueSync ();
563
615
});
564
-
616
+
565
617
if (statement.StartsWith (" CREATE" )
566
618
&& statementResult.GetStatus () == EStatus::PRECONDITION_FAILED
567
619
&& statementResult.GetIssues ().ToOneLineString ().find (" exists" ) != TString::npos)
568
620
{
569
621
LOG_D (" User from create statement " << statement.Quote () << " already exists, trying to alter it" );
570
- auto alterStatement = " ALTER" + statement.substr (6 );
622
+ auto alterStatement = " ALTER" + statement.substr (6 );
571
623
auto alterStatementResult = client.RetryOperationSync ([&](TSession session) {
572
624
return session.ExecuteSchemeQuery (alterStatement).ExtractValueSync ();
573
625
});
@@ -658,7 +710,7 @@ TRestoreResult TRestoreClient::ReplaceClusterRoot(TString& outPath) {
658
710
if (clusterRootEnd != std::string::npos) {
659
711
outPath = ClusterRootPath + outPath.substr (clusterRootEnd);
660
712
} else {
661
- return Result<TRestoreResult>(EStatus::INTERNAL_ERROR,
713
+ return Result<TRestoreResult>(EStatus::INTERNAL_ERROR,
662
714
TStringBuilder () << " Can't find cluster root path in "
663
715
<< outPath.Quote () << " to replace it on "
664
716
<< ClusterRootPath.Quote ());
@@ -686,7 +738,7 @@ TRestoreResult TRestoreClient::RestoreDatabaseImpl(const TString& fsPath, const
686
738
}
687
739
688
740
LOG_I (" Restore database from " << fsPath.Quote () << " to " << dbPath.Quote ());
689
-
741
+
690
742
if (auto result = CreateDatabase (CmsClient, dbPath, TCreateDatabaseSettings (dbDesc)); !result.IsSuccess ()) {
691
743
if (result.GetStatus () == EStatus::ALREADY_EXISTS) {
692
744
LOG_W (" Database " << dbPath.Quote () << " already exists, continue restoring to this database" );
@@ -723,8 +775,11 @@ TRestoreResult TRestoreClient::RestoreDatabaseImpl(const TString& fsPath, const
723
775
724
776
if (settings.WithContent_ ) {
725
777
auto restoreResult = RestoreFolder (fsPath, dbPath, " " , {}, {});
726
- if (auto retryViewResult = RetryViewRestoration (); !retryViewResult.IsSuccess ()) {
727
- restoreResult = retryViewResult;
778
+ if (auto result = RetryViewRestoration (); !result.IsSuccess ()) {
779
+ restoreResult = result;
780
+ }
781
+ if (auto result = RestoreExternalTables (); !result.IsSuccess ()) {
782
+ restoreResult = result;
728
783
}
729
784
return restoreResult;
730
785
} else {
@@ -761,6 +816,7 @@ TRestoreResult TRestoreClient::RestoreDatabases(const TFsPath& fsPath, const TRe
761
816
if (IsFileExists (fsPath.Child (NFiles::Database ().FileName ))) {
762
817
TRestoreDatabaseSettings dbSettings = {
763
818
.WaitNodesDuration_ = settings.WaitNodesDuration_ ,
819
+ .Database_ = std::nullopt,
764
820
.WithContent_ = false
765
821
};
766
822
@@ -866,6 +922,16 @@ TRestoreResult TRestoreClient::RestoreFolder(
866
922
return RestoreReplication (fsPath, dbRestoreRoot, dbPathRelativeToRestoreRoot, settings, oldEntries.contains (objectDbPath));
867
923
}
868
924
925
+ if (IsFileExists (fsPath.Child (NFiles::CreateExternalDataSource ().FileName ))) {
926
+ return RestoreExternalDataSource (fsPath, objectDbPath, settings, oldEntries.contains (objectDbPath));
927
+ }
928
+
929
+ if (IsFileExists (fsPath.Child (NFiles::CreateExternalTable ().FileName ))) {
930
+ // delay external table restoration
931
+ ExternalTableRestorationCalls.emplace_back (fsPath, objectDbPath, settings, oldEntries.contains (objectDbPath));
932
+ return Result<TRestoreResult>();
933
+ }
934
+
869
935
if (IsFileExists (fsPath.Child (NFiles::Empty ().FileName ))) {
870
936
return RestoreEmptyDir (fsPath, objectDbPath, settings, oldEntries.contains (objectDbPath));
871
937
}
@@ -889,6 +955,11 @@ TRestoreResult TRestoreClient::RestoreFolder(
889
955
result = RestoreCoordinationNode (child, childDbPath, settings, oldEntries.contains (childDbPath));
890
956
} else if (IsFileExists (child.Child (NFiles::CreateAsyncReplication ().FileName ))) {
891
957
result = RestoreReplication (child, dbRestoreRoot, Join (' /' , dbPathRelativeToRestoreRoot, child.GetName ()), settings, oldEntries.contains (childDbPath));
958
+ } else if (IsFileExists (child.Child (NFiles::CreateExternalDataSource ().FileName ))) {
959
+ result = RestoreExternalDataSource (child, childDbPath, settings, oldEntries.contains (childDbPath));
960
+ } else if (IsFileExists (child.Child (NFiles::CreateExternalTable ().FileName ))) {
961
+ // delay external table restoration
962
+ ExternalTableRestorationCalls.emplace_back (child, childDbPath, settings, oldEntries.contains (childDbPath));
892
963
} else if (child.IsDirectory ()) {
893
964
result = RestoreFolder (child, dbRestoreRoot, Join (' /' , dbPathRelativeToRestoreRoot, child.GetName ()), settings, oldEntries);
894
965
}
@@ -900,7 +971,7 @@ TRestoreResult TRestoreClient::RestoreFolder(
900
971
901
972
const bool dbPathExists = oldEntries.contains (dbPath);
902
973
if (!result.Defined () && !dbPathExists) {
903
- // This situation occurs when all the children of the folder are views.
974
+ // This situation occurs when all the children of the folder are views or external tables .
904
975
return RestoreEmptyDir (fsPath, dbPath, settings, dbPathExists);
905
976
}
906
977
@@ -1118,6 +1189,82 @@ TRestoreResult TRestoreClient::RestoreCoordinationNode(
1118
1189
return Result<TRestoreResult>(dbPath, std::move (result));
1119
1190
}
1120
1191
1192
+ TRestoreResult TRestoreClient::RestoreExternalDataSource (
1193
+ const TFsPath& fsPath,
1194
+ const TString& dbPath,
1195
+ const TRestoreSettings& settings,
1196
+ bool isAlreadyExisting)
1197
+ {
1198
+ LOG_D (" Process " << fsPath.GetPath ().Quote ());
1199
+
1200
+ if (auto error = ErrorOnIncomplete (fsPath)) {
1201
+ return *error;
1202
+ }
1203
+
1204
+ LOG_I (" Restore external data source " << fsPath.GetPath ().Quote () << " to " << dbPath.Quote ());
1205
+
1206
+ if (settings.DryRun_ ) {
1207
+ return CheckExistenceAndType (SchemeClient, dbPath, ESchemeEntryType::ExternalDataSource);
1208
+ }
1209
+
1210
+ TString query = ReadExternalDataSourceQuery (fsPath, Log.get ());
1211
+
1212
+ NYql::TIssues issues;
1213
+ if (!RewriteCreateQuery (query, " CREATE EXTERNAL DATA SOURCE IF NOT EXISTS `{}`" , dbPath, issues)) {
1214
+ return Result<TRestoreResult>(fsPath.GetPath (), EStatus::BAD_REQUEST, issues.ToString ());
1215
+ }
1216
+
1217
+ auto result = QueryClient.RetryQuerySync ([&](NQuery::TSession session) {
1218
+ return session.ExecuteQuery (query, NQuery::TTxControl::NoTx ()).ExtractValueSync ();
1219
+ });
1220
+
1221
+ if (result.IsSuccess ()) {
1222
+ LOG_D (" Created " << dbPath.Quote ());
1223
+ return RestorePermissions (fsPath, dbPath, settings, isAlreadyExisting);
1224
+ }
1225
+
1226
+ LOG_E (" Failed to create " << dbPath.Quote ());
1227
+ return Result<TRestoreResult>(dbPath, std::move (result));
1228
+ }
1229
+
1230
+ TRestoreResult TRestoreClient::RestoreExternalTable (
1231
+ const TFsPath& fsPath,
1232
+ const TString& dbPath,
1233
+ const TRestoreSettings& settings,
1234
+ bool isAlreadyExisting)
1235
+ {
1236
+ LOG_D (" Process " << fsPath.GetPath ().Quote ());
1237
+
1238
+ if (auto error = ErrorOnIncomplete (fsPath)) {
1239
+ return *error;
1240
+ }
1241
+
1242
+ LOG_I (" Restore external table " << fsPath.GetPath ().Quote () << " to " << dbPath.Quote ());
1243
+
1244
+ if (settings.DryRun_ ) {
1245
+ return CheckExistenceAndType (SchemeClient, dbPath, ESchemeEntryType::ExternalTable);
1246
+ }
1247
+
1248
+ TString query = ReadExternalTableQuery (fsPath, Log.get ());
1249
+
1250
+ NYql::TIssues issues;
1251
+ if (!RewriteCreateQuery (query, " CREATE EXTERNAL TABLE IF NOT EXISTS `{}`" , dbPath, issues)) {
1252
+ return Result<TRestoreResult>(fsPath.GetPath (), EStatus::BAD_REQUEST, issues.ToString ());
1253
+ }
1254
+
1255
+ auto result = QueryClient.RetryQuerySync ([&](NQuery::TSession session) {
1256
+ return session.ExecuteQuery (query, NQuery::TTxControl::NoTx ()).ExtractValueSync ();
1257
+ });
1258
+
1259
+ if (result.IsSuccess ()) {
1260
+ LOG_D (" Created " << dbPath.Quote ());
1261
+ return RestorePermissions (fsPath, dbPath, settings, isAlreadyExisting);
1262
+ }
1263
+
1264
+ LOG_E (" Failed to create " << dbPath.Quote ());
1265
+ return Result<TRestoreResult>(dbPath, std::move (result));
1266
+ }
1267
+
1121
1268
TRestoreResult TRestoreClient::RestoreTable (
1122
1269
const TFsPath& fsPath,
1123
1270
const TString& dbPath,
@@ -1544,7 +1691,7 @@ TRestoreResult TRestoreClient::RestorePermissionsImpl(
1544
1691
if (result.GetStatus () == EStatus::UNAUTHORIZED) {
1545
1692
LOG_W (" Not enough rights to restore permissions on " << dbPath.Quote () << " , skipping" );
1546
1693
return Result<TRestoreResult>();
1547
- }
1694
+ }
1548
1695
1549
1696
return result;
1550
1697
}
0 commit comments