[feat](Nereids) support show constraint command (#29667)
show constraints from t1; +------+-------------+-----------------------------------------+ | Name | Type | Definition | +------+-------------+-----------------------------------------+ | fk | FOREIGN KEY | FOREIGN KEY (id) REFERENCES cir.t1 (id) | | uk | UNIQUE | UNIQUE (id) | | pk | PRIMARY KEY | PRIMARY KEY (id) | +------+-------------+-----------------------------------------+
This commit is contained in:
@ -164,6 +164,7 @@ CONNECTION: 'CONNECTION';
|
||||
CONNECTION_ID: 'CONNECTION_ID';
|
||||
CONSISTENT: 'CONSISTENT';
|
||||
CONSTRAINT: 'CONSTRAINT';
|
||||
CONSTRAINTS: 'CONSTRAINTS';
|
||||
CONVERT: 'CONVERT';
|
||||
COPY: 'COPY';
|
||||
COUNT: 'COUNT';
|
||||
|
||||
@ -102,6 +102,7 @@ statement
|
||||
constraint #addConstraint
|
||||
| ALTER TABLE table=relation
|
||||
DROP CONSTRAINT constraintName=errorCapturingIdentifier #dropConstraint
|
||||
| SHOW CONSTRAINTS FROM table=relation #showConstraint
|
||||
| CALL functionName=identifier LEFT_PAREN (expression (COMMA expression)*)? RIGHT_PAREN #callProcedure
|
||||
;
|
||||
|
||||
|
||||
@ -341,7 +341,7 @@ public abstract class Table extends MetaObject implements Writable, TableIf {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Constraint> getConstraintsMap() {
|
||||
public Map<String, Constraint> getConstraintsMapUnsafe() {
|
||||
return constraintsMap;
|
||||
}
|
||||
|
||||
|
||||
@ -33,6 +33,7 @@ import org.apache.doris.statistics.TableStatsMeta;
|
||||
import org.apache.doris.thrift.TTableDescriptor;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
@ -162,7 +163,7 @@ public interface TableIf {
|
||||
void write(DataOutput out) throws IOException;
|
||||
|
||||
// Don't use it outside due to its thread-unsafe, use get specific constraints instead.
|
||||
default Map<String, Constraint> getConstraintsMap() {
|
||||
default Map<String, Constraint> getConstraintsMapUnsafe() {
|
||||
throw new RuntimeException(String.format("Not implemented constraint for table %s. "
|
||||
+ "And the function can't be called outside, consider get specific function "
|
||||
+ "like getForeignKeyConstraints/getPrimaryKeyConstraints/getUniqueConstraints.", this));
|
||||
@ -171,7 +172,7 @@ public interface TableIf {
|
||||
default Set<ForeignKeyConstraint> getForeignKeyConstraints() {
|
||||
readLock();
|
||||
try {
|
||||
return getConstraintsMap().values().stream()
|
||||
return getConstraintsMapUnsafe().values().stream()
|
||||
.filter(ForeignKeyConstraint.class::isInstance)
|
||||
.map(ForeignKeyConstraint.class::cast)
|
||||
.collect(ImmutableSet.toImmutableSet());
|
||||
@ -182,10 +183,21 @@ public interface TableIf {
|
||||
}
|
||||
}
|
||||
|
||||
default Map<String, Constraint> getConstraintMap() {
|
||||
readLock();
|
||||
try {
|
||||
return ImmutableMap.copyOf(getConstraintsMapUnsafe());
|
||||
} catch (Exception ignored) {
|
||||
return ImmutableMap.of();
|
||||
} finally {
|
||||
readUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
default Set<PrimaryKeyConstraint> getPrimaryKeyConstraints() {
|
||||
readLock();
|
||||
try {
|
||||
return getConstraintsMap().values().stream()
|
||||
return getConstraintsMapUnsafe().values().stream()
|
||||
.filter(PrimaryKeyConstraint.class::isInstance)
|
||||
.map(PrimaryKeyConstraint.class::cast)
|
||||
.collect(ImmutableSet.toImmutableSet());
|
||||
@ -199,7 +211,7 @@ public interface TableIf {
|
||||
default Set<UniqueConstraint> getUniqueConstraints() {
|
||||
readLock();
|
||||
try {
|
||||
return getConstraintsMap().values().stream()
|
||||
return getConstraintsMapUnsafe().values().stream()
|
||||
.filter(UniqueConstraint.class::isInstance)
|
||||
.map(UniqueConstraint.class::cast)
|
||||
.collect(ImmutableSet.toImmutableSet());
|
||||
@ -227,7 +239,7 @@ public interface TableIf {
|
||||
default void addUniqueConstraint(String name, ImmutableList<String> columns) {
|
||||
writeLock();
|
||||
try {
|
||||
Map<String, Constraint> constraintMap = getConstraintsMap();
|
||||
Map<String, Constraint> constraintMap = getConstraintsMapUnsafe();
|
||||
UniqueConstraint uniqueConstraint = new UniqueConstraint(name, ImmutableSet.copyOf(columns));
|
||||
checkConstraintNotExistence(name, uniqueConstraint, constraintMap);
|
||||
constraintMap.put(name, uniqueConstraint);
|
||||
@ -239,7 +251,7 @@ public interface TableIf {
|
||||
default void addPrimaryKeyConstraint(String name, ImmutableList<String> columns) {
|
||||
writeLock();
|
||||
try {
|
||||
Map<String, Constraint> constraintMap = getConstraintsMap();
|
||||
Map<String, Constraint> constraintMap = getConstraintsMapUnsafe();
|
||||
PrimaryKeyConstraint primaryKeyConstraint = new PrimaryKeyConstraint(name, ImmutableSet.copyOf(columns));
|
||||
checkConstraintNotExistence(name, primaryKeyConstraint, constraintMap);
|
||||
constraintMap.put(name, primaryKeyConstraint);
|
||||
@ -251,7 +263,7 @@ public interface TableIf {
|
||||
default void updatePrimaryKeyForForeignKey(PrimaryKeyConstraint requirePrimaryKey, TableIf referencedTable) {
|
||||
referencedTable.writeLock();
|
||||
try {
|
||||
Optional<Constraint> primaryKeyConstraint = referencedTable.getConstraintsMap().values().stream()
|
||||
Optional<Constraint> primaryKeyConstraint = referencedTable.getConstraintsMapUnsafe().values().stream()
|
||||
.filter(requirePrimaryKey::equals)
|
||||
.findFirst();
|
||||
if (!primaryKeyConstraint.isPresent()) {
|
||||
@ -269,7 +281,7 @@ public interface TableIf {
|
||||
TableIf referencedTable, ImmutableList<String> referencedColumns) {
|
||||
writeLock();
|
||||
try {
|
||||
Map<String, Constraint> constraintMap = getConstraintsMap();
|
||||
Map<String, Constraint> constraintMap = getConstraintsMapUnsafe();
|
||||
ForeignKeyConstraint foreignKeyConstraint =
|
||||
new ForeignKeyConstraint(name, columns, referencedTable, referencedColumns);
|
||||
checkConstraintNotExistence(name, foreignKeyConstraint, constraintMap);
|
||||
@ -285,7 +297,7 @@ public interface TableIf {
|
||||
default void dropConstraint(String name) {
|
||||
writeLock();
|
||||
try {
|
||||
Map<String, Constraint> constraintMap = getConstraintsMap();
|
||||
Map<String, Constraint> constraintMap = getConstraintsMapUnsafe();
|
||||
if (!constraintMap.containsKey(name)) {
|
||||
throw new AnalysisException(
|
||||
String.format("Unknown constraint %s on table %s.", name, this.getName()));
|
||||
@ -304,7 +316,7 @@ public interface TableIf {
|
||||
default void dropFKReferringPK(TableIf table, PrimaryKeyConstraint constraint) {
|
||||
writeLock();
|
||||
try {
|
||||
Map<String, Constraint> constraintMap = getConstraintsMap();
|
||||
Map<String, Constraint> constraintMap = getConstraintsMapUnsafe();
|
||||
constraintMap.entrySet().removeIf(e -> e.getValue() instanceof ForeignKeyConstraint
|
||||
&& ((ForeignKeyConstraint) e.getValue()).isReferringPK(table, constraint));
|
||||
} finally {
|
||||
|
||||
@ -19,9 +19,19 @@ package org.apache.doris.catalog.constraint;
|
||||
|
||||
public abstract class Constraint {
|
||||
public enum ConstraintType {
|
||||
FOREIGN_KEY,
|
||||
PRIMARY_KEY,
|
||||
UNIQUE
|
||||
FOREIGN_KEY("FOREIGN KEY"),
|
||||
PRIMARY_KEY("PRIMARY KEY"),
|
||||
UNIQUE("UNIQUE");
|
||||
|
||||
private final String name;
|
||||
|
||||
private ConstraintType(String stringValue) {
|
||||
this.name = stringValue;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
private final String name;
|
||||
@ -36,4 +46,8 @@ public abstract class Constraint {
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public ConstraintType getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,4 +104,15 @@ public class ForeignKeyConstraint extends Constraint {
|
||||
return Objects.equals(foreignToReference, other.foreignToReference)
|
||||
&& Objects.equals(referencedTable, other.referencedTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String foreignKeys = "(" + String.join(", ", foreignToReference.keySet()) + ")";
|
||||
String primaryKeys = "(" + String.join(", ", foreignToReference.values()) + ")";
|
||||
return String.format("FOREIGN KEY %s REFERENCES %s %s", foreignKeys, referencedTable, primaryKeys);
|
||||
}
|
||||
|
||||
public String getTypeName() {
|
||||
return "FOREIGN KEY";
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,6 +57,11 @@ public class PrimaryKeyConstraint extends Constraint {
|
||||
.collect(ImmutableList.toImmutableList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PRIMARY KEY (" + String.join(", ", columns) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@ -65,4 +65,10 @@ class TableIdentifier {
|
||||
public int hashCode() {
|
||||
return Objects.hash(databaseId, tableId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
TableIf tableIf = this.toTableIf();
|
||||
return String.format("%s.%s", tableIf.getDatabase().getFullName(), tableIf.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +53,11 @@ public class UniqueConstraint extends Constraint {
|
||||
return columns.equals(that.columns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UNIQUE (" + String.join(", ", columns) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(columns);
|
||||
|
||||
@ -156,6 +156,7 @@ import org.apache.doris.nereids.DorisParser.SelectClauseContext;
|
||||
import org.apache.doris.nereids.DorisParser.SelectColumnClauseContext;
|
||||
import org.apache.doris.nereids.DorisParser.SelectHintContext;
|
||||
import org.apache.doris.nereids.DorisParser.SetOperationContext;
|
||||
import org.apache.doris.nereids.DorisParser.ShowConstraintContext;
|
||||
import org.apache.doris.nereids.DorisParser.SimpleColumnDefContext;
|
||||
import org.apache.doris.nereids.DorisParser.SimpleColumnDefsContext;
|
||||
import org.apache.doris.nereids.DorisParser.SingleStatementContext;
|
||||
@ -347,6 +348,7 @@ import org.apache.doris.nereids.trees.plans.commands.LoadCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.PauseMTMVCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.RefreshMTMVCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.ResumeMTMVCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.UpdateCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.info.AlterMTMVInfo;
|
||||
import org.apache.doris.nereids.trees.plans.commands.info.AlterMTMVPropertyInfo;
|
||||
@ -429,6 +431,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -746,6 +749,13 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
|
||||
return new AlterMTMVCommand(alterMTMVInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogicalPlan visitShowConstraint(ShowConstraintContext ctx) {
|
||||
Set<UnboundRelation> unboundRelation = visitRelation(ctx.table)
|
||||
.collect(UnboundRelation.class::isInstance);
|
||||
return new ShowConstraintsCommand(unboundRelation.iterator().next().getNameParts());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogicalPlan visitAddConstraint(AddConstraintContext ctx) {
|
||||
LogicalPlan curTable = visitRelation(ctx.table);
|
||||
|
||||
@ -133,6 +133,7 @@ public enum PlanType {
|
||||
ALTER_MTMV_COMMAND,
|
||||
ADD_CONSTRAINT_COMMAND,
|
||||
DROP_CONSTRAINT_COMMAND,
|
||||
SHOW_CONSTRAINTS_COMMAND,
|
||||
REFRESH_MTMV_COMMAND,
|
||||
DROP_MTMV_COMMAND,
|
||||
PAUSE_MTMV_COMMAND,
|
||||
|
||||
@ -0,0 +1,66 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
package org.apache.doris.nereids.trees.plans.commands;
|
||||
|
||||
import org.apache.doris.catalog.TableIf;
|
||||
import org.apache.doris.nereids.trees.plans.PlanType;
|
||||
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
|
||||
import org.apache.doris.nereids.util.RelationUtil;
|
||||
import org.apache.doris.qe.ConnectContext;
|
||||
import org.apache.doris.qe.StmtExecutor;
|
||||
|
||||
import org.apache.hadoop.util.Lists;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* add constraint command
|
||||
*/
|
||||
public class ShowConstraintsCommand extends Command implements NoForward {
|
||||
|
||||
public static final Logger LOG = LogManager.getLogger(ShowConstraintsCommand.class);
|
||||
private final List<String> nameParts;
|
||||
|
||||
/**
|
||||
* constructor
|
||||
*/
|
||||
public ShowConstraintsCommand(List<String> nameParts) {
|
||||
super(PlanType.SHOW_CONSTRAINTS_COMMAND);
|
||||
this.nameParts = nameParts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ConnectContext ctx, StmtExecutor executor) throws Exception {
|
||||
TableIf tableIf = RelationUtil.getDbAndTable(
|
||||
RelationUtil.getQualifierName(ctx, nameParts), ctx.getEnv()).value();
|
||||
List<List<String>> res = tableIf.getConstraintMap().entrySet().stream()
|
||||
.map(e -> Lists.newArrayList(e.getKey(),
|
||||
e.getValue().getType().getName(),
|
||||
e.getValue().toString()))
|
||||
.collect(Collectors.toList());
|
||||
executor.handleShowConstraintStmt(res);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
|
||||
return visitor.visitShowConstraintsCommand(this, context);
|
||||
}
|
||||
}
|
||||
@ -38,6 +38,7 @@ import org.apache.doris.nereids.trees.plans.commands.LoadCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.PauseMTMVCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.RefreshMTMVCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.ResumeMTMVCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand;
|
||||
import org.apache.doris.nereids.trees.plans.commands.UpdateCommand;
|
||||
|
||||
/** CommandVisitor. */
|
||||
@ -108,6 +109,10 @@ public interface CommandVisitor<R, C> {
|
||||
return visitCommand(dropConstraintCommand, context);
|
||||
}
|
||||
|
||||
default R visitShowConstraintsCommand(ShowConstraintsCommand showConstraintsCommand, C context) {
|
||||
return visitCommand(showConstraintsCommand, context);
|
||||
}
|
||||
|
||||
default R visitRefreshMTMVCommand(RefreshMTMVCommand refreshMTMVCommand, C context) {
|
||||
return visitCommand(refreshMTMVCommand, context);
|
||||
}
|
||||
|
||||
@ -2367,6 +2367,16 @@ public class StmtExecutor {
|
||||
private void handleLockTablesStmt() {
|
||||
}
|
||||
|
||||
public void handleShowConstraintStmt(List<List<String>> result) throws IOException {
|
||||
ShowResultSetMetaData metaData = ShowResultSetMetaData.builder()
|
||||
.addColumn(new Column("Name", ScalarType.createVarchar(20)))
|
||||
.addColumn(new Column("Type", ScalarType.createVarchar(20)))
|
||||
.addColumn(new Column("Definition", ScalarType.createVarchar(20)))
|
||||
.build();
|
||||
ResultSet resultSet = new ShowResultSet(metaData, result);
|
||||
sendResultSet(resultSet);
|
||||
}
|
||||
|
||||
public void handleExplainStmt(String result, boolean isNereids) throws IOException {
|
||||
ShowResultSetMetaData metaData = ShowResultSetMetaData.builder()
|
||||
.addColumn(new Column("Explain String" + (isNereids ? "(Nereids Planner)" : "(Old Planner)"),
|
||||
|
||||
@ -81,7 +81,7 @@ class ConstraintTest extends TestWithFeService implements PlanPatternMatchSuppor
|
||||
"alter table t1 drop constraint pk");
|
||||
dropCommand.run(connectContext, null);
|
||||
PlanChecker.from(connectContext).parse("select * from t1").analyze().matches(
|
||||
logicalOlapScan().when(o -> o.getTable().getConstraintsMap().isEmpty()));
|
||||
logicalOlapScan().when(o -> o.getTable().getConstraintsMapUnsafe().isEmpty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -102,7 +102,7 @@ class ConstraintTest extends TestWithFeService implements PlanPatternMatchSuppor
|
||||
"alter table t1 drop constraint un");
|
||||
dropCommand.run(connectContext, null);
|
||||
PlanChecker.from(connectContext).parse("select * from t1").analyze().matches(
|
||||
logicalOlapScan().when(o -> o.getTable().getConstraintsMap().isEmpty()));
|
||||
logicalOlapScan().when(o -> o.getTable().getConstraintsMapUnsafe().isEmpty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -149,7 +149,7 @@ class ConstraintTest extends TestWithFeService implements PlanPatternMatchSuppor
|
||||
"alter table t1 drop constraint fk");
|
||||
dropCommand.run(connectContext, null);
|
||||
PlanChecker.from(connectContext).parse("select * from t1").analyze().matches(
|
||||
logicalOlapScan().when(o -> o.getTable().getConstraintsMap().isEmpty()));
|
||||
logicalOlapScan().when(o -> o.getTable().getConstraintsMapUnsafe().isEmpty()));
|
||||
// drop pk and fk referenced it also should be dropped
|
||||
((AddConstraintCommand) new NereidsParser().parseSingle(
|
||||
"alter table t1 add constraint fk foreign key (k1, k2) references t2(k1, k2)")).run(connectContext,
|
||||
@ -158,8 +158,8 @@ class ConstraintTest extends TestWithFeService implements PlanPatternMatchSuppor
|
||||
.run(connectContext, null);
|
||||
|
||||
PlanChecker.from(connectContext).parse("select * from t1").analyze().matches(
|
||||
logicalOlapScan().when(o -> o.getTable().getConstraintsMap().isEmpty()));
|
||||
logicalOlapScan().when(o -> o.getTable().getConstraintsMapUnsafe().isEmpty()));
|
||||
PlanChecker.from(connectContext).parse("select * from t2").analyze().matches(
|
||||
logicalOlapScan().when(o -> o.getTable().getConstraintsMap().isEmpty()));
|
||||
logicalOlapScan().when(o -> o.getTable().getConstraintsMapUnsafe().isEmpty()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user