Skip to content

Commit ec72c59

Browse files
authored
Show pre-delete tables in SHOW TABLES and reject modifications on them (#18770)
1 parent 5a9c6b9 commit ec72c59

16 files changed

Lines changed: 424 additions & 21 deletions

File tree

‎iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ public enum TSStatusCode {
9999
COLUMN_ALREADY_EXISTS(552),
100100
TABLE_IS_LOST(553),
101101
TABLE_INCOMPATIBLE(554),
102+
TABLE_IN_PRE_DELETE(555),
102103
ONLY_LOGICAL_VIEW(560),
103104

104105
// Storage Engine

‎iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/schema/ClusterSchemaManager.java‎

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.iotdb.common.rpc.thrift.TSStatus;
2525
import org.apache.iotdb.commons.conf.CommonDescriptor;
2626
import org.apache.iotdb.commons.exception.MetadataException;
27+
import org.apache.iotdb.commons.exception.table.TableInDeletionException;
2728
import org.apache.iotdb.commons.path.PartialPath;
2829
import org.apache.iotdb.commons.path.PathPatternTree;
2930
import org.apache.iotdb.commons.schema.SchemaConstant;
@@ -1540,13 +1541,26 @@ public Optional<Pair<TsTable, TableNodeStatus>> getTableAndStatusIfExists(
15401541
return clusterSchemaInfo.getTsTableIfExists(database, tableName);
15411542
}
15421543

1544+
public Optional<TsTable> getTableWithUsingStatusIfExists(
1545+
final String database, final String tableName) throws MetadataException {
1546+
final Optional<Pair<TsTable, TableNodeStatus>> tableAndStatus =
1547+
getTableAndStatusIfExists(database, tableName);
1548+
if (tableAndStatus.isEmpty()) {
1549+
return Optional.empty();
1550+
}
1551+
if (TableNodeStatus.PRE_DELETE == tableAndStatus.get().getRight()) {
1552+
throw new TableInDeletionException(database, tableName);
1553+
}
1554+
return Optional.of(tableAndStatus.get().getLeft());
1555+
}
1556+
15431557
public synchronized Pair<TSStatus, TsTable> tableColumnCheckForColumnExtension(
15441558
final String database,
15451559
final String tableName,
15461560
final List<TsTableColumnSchema> columnSchemaList,
15471561
final boolean isTableView)
15481562
throws MetadataException {
1549-
final TsTable originalTable = getTableIfExists(database, tableName).orElse(null);
1563+
final TsTable originalTable = getTableWithUsingStatusIfExists(database, tableName).orElse(null);
15501564

15511565
if (Objects.isNull(originalTable)) {
15521566
return new Pair<>(
@@ -1601,7 +1615,7 @@ public synchronized Pair<TSStatus, TsTable> tableColumnCheckForColumnAltering(
16011615
final TSDataType dataType,
16021616
final boolean isGeneratedByPipe)
16031617
throws MetadataException {
1604-
final TsTable originalTable = getTableIfExists(database, tableName).orElse(null);
1618+
final TsTable originalTable = getTableWithUsingStatusIfExists(database, tableName).orElse(null);
16051619

16061620
if (Objects.isNull(originalTable)) {
16071621
return new Pair<>(
@@ -1638,7 +1652,7 @@ public synchronized Pair<TSStatus, TsTable> tableColumnCheckForColumnRenaming(
16381652
final String newName,
16391653
final boolean isTableView)
16401654
throws MetadataException {
1641-
final TsTable originalTable = getTableIfExists(database, tableName).orElse(null);
1655+
final TsTable originalTable = getTableWithUsingStatusIfExists(database, tableName).orElse(null);
16421656

16431657
if (Objects.isNull(originalTable)) {
16441658
return new Pair<>(
@@ -1691,7 +1705,7 @@ public synchronized Pair<TSStatus, TsTable> tableCheckForRenaming(
16911705
final String newName,
16921706
final boolean isTableView)
16931707
throws MetadataException {
1694-
final TsTable originalTable = getTableIfExists(database, tableName).orElse(null);
1708+
final TsTable originalTable = getTableWithUsingStatusIfExists(database, tableName).orElse(null);
16951709

16961710
if (Objects.isNull(originalTable)) {
16971711
return new Pair<>(
@@ -1776,7 +1790,7 @@ public synchronized Pair<TSStatus, TsTable> updateTableProperties(
17761790
final Map<String, String> updatedProperties,
17771791
final boolean isTableView)
17781792
throws MetadataException {
1779-
final TsTable originalTable = getTableIfExists(database, tableName).orElse(null);
1793+
final TsTable originalTable = getTableWithUsingStatusIfExists(database, tableName).orElse(null);
17801794

17811795
if (Objects.isNull(originalTable)) {
17821796
return new Pair<>(

‎iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/ClusterSchemaInfo.java‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,9 +1352,11 @@ public ShowTableResp showTables(final ShowTablePlan plan) {
13521352
})
13531353
.collect(Collectors.toList())
13541354
: tableModelMTree
1355-
.getAllUsingTablesUnderSpecificDatabase(
1355+
.getAllTablesUnderSpecificDatabase(
13561356
getQualifiedDatabasePartialPath(plan.getDatabase()))
13571357
.stream()
1358+
.filter(pair -> pair.getRight() != TableNodeStatus.PRE_CREATE)
1359+
.map(Pair::getLeft)
13581360
.map(
13591361
tsTable ->
13601362
new TTableInfo(
@@ -1447,7 +1449,7 @@ public DescTableResp descTable(final DescTablePlan plan) {
14471449
}
14481450
return new DescTableResp(
14491451
StatusUtils.OK,
1450-
tableModelMTree.getUsingTableSchema(databasePath, plan.getTableName()),
1452+
tableModelMTree.getTableSchemaForDesc(databasePath, plan.getTableName()),
14511453
null,
14521454
null);
14531455
} catch (final MetadataException e) {

‎iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/ConfigMTree.java‎

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.apache.iotdb.commons.exception.SemanticException;
2828
import org.apache.iotdb.commons.exception.table.ColumnNotExistsException;
2929
import org.apache.iotdb.commons.exception.table.TableAlreadyExistsException;
30+
import org.apache.iotdb.commons.exception.table.TableInDeletionException;
3031
import org.apache.iotdb.commons.exception.table.TableNotExistsException;
3132
import org.apache.iotdb.commons.path.PartialPath;
3233
import org.apache.iotdb.commons.path.PathPatternTree;
@@ -796,7 +797,7 @@ public void setTableComment(
796797
final String comment,
797798
final boolean isView)
798799
throws MetadataException {
799-
final TsTable table = getTable(database, tableName);
800+
final TsTable table = getTableWithUsingStatus(database, tableName).getTable();
800801
final Optional<Pair<TSStatus, TsTable>> check =
801802
ClusterSchemaManager.checkTable4View(database.getTailNode(), table, isView);
802803
if (check.isPresent()) {
@@ -816,9 +817,8 @@ public void setTableColumnComment(
816817
final @Nonnull String columnName,
817818
final @Nullable String comment)
818819
throws MetadataException {
819-
final TsTable table = getTable(database, tableName);
820-
821-
final TsTableColumnSchema columnSchema = table.getColumnSchema(columnName);
820+
final TsTableColumnSchema columnSchema =
821+
getTableWithUsingStatus(database, tableName).getTable().getColumnSchema(columnName);
822822

823823
if (Objects.isNull(columnSchema)) {
824824
throw new ColumnNotExistsException(
@@ -988,14 +988,13 @@ public boolean preDeleteColumn(
988988
final String columnName,
989989
final boolean isView)
990990
throws MetadataException, SemanticException {
991-
final ConfigTableNode node = getTableNode(database, tableName);
991+
final ConfigTableNode node = getTableWithUsingStatus(database, tableName);
992992
final Optional<Pair<TSStatus, TsTable>> check =
993993
ClusterSchemaManager.checkTable4View(database.getTailNode(), node.getTable(), isView);
994994
if (check.isPresent()) {
995995
throw new SemanticException(
996996
check.get().getLeft().getMessage(), check.get().getLeft().getCode());
997997
}
998-
999998
final TsTableColumnSchema columnSchema = node.getTable().getColumnSchema(columnName);
1000999
if (Objects.isNull(columnSchema)) {
10011000
throw new ColumnNotExistsException(
@@ -1024,7 +1023,7 @@ public void commitDeleteColumn(
10241023
public void preAlterColumnDataType(
10251024
PartialPath database, String tableName, String columnName, TSDataType dataType)
10261025
throws MetadataException {
1027-
final ConfigTableNode node = getTableNode(database, tableName);
1026+
final ConfigTableNode node = getTableWithUsingStatus(database, tableName);
10281027
final TsTableColumnSchema columnSchema = node.getTable().getColumnSchema(columnName);
10291028

10301029
if (Objects.isNull(columnSchema)) {
@@ -1089,6 +1088,36 @@ public TsTable getUsingTableSchema(final PartialPath database, final String tabl
10891088
return newTable;
10901089
}
10911090

1091+
/**
1092+
* The schema to report in DESC. Unlike {@link #getUsingTableSchema}, a column whose deletion is
1093+
* still pending is kept, so that the user can see which columns exist and are only waiting for
1094+
* the pending procedure. A pending data type change is applied on top.
1095+
*/
1096+
public TsTable getTableSchemaForDesc(final PartialPath database, final String tableName)
1097+
throws MetadataException {
1098+
final ConfigTableNode node = getTableNode(database, tableName);
1099+
if (node.getPreAlteredColumns().isEmpty()) {
1100+
return node.getTable();
1101+
}
1102+
final TsTable table = new TsTable(node.getTable());
1103+
node.getPreAlteredColumns()
1104+
.forEach(
1105+
(columnName, dataType) -> {
1106+
final TsTableColumnSchema columnSchema = table.getColumnSchema(columnName);
1107+
if (columnSchema == null) {
1108+
return;
1109+
}
1110+
columnSchema.setDataType(dataType);
1111+
if (columnSchema instanceof FieldColumnSchema) {
1112+
final FieldColumnSchema fieldColumnSchema = (FieldColumnSchema) columnSchema;
1113+
fieldColumnSchema.setEncoding(
1114+
SchemaUtils.getDataTypeCompatibleEncoding(
1115+
dataType, fieldColumnSchema.getEncoding()));
1116+
}
1117+
});
1118+
return table;
1119+
}
1120+
10921121
public TableSchemaDetails getTableSchemaDetails(
10931122
final PartialPath database, final String tableName) throws MetadataException {
10941123
final ConfigTableNode node = getTableNode(database, tableName);
@@ -1130,6 +1159,19 @@ private ConfigTableNode getTableNode(final PartialPath database, final String ta
11301159
return ((ConfigTableNode) databaseNode.getChild(tableName));
11311160
}
11321161

1162+
/**
1163+
* A table in the pre-delete status is about to be dropped, pre-create status is a temporary
1164+
* status, ignore it.
1165+
*/
1166+
private ConfigTableNode getTableWithUsingStatus(
1167+
final PartialPath database, final String tableName) throws MetadataException {
1168+
final ConfigTableNode tableNode = getTableNode(database, tableName);
1169+
if (tableNode.getStatus() == TableNodeStatus.PRE_DELETE) {
1170+
throw new TableInDeletionException(database.getFullPath(), tableName);
1171+
}
1172+
return tableNode;
1173+
}
1174+
11331175
// endregion
11341176

11351177
// region Serialization and Deserialization

‎iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/table/DeleteDevicesProcedure.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ private void checkTableExistence(final ConfigNodeProcedureEnv env) {
148148
try {
149149
if (!env.getConfigManager()
150150
.getClusterSchemaManager()
151-
.getTableIfExists(database, tableName)
151+
.getTableWithUsingStatusIfExists(database, tableName)
152152
.isPresent()) {
153153
setFailure(
154154
new ProcedureException(

‎iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/ClusterSchemaManagerTest.java‎

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,27 @@
1818
*/
1919
package org.apache.iotdb.confignode.manager;
2020

21+
import org.apache.iotdb.commons.exception.table.TableInDeletionException;
2122
import org.apache.iotdb.commons.schema.table.TsTable;
2223
import org.apache.iotdb.commons.schema.table.TsTableInternalRPCUtil;
24+
import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType;
25+
import org.apache.iotdb.confignode.consensus.request.write.database.DatabaseSchemaPlan;
26+
import org.apache.iotdb.confignode.consensus.request.write.table.CommitCreateTablePlan;
27+
import org.apache.iotdb.confignode.consensus.request.write.table.PreCreateTablePlan;
28+
import org.apache.iotdb.confignode.consensus.request.write.table.PreDeleteTablePlan;
2329
import org.apache.iotdb.confignode.manager.schema.ClusterSchemaManager;
2430
import org.apache.iotdb.confignode.manager.schema.ClusterSchemaQuotaStatistics;
2531
import org.apache.iotdb.confignode.persistence.schema.ClusterSchemaInfo;
2632
import org.apache.iotdb.confignode.rpc.thrift.TDatabaseSchema;
33+
import org.apache.iotdb.rpc.TSStatusCode;
2734

35+
import org.apache.tsfile.enums.TSDataType;
2836
import org.apache.tsfile.utils.Pair;
2937
import org.junit.Assert;
3038
import org.junit.Test;
3139
import org.mockito.Mockito;
3240

41+
import java.util.ArrayList;
3342
import java.util.Collections;
3443
import java.util.HashMap;
3544
import java.util.List;
@@ -89,4 +98,84 @@ public void testGetAllTableInfoForDataNodeActivationWithDeletedDatabase() {
8998
Assert.assertEquals(Collections.singleton("test"), tableInfo.right.keySet());
9099
Assert.assertTrue(tableInfo.right.get("test").isEmpty());
91100
}
101+
102+
@Test
103+
public void testGetTableWithUsingStatusIfExists() throws Exception {
104+
final String database = "root.pre_delete_manager_test";
105+
final String table = "table1";
106+
final ClusterSchemaInfo clusterSchemaInfo = new ClusterSchemaInfo();
107+
final ClusterSchemaManager clusterSchemaManager = managerOf(clusterSchemaInfo);
108+
clusterSchemaInfo.createDatabase(
109+
new DatabaseSchemaPlan(
110+
ConfigPhysicalPlanType.CreateDatabase,
111+
new TDatabaseSchema(database).setIsTableModel(true)));
112+
113+
// A missing table yields an empty result instead of an exception.
114+
Assert.assertFalse(
115+
clusterSchemaManager.getTableWithUsingStatusIfExists(database, table).isPresent());
116+
117+
clusterSchemaInfo.preCreateTable(new PreCreateTablePlan(database, new TsTable(table)));
118+
clusterSchemaInfo.commitCreateTable(new CommitCreateTablePlan(database, table));
119+
120+
// A table in the using status is returned.
121+
Assert.assertTrue(
122+
clusterSchemaManager.getTableWithUsingStatusIfExists(database, table).isPresent());
123+
124+
clusterSchemaInfo.preDeleteTable(new PreDeleteTablePlan(database, table));
125+
126+
// A table in the pre-delete status is rejected with the dedicated status code.
127+
final TableInDeletionException exception =
128+
Assert.assertThrows(
129+
TableInDeletionException.class,
130+
() -> clusterSchemaManager.getTableWithUsingStatusIfExists(database, table));
131+
Assert.assertEquals(TSStatusCode.TABLE_IN_PRE_DELETE.getStatusCode(), exception.getErrorCode());
132+
}
133+
134+
@Test
135+
public void testTableChecksRejectTableInPreDelete() throws Exception {
136+
final String database = "root.pre_delete_manager_test";
137+
final String table = "table1";
138+
final ClusterSchemaInfo clusterSchemaInfo = new ClusterSchemaInfo();
139+
final ClusterSchemaManager clusterSchemaManager = managerOf(clusterSchemaInfo);
140+
clusterSchemaInfo.createDatabase(
141+
new DatabaseSchemaPlan(
142+
ConfigPhysicalPlanType.CreateDatabase,
143+
new TDatabaseSchema(database).setIsTableModel(true)));
144+
clusterSchemaInfo.preCreateTable(new PreCreateTablePlan(database, new TsTable(table)));
145+
clusterSchemaInfo.commitCreateTable(new CommitCreateTablePlan(database, table));
146+
clusterSchemaInfo.preDeleteTable(new PreDeleteTablePlan(database, table));
147+
148+
// Every check that guards a table procedure must reject the table, so that no procedure keeps
149+
// modifying a table that is being deleted.
150+
Assert.assertThrows(
151+
TableInDeletionException.class,
152+
() ->
153+
clusterSchemaManager.tableColumnCheckForColumnExtension(
154+
database, table, new ArrayList<>(), false));
155+
Assert.assertThrows(
156+
TableInDeletionException.class,
157+
() ->
158+
clusterSchemaManager.tableColumnCheckForColumnAltering(
159+
database, table, "field", TSDataType.INT32, false));
160+
Assert.assertThrows(
161+
TableInDeletionException.class,
162+
() ->
163+
clusterSchemaManager.tableColumnCheckForColumnRenaming(
164+
database, table, "field", "field2", false));
165+
Assert.assertThrows(
166+
TableInDeletionException.class,
167+
() -> clusterSchemaManager.tableCheckForRenaming(database, table, "table2", false));
168+
Assert.assertThrows(
169+
TableInDeletionException.class,
170+
() ->
171+
clusterSchemaManager.updateTableProperties(
172+
database, table, new HashMap<>(), new HashMap<>(), false));
173+
}
174+
175+
private static ClusterSchemaManager managerOf(final ClusterSchemaInfo clusterSchemaInfo) {
176+
return new ClusterSchemaManager(
177+
Mockito.mock(IManager.class),
178+
clusterSchemaInfo,
179+
Mockito.mock(ClusterSchemaQuotaStatistics.class));
180+
}
92181
}

0 commit comments

Comments
 (0)