[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:
谢健
2024-01-09 15:31:13 +08:00
committed by yiguolei
parent be56bf06cf
commit 34fe5ee38b
17 changed files with 257 additions and 19 deletions

View File

@ -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;
}

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -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";
}
}

View File

@ -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) {

View File

@ -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());
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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)"),