|
14 | 14 | package com.facebook.presto.hive.metastore.file;
|
15 | 15 |
|
16 | 16 | import com.facebook.airlift.json.JsonCodec;
|
| 17 | +import com.facebook.airlift.log.Logger; |
17 | 18 | import com.facebook.presto.common.predicate.Domain;
|
18 | 19 | import com.facebook.presto.common.type.Type;
|
19 | 20 | import com.facebook.presto.hive.HdfsContext;
|
|
63 | 64 | import org.apache.hadoop.fs.FSDataInputStream;
|
64 | 65 | import org.apache.hadoop.fs.FileStatus;
|
65 | 66 | import org.apache.hadoop.fs.FileSystem;
|
| 67 | +import org.apache.hadoop.fs.FileUtil; |
66 | 68 | import org.apache.hadoop.fs.Path;
|
67 | 69 |
|
68 | 70 | import javax.annotation.concurrent.ThreadSafe;
|
|
99 | 101 | import static com.facebook.presto.hive.metastore.MetastoreUtil.getHiveBasicStatistics;
|
100 | 102 | import static com.facebook.presto.hive.metastore.MetastoreUtil.getPartitionNamesWithEmptyVersion;
|
101 | 103 | import static com.facebook.presto.hive.metastore.MetastoreUtil.isIcebergTable;
|
102 |
| -import static com.facebook.presto.hive.metastore.MetastoreUtil.isIcebergView; |
103 | 104 | import static com.facebook.presto.hive.metastore.MetastoreUtil.makePartName;
|
104 | 105 | import static com.facebook.presto.hive.metastore.MetastoreUtil.toPartitionValues;
|
105 | 106 | import static com.facebook.presto.hive.metastore.MetastoreUtil.updateStatisticsParameters;
|
|
117 | 118 | import static com.google.common.base.MoreObjects.toStringHelper;
|
118 | 119 | import static com.google.common.base.Preconditions.checkArgument;
|
119 | 120 | import static com.google.common.collect.ImmutableList.toImmutableList;
|
| 121 | +import static java.lang.String.format; |
120 | 122 | import static java.util.Collections.emptyList;
|
121 | 123 | import static java.util.Objects.requireNonNull;
|
122 | 124 | import static java.util.UUID.randomUUID;
|
|
128 | 130 | public class FileHiveMetastore
|
129 | 131 | implements ExtendedHiveMetastore
|
130 | 132 | {
|
| 133 | + private static final Logger LOG = Logger.get(FileHiveMetastore.class); |
131 | 134 | private static final String PUBLIC_ROLE_NAME = "public";
|
132 | 135 | private static final String ADMIN_ROLE_NAME = "admin";
|
133 | 136 | private static final String PRESTO_SCHEMA_FILE_NAME = ".prestoSchema";
|
@@ -208,9 +211,8 @@ public synchronized void renameDatabase(MetastoreContext metastoreContext, Strin
|
208 | 211 | verifyDatabaseNotExists(metastoreContext, newDatabaseName);
|
209 | 212 |
|
210 | 213 | try {
|
211 |
| - if (!metadataFileSystem.rename(getDatabaseMetadataDirectory(databaseName), getDatabaseMetadataDirectory(newDatabaseName))) { |
212 |
| - throw new PrestoException(HIVE_METASTORE_ERROR, "Could not rename database metadata directory"); |
213 |
| - } |
| 214 | + renamePath(getDatabaseMetadataDirectory(databaseName), getDatabaseMetadataDirectory(newDatabaseName), |
| 215 | + "Could not rename database metadata directory"); |
214 | 216 | }
|
215 | 217 | catch (IOException e) {
|
216 | 218 | throw new PrestoException(HIVE_METASTORE_ERROR, e);
|
@@ -514,22 +516,85 @@ public synchronized MetastoreOperationResult renameTable(MetastoreContext metast
|
514 | 516 | requireNonNull(newTableName, "newTableName is null");
|
515 | 517 | Table table = getRequiredTable(metastoreContext, databaseName, tableName);
|
516 | 518 | getRequiredDatabase(metastoreContext, newDatabaseName);
|
517 |
| - if (isIcebergTable(table) && !isIcebergView(table)) { |
518 |
| - throw new PrestoException(NOT_SUPPORTED, "Rename not supported for Iceberg tables"); |
519 |
| - } |
520 | 519 | // verify new table does not exist
|
521 | 520 | verifyTableNotExists(metastoreContext, newDatabaseName, newTableName);
|
522 | 521 |
|
| 522 | + Path metadataDirectory = getTableMetadataDirectory(databaseName, tableName); |
| 523 | + Path newMetadataDirectory = getTableMetadataDirectory(newDatabaseName, newTableName); |
| 524 | + |
| 525 | + if (isIcebergTable(table)) { |
| 526 | + renameIcebergTable(metadataDirectory, newMetadataDirectory); |
| 527 | + } |
| 528 | + else { |
| 529 | + renameTable(metadataDirectory, newMetadataDirectory); |
| 530 | + } |
| 531 | + |
| 532 | + return EMPTY_RESULT; |
| 533 | + } |
| 534 | + |
| 535 | + private void renameIcebergTable(Path originalMetadataDirectory, Path newMetadataDirectory) |
| 536 | + { |
| 537 | + Optional<Runnable> rollbackAction = Optional.empty(); |
523 | 538 | try {
|
524 |
| - if (!metadataFileSystem.rename(getTableMetadataDirectory(databaseName, tableName), getTableMetadataDirectory(newDatabaseName, newTableName))) { |
525 |
| - throw new PrestoException(HIVE_METASTORE_ERROR, "Could not rename table directory"); |
| 539 | + // If the directory `.prestoPermissions` exists, copy it to the new table metadata directory |
| 540 | + Path originTablePermissionDir = new Path(originalMetadataDirectory, PRESTO_PERMISSIONS_DIRECTORY_NAME); |
| 541 | + Path newTablePermissionDir = new Path(newMetadataDirectory, PRESTO_PERMISSIONS_DIRECTORY_NAME); |
| 542 | + if (metadataFileSystem.exists(originTablePermissionDir)) { |
| 543 | + if (!FileUtil.copy(metadataFileSystem, originTablePermissionDir, |
| 544 | + metadataFileSystem, newTablePermissionDir, false, metadataFileSystem.getConf())) { |
| 545 | + throw new IOException(format("Could not rename table. Failed to copy directory: %s to %s", originTablePermissionDir, newTablePermissionDir)); |
| 546 | + } |
| 547 | + else { |
| 548 | + rollbackAction = Optional.of(() -> { |
| 549 | + try { |
| 550 | + metadataFileSystem.delete(newTablePermissionDir, true); |
| 551 | + } |
| 552 | + catch (IOException e) { |
| 553 | + LOG.warn("Could not delete table permission directory: %s", newTablePermissionDir); |
| 554 | + } |
| 555 | + }); |
| 556 | + } |
| 557 | + } |
| 558 | + |
| 559 | + // Rename file `.prestoSchema` to change it to the new metadata path |
| 560 | + // This will atomically execute the table renaming behavior |
| 561 | + Path originMetadataFile = new Path(originalMetadataDirectory, PRESTO_SCHEMA_FILE_NAME); |
| 562 | + Path newMetadataFile = new Path(newMetadataDirectory, PRESTO_SCHEMA_FILE_NAME); |
| 563 | + renamePath(originMetadataFile, newMetadataFile, |
| 564 | + format("Could not rename table. Failed to rename file %s to %s", originMetadataFile, newMetadataFile)); |
| 565 | + |
| 566 | + // Subsequent action, delete the redundant directory `.prestoPermissions` from the original table metadata path |
| 567 | + try { |
| 568 | + metadataFileSystem.delete(new Path(originalMetadataDirectory, PRESTO_PERMISSIONS_DIRECTORY_NAME), true); |
| 569 | + } |
| 570 | + catch (IOException e) { |
| 571 | + // ignore |
526 | 572 | }
|
527 | 573 | }
|
528 | 574 | catch (IOException e) {
|
| 575 | + // If table renaming fails and rollback action has already been recorded, perform the rollback action to clean up junk files |
| 576 | + rollbackAction.ifPresent(Runnable::run); |
529 | 577 | throw new PrestoException(HIVE_METASTORE_ERROR, e);
|
530 | 578 | }
|
| 579 | + } |
531 | 580 |
|
532 |
| - return EMPTY_RESULT; |
| 581 | + private void renameTable(Path originalMetadataDirectory, Path newMetadataDirectory) |
| 582 | + { |
| 583 | + try { |
| 584 | + renamePath(originalMetadataDirectory, newMetadataDirectory, |
| 585 | + format("Could not rename table. Failed to rename directory %s to %s", originalMetadataDirectory, newMetadataDirectory)); |
| 586 | + } |
| 587 | + catch (IOException e) { |
| 588 | + throw new PrestoException(HIVE_METASTORE_ERROR, e); |
| 589 | + } |
| 590 | + } |
| 591 | + |
| 592 | + private void renamePath(Path originalPath, Path targetPath, String errorMessage) |
| 593 | + throws IOException |
| 594 | + { |
| 595 | + if (!metadataFileSystem.rename(originalPath, targetPath)) { |
| 596 | + throw new IOException(errorMessage); |
| 597 | + } |
533 | 598 | }
|
534 | 599 |
|
535 | 600 | @Override
|
|
0 commit comments