Skip to content

Commit 8fb0b46

Browse files
authored
Fixed the bug that the view from tree can be written
1 parent 8b18a87 commit 8fb0b46

7 files changed

Lines changed: 73 additions & 29 deletions

File tree

integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,18 @@ public void testTreeViewTable() throws Exception {
875875

876876
statement.execute(
877877
"create or replace view tree_table (tag1 tag, tag2 tag, S1 int32 field, s3 from s2) as root.a.**");
878+
879+
// Cannot be written
880+
try {
881+
statement.execute(
882+
"insert into tree_table(time, tag1, tag2, S1, s3) values (1, 1, 1, 1, 1)");
883+
fail();
884+
} catch (final SQLException e) {
885+
assertEquals(
886+
"701: The table tree_view_db.tree_table is a view from tree, cannot be written or deleted from",
887+
e.getMessage());
888+
}
889+
878890
statement.execute("alter view tree_table rename to view_table");
879891
statement.execute("alter view view_table rename column s1 to s11");
880892
statement.execute("alter view view_table set properties ttl=100");

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowsStatement;
5252
import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertTabletStatement;
5353
import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache;
54+
import org.apache.iotdb.db.schemaengine.table.DataNodeTreeViewSchemaUtils;
5455
import org.apache.iotdb.db.storageengine.dataregion.modification.DeletionPredicate;
5556
import org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate;
5657
import org.apache.iotdb.db.storageengine.dataregion.modification.IDPredicate.And;
@@ -350,6 +351,7 @@ private static void validateSchema(final Delete node, final MPPQueryContext quer
350351
throw new SemanticException("Table " + tableName + " not found");
351352
}
352353

354+
DataNodeTreeViewSchemaUtils.checkTableInWrite(databaseName, table);
353355
// Maybe set by pipe transfer
354356
if (Objects.isNull(node.getTableDeletionEntries())) {
355357
node.setTableDeletionEntries(
@@ -480,7 +482,7 @@ private static IDPredicate parseIsNull(
480482
throw new SemanticException("Left hand expression is not an identifier: " + leftHandExp);
481483
}
482484
String columnName = ((Identifier) leftHandExp).getValue();
483-
int idColumnOrdinal = table.getIdColumnOrdinal(columnName);
485+
int idColumnOrdinal = table.getTagColumnOrdinal(columnName);
484486
if (idColumnOrdinal == -1) {
485487
throw new SemanticException(
486488
"The column '" + columnName + "' does not exist or is not a tag column");
@@ -551,7 +553,7 @@ private static IDPredicate parseComparison(
551553
}
552554
// id predicate
553555
String columnName = identifier.getValue();
554-
int idColumnOrdinal = table.getIdColumnOrdinal(columnName);
556+
int idColumnOrdinal = table.getTagColumnOrdinal(columnName);
555557
if (idColumnOrdinal == -1) {
556558
throw new SemanticException(
557559
"The column '" + columnName + "' does not exist or is not a tag column");

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@
188188
import org.apache.iotdb.db.queryengine.plan.statement.component.FillPolicy;
189189
import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertBaseStatement;
190190
import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache;
191+
import org.apache.iotdb.db.schemaengine.table.DataNodeTreeViewSchemaUtils;
191192
import org.apache.iotdb.rpc.RpcUtils;
192193
import org.apache.iotdb.rpc.TSStatusCode;
193194
import org.apache.iotdb.udf.api.exception.UDFException;
@@ -490,6 +491,7 @@ protected Scope visitUpdate(final Update node, final Optional<Scope> context) {
490491
final TranslationMap translationMap = analyzeTraverseDevice(node, context, true);
491492
final TsTable table =
492493
DataNodeTableCache.getInstance().getTable(node.getDatabase(), node.getTableName());
494+
DataNodeTreeViewSchemaUtils.checkTableInWrite(node.getDatabase(), table);
493495
if (!node.parseRawExpression(
494496
null,
495497
table,
@@ -551,6 +553,7 @@ protected Scope visitDeleteDevice(final DeleteDevice node, final Optional<Scope>
551553
if (Objects.isNull(table)) {
552554
TableMetadataImpl.throwTableNotExistsException(node.getDatabase(), node.getTableName());
553555
}
556+
DataNodeTreeViewSchemaUtils.checkTableInWrite(node.getDatabase(), table);
554557
node.parseModEntries(table);
555558
analyzeTraverseDevice(node, context, node.getWhere().isPresent());
556559
node.parseRawExpression(

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -793,11 +793,11 @@ public Optional<TableSchema> validateTableHeaderSchema(
793793
TableSchema tableSchema,
794794
MPPQueryContext context,
795795
boolean allowCreateTable,
796-
boolean isStrictIdColumn)
796+
boolean isStrictTagColumn)
797797
throws LoadAnalyzeTableColumnDisorderException {
798798
return TableHeaderSchemaValidator.getInstance()
799799
.validateTableHeaderSchema(
800-
database, tableSchema, context, allowCreateTable, isStrictIdColumn);
800+
database, tableSchema, context, allowCreateTable, isStrictTagColumn);
801801
}
802802

803803
@Override

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/TableHeaderSchemaValidator.java

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl;
4747
import org.apache.iotdb.db.queryengine.plan.relational.type.InternalTypeManager;
4848
import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache;
49+
import org.apache.iotdb.db.schemaengine.table.DataNodeTreeViewSchemaUtils;
4950
import org.apache.iotdb.rpc.TSStatusCode;
5051

5152
import com.google.common.util.concurrent.ListenableFuture;
@@ -94,7 +95,7 @@ public Optional<TableSchema> validateTableHeaderSchema(
9495
final TableSchema tableSchema,
9596
final MPPQueryContext context,
9697
final boolean allowCreateTable,
97-
final boolean isStrictIdColumn)
98+
final boolean isStrictTagColumn)
9899
throws LoadAnalyzeTableColumnDisorderException {
99100
// The schema cache R/W and fetch operation must be locked together thus the cache clean
100101
// operation executed by delete timeSeries will be effective.
@@ -130,39 +131,41 @@ public Optional<TableSchema> validateTableHeaderSchema(
130131
TableMetadataImpl.throwTableNotExistsException(database, tableSchema.getTableName());
131132
}
132133
} else {
133-
// If table with this name already exists and isStrictIdColumn is true, make sure the existing
134+
DataNodeTreeViewSchemaUtils.checkTableInWrite(database, table);
135+
// If table with this name already exists and isStrictTagColumn is true, make sure the
136+
// existing
134137
// id columns are the prefix of the incoming id columns, or vice versa
135-
if (isStrictIdColumn) {
136-
final List<TsTableColumnSchema> realIdColumns = table.getIdColumnSchemaList();
137-
final List<ColumnSchema> incomingIdColumns = tableSchema.getIdColumns();
138-
if (realIdColumns.size() <= incomingIdColumns.size()) {
138+
if (isStrictTagColumn) {
139+
final List<TsTableColumnSchema> realTagColumns = table.getTagColumnSchemaList();
140+
final List<ColumnSchema> incomingTagColumns = tableSchema.getIdColumns();
141+
if (realTagColumns.size() <= incomingTagColumns.size()) {
139142
// When incoming table has more ID columns, the existing id columns
140143
// should be the prefix of the incoming id columns (or equal)
141-
for (int indexReal = 0; indexReal < realIdColumns.size(); indexReal++) {
142-
final String idName = realIdColumns.get(indexReal).getColumnName();
143-
final int indexIncoming = tableSchema.getIndexAmongIdColumns(idName);
144+
for (int indexReal = 0; indexReal < realTagColumns.size(); indexReal++) {
145+
final String tagName = realTagColumns.get(indexReal).getColumnName();
146+
final int indexIncoming = tableSchema.getIndexAmongIdColumns(tagName);
144147
if (indexIncoming != indexReal) {
145148
throw new LoadAnalyzeTableColumnDisorderException(
146149
String.format(
147150
"Can not create table because incoming table has no less id columns than existing table, "
148151
+ "and the existing id columns are not the prefix of the incoming id columns. "
149152
+ "Existing id column: %s, index in existing table: %s, index in incoming table: %s",
150-
idName, indexReal, indexIncoming));
153+
tagName, indexReal, indexIncoming));
151154
}
152155
}
153156
} else {
154157
// When existing table has more ID columns, the incoming id columns
155158
// should be the prefix of the existing id columns
156-
for (int indexIncoming = 0; indexIncoming < incomingIdColumns.size(); indexIncoming++) {
157-
final String idName = incomingIdColumns.get(indexIncoming).getName();
158-
final int indexReal = table.getIdColumnOrdinal(idName);
159+
for (int indexIncoming = 0; indexIncoming < incomingTagColumns.size(); indexIncoming++) {
160+
final String tagName = incomingTagColumns.get(indexIncoming).getName();
161+
final int indexReal = table.getTagColumnOrdinal(tagName);
159162
if (indexReal != indexIncoming) {
160163
throw new LoadAnalyzeTableColumnDisorderException(
161164
String.format(
162165
"Can not create table because existing table has more id columns than incoming table, "
163166
+ "and the incoming id columns are not the prefix of the existing id columns. "
164167
+ "Incoming id column: %s, index in existing table: %s, index in incoming table: %s",
165-
idName, indexReal, indexIncoming));
168+
tagName, indexReal, indexIncoming));
166169
}
167170
}
168171
}

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/table/DataNodeTreeViewSchemaUtils.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@
1919

2020
package org.apache.iotdb.db.schemaengine.table;
2121

22+
import org.apache.iotdb.commons.exception.IoTDBException;
2223
import org.apache.iotdb.commons.path.PartialPath;
2324
import org.apache.iotdb.commons.schema.table.TsTable;
25+
import org.apache.iotdb.db.exception.sql.SemanticException;
2426
import org.apache.iotdb.db.storageengine.dataregion.memtable.DeviceIDFactory;
27+
import org.apache.iotdb.rpc.TSStatusCode;
2528

2629
import org.apache.tsfile.file.metadata.IDeviceID;
2730
import org.apache.tsfile.file.metadata.StringArrayDeviceID;
@@ -30,10 +33,27 @@
3033
import java.util.StringJoiner;
3134
import java.util.stream.Stream;
3235

36+
import static org.apache.iotdb.commons.schema.table.TreeViewSchema.TREE_PATH_PATTERN;
3337
import static org.apache.iotdb.commons.schema.table.TreeViewSchema.getPrefixPattern;
3438

3539
public class DataNodeTreeViewSchemaUtils {
3640

41+
public static void checkTableInWrite(final String database, final TsTable table) {
42+
if (isTreeViewTable(table)) {
43+
throw new SemanticException(
44+
new IoTDBException(
45+
String.format(
46+
"The table %s.%s is a view from tree, cannot be written or deleted from",
47+
database, table.getTableName()),
48+
TSStatusCode.SEMANTIC_ERROR.getStatusCode()));
49+
}
50+
}
51+
52+
// For better performance
53+
public static boolean isTreeViewTable(final TsTable table) {
54+
return table.containsPropWithoutLock(TREE_PATH_PATTERN);
55+
}
56+
3757
public static String[] getPatternNodes(final TsTable table) {
3858
final PartialPath path = getPrefixPattern(table);
3959
return Arrays.copyOf(path.getNodes(), path.getNodeLength() - 1);

iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,15 @@ public class TsTable {
6868
private String tableName;
6969

7070
private final Map<String, TsTableColumnSchema> columnSchemaMap = new LinkedHashMap<>();
71-
private final Map<String, Integer> idColumnIndexMap = new HashMap<>();
71+
private final Map<String, Integer> tagColumnIndexMap = new HashMap<>();
7272

7373
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
7474

7575
private Map<String, String> props = null;
7676

7777
// Cache, avoid string parsing
7878
private transient long ttlValue = Long.MIN_VALUE;
79-
private transient int idNums = 0;
79+
private transient int tagNums = 0;
8080
private transient int fieldNum = 0;
8181

8282
public TsTable(final String tableName) {
@@ -104,25 +104,25 @@ public TsTableColumnSchema getColumnSchema(final String columnName) {
104104
}
105105
}
106106

107-
public int getIdColumnOrdinal(final String columnName) {
107+
public int getTagColumnOrdinal(final String columnName) {
108108
readWriteLock.readLock().lock();
109109
try {
110-
return idColumnIndexMap.getOrDefault(columnName.toLowerCase(), -1);
110+
return tagColumnIndexMap.getOrDefault(columnName.toLowerCase(), -1);
111111
} finally {
112112
readWriteLock.readLock().unlock();
113113
}
114114
}
115115

116-
public List<TsTableColumnSchema> getIdColumnSchemaList() {
116+
public List<TsTableColumnSchema> getTagColumnSchemaList() {
117117
readWriteLock.readLock().lock();
118118
try {
119-
final List<TsTableColumnSchema> idColumnSchemaList = new ArrayList<>();
119+
final List<TsTableColumnSchema> tagColumnSchemaList = new ArrayList<>();
120120
for (final TsTableColumnSchema columnSchema : columnSchemaMap.values()) {
121121
if (TsTableColumnCategory.TAG.equals(columnSchema.getColumnCategory())) {
122-
idColumnSchemaList.add(columnSchema);
122+
tagColumnSchemaList.add(columnSchema);
123123
}
124124
}
125-
return idColumnSchemaList;
125+
return tagColumnSchemaList;
126126
} finally {
127127
readWriteLock.readLock().unlock();
128128
}
@@ -143,8 +143,8 @@ public void addColumnSchema(final TsTableColumnSchema columnSchema) {
143143
try {
144144
columnSchemaMap.put(columnSchema.getColumnName(), columnSchema);
145145
if (columnSchema.getColumnCategory().equals(TsTableColumnCategory.TAG)) {
146-
idNums++;
147-
idColumnIndexMap.put(columnSchema.getColumnName(), idNums - 1);
146+
tagNums++;
147+
tagColumnIndexMap.put(columnSchema.getColumnName(), tagNums - 1);
148148
} else if (columnSchema.getColumnCategory().equals(TsTableColumnCategory.FIELD)) {
149149
fieldNum++;
150150
}
@@ -221,7 +221,7 @@ public int getColumnNum() {
221221
public int getTagNum() {
222222
readWriteLock.readLock().lock();
223223
try {
224-
return idNums;
224+
return tagNums;
225225
} finally {
226226
readWriteLock.readLock().unlock();
227227
}
@@ -273,6 +273,10 @@ public Map<String, String> getProps() {
273273
}
274274
}
275275

276+
public boolean containsPropWithoutLock(final String propKey) {
277+
return props != null && props.containsKey(propKey);
278+
}
279+
276280
public Optional<String> getPropValue(final String propKey) {
277281
readWriteLock.readLock().lock();
278282
try {

0 commit comments

Comments
 (0)