morrySnow commented on code in PR #27627: URL: https://github.com/apache/doris/pull/27627#discussion_r1408706210
########## fe/fe-core/src/main/java/org/apache/doris/catalog/Table.java: ########## @@ -115,6 +117,9 @@ public abstract class Table extends MetaObject implements Writable, TableIf { // sql for creating this table, default is ""; protected String ddlSql = ""; + @SerializedName(value = "constraints") + private volatile ConcurrentMap<String, Constraint> constraintsMap = Maps.newConcurrentMap(); Review Comment: why volatile and concurrentMap? we should always lock table before write it ########## fe/fe-core/src/main/java/org/apache/doris/catalog/external/Constraint.java: ########## @@ -0,0 +1,21 @@ +// 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.catalog.external; Review Comment: why named this package `external`? constraint is better? ########## fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4: ########## @@ -96,6 +96,18 @@ statement | (REFRESH (refreshMethod | refreshTrigger | refreshMethod refreshTrigger)) | (SET LEFT_PAREN fileProperties=propertyItemList RIGHT_PAREN)) #alterMTMV | DROP MATERIALIZED VIEW (IF EXISTS)? mvName=multipartIdentifier #dropMTMV + | ALTER TABLE table=relation + ADD CONSTRAINT constraintName=errorCapturingIdentifier + constraint #addConstraint + | ALTER TABLE table=relation + DROP CONSTRAINT constraintName=errorCapturingIdentifier #dropConstraint Review Comment: indent ########## fe/fe-core/src/main/java/org/apache/doris/catalog/ForeignKeyConstraint.java: ########## @@ -0,0 +1,74 @@ +// 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.catalog; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.collect.ImmutableSet; + +import java.util.Objects; + +public class ForeignKeyConstraint extends Constraint { + private final ImmutableMap<Column, Column> foreignToReference; + private final TableIf referencedTable; + + public ForeignKeyConstraint(ImmutableList<Column> columns, + TableIf referencedTable, ImmutableList<Column> referencedColumns) { + this.referencedTable = referencedTable; + ImmutableMap.Builder<Column, Column> builder = new Builder<>(); + for (int i = 0; i < columns.size(); i++) { + builder.put(columns.get(i), referencedColumns.get(i)); Review Comment: check size of two list ########## fe/fe-core/src/main/java/org/apache/doris/catalog/TableIf.java: ########## @@ -153,6 +156,80 @@ default int getBaseColumnIdxByName(String colName) { void write(DataOutput out) throws IOException; + default Map<String, Constraint> getConstraintMap() { + throw new RuntimeException("Not implemented constraint for table " + this.toString()); + } + + // Note this function is not thread safe + default void checkConstraintNotExistence(String name, Constraint primaryKeyConstraint, + Map<String, Constraint> constraintMap) { + if (constraintMap.containsKey(name)) { + throw new RuntimeException(String.format("Constraint name %s has existed", name)); + } + for (Map.Entry<String, Constraint> entry : constraintMap.entrySet()) { + if (entry.getValue().equals(primaryKeyConstraint)) { + throw new RuntimeException(String.format( + "Constraint %s has existed, named %s", primaryKeyConstraint, entry.getKey())); + } + } + } + + default void addUniqueConstraint(String name, ImmutableList<Column> columns) { + writeLock(); + try { + Map<String, Constraint> constraintMap = getConstraintMap(); + UniqueConstraint uniqueConstraint = new UniqueConstraint(ImmutableSet.copyOf(columns)); + checkConstraintNotExistence(name, uniqueConstraint, constraintMap); + constraintMap.put(name, uniqueConstraint); Review Comment: should check constraintMap is null or add NotNull annotation on getConstraintMap ########## fe/fe-core/src/main/java/org/apache/doris/catalog/ForeignKeyConstraint.java: ########## @@ -0,0 +1,74 @@ +// 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.catalog; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.collect.ImmutableSet; + +import java.util.Objects; + +public class ForeignKeyConstraint extends Constraint { + private final ImmutableMap<Column, Column> foreignToReference; + private final TableIf referencedTable; + + public ForeignKeyConstraint(ImmutableList<Column> columns, + TableIf referencedTable, ImmutableList<Column> referencedColumns) { Review Comment: check null for parameters ########## fe/fe-core/src/main/java/org/apache/doris/catalog/ForeignKeyConstraint.java: ########## @@ -0,0 +1,74 @@ +// 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.catalog; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.collect.ImmutableSet; + +import java.util.Objects; + +public class ForeignKeyConstraint extends Constraint { + private final ImmutableMap<Column, Column> foreignToReference; + private final TableIf referencedTable; + + public ForeignKeyConstraint(ImmutableList<Column> columns, + TableIf referencedTable, ImmutableList<Column> referencedColumns) { + this.referencedTable = referencedTable; + ImmutableMap.Builder<Column, Column> builder = new Builder<>(); + for (int i = 0; i < columns.size(); i++) { + builder.put(columns.get(i), referencedColumns.get(i)); Review Comment: check item nullable ########## fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/AddConstraintCommand.java: ########## @@ -0,0 +1,102 @@ +// 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.Column; +import org.apache.doris.catalog.Table; +import org.apache.doris.catalog.TableIf; +import org.apache.doris.common.Pair; +import org.apache.doris.nereids.NereidsPlanner; +import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.properties.PhysicalProperties; +import org.apache.doris.nereids.trees.expressions.SlotReference; +import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.PlanType; +import org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel; +import org.apache.doris.nereids.trees.plans.logical.LogicalCatalogRelation; +import org.apache.doris.nereids.trees.plans.logical.LogicalPlan; +import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.StmtExecutor; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Set; + +/** + * create multi table materialized view + */ +public class AddConstraintCommand extends Command implements ForwardWithSync { + + public static final Logger LOG = LogManager.getLogger(AddConstraintCommand.class); + private final String name; + private final Constraint constraint; + + /** + * constructor + */ + public AddConstraintCommand(String name, Constraint constraint) { + super(PlanType.ADD_CONSTRAINT_COMMAND); + this.constraint = constraint; + this.name = name; + } + + @Override + public void run(ConnectContext ctx, StmtExecutor executor) throws Exception { + Pair<ImmutableList<Column>, TableIf> columnsAndTable = extractColumnsAndTable(ctx, constraint.toProject()); + if (constraint.isForeignKey()) { + Pair<ImmutableList<Column>, TableIf> foreignColumnsAndTable + = extractColumnsAndTable(ctx, constraint.toReferenceProject()); + columnsAndTable.second.addForeignConstraint(name, columnsAndTable.first, + foreignColumnsAndTable.second, foreignColumnsAndTable.first); + } else if (constraint.isPrimaryKey()) { + columnsAndTable.second.addPrimaryKeyConstraint(name, columnsAndTable.first); + } else if (constraint.isUnique()) { + columnsAndTable.second.addUniqueConstraint(name, columnsAndTable.first); + } + } + + private Pair<ImmutableList<Column>, TableIf> extractColumnsAndTable(ConnectContext ctx, LogicalPlan plan) { + NereidsPlanner planner = new NereidsPlanner(ctx.getStatementContext()); + Plan analyzedPlan = planner.plan(plan, PhysicalProperties.ANY, ExplainLevel.ANALYZED_PLAN); + Set<LogicalCatalogRelation> logicalCatalogRelationSet = analyzedPlan + .collect(LogicalCatalogRelation.class::isInstance); + if (logicalCatalogRelationSet.size() != 1) { + throw new AnalysisException("Can not found table in constraint" + constraint.toString()); Review Comment: ```suggestion throw new AnalysisException("Can not found table in constraint " + constraint.toString()); ``` ########## fe/fe-core/src/main/java/org/apache/doris/catalog/TableIf.java: ########## @@ -153,6 +156,80 @@ default int getBaseColumnIdxByName(String colName) { void write(DataOutput out) throws IOException; + default Map<String, Constraint> getConstraintMap() { + throw new RuntimeException("Not implemented constraint for table " + this.toString()); + } + + // Note this function is not thread safe + default void checkConstraintNotExistence(String name, Constraint primaryKeyConstraint, + Map<String, Constraint> constraintMap) { + if (constraintMap.containsKey(name)) { + throw new RuntimeException(String.format("Constraint name %s has existed", name)); + } + for (Map.Entry<String, Constraint> entry : constraintMap.entrySet()) { + if (entry.getValue().equals(primaryKeyConstraint)) { + throw new RuntimeException(String.format( + "Constraint %s has existed, named %s", primaryKeyConstraint, entry.getKey())); + } + } + } + + default void addUniqueConstraint(String name, ImmutableList<Column> columns) { + writeLock(); + try { + Map<String, Constraint> constraintMap = getConstraintMap(); + UniqueConstraint uniqueConstraint = new UniqueConstraint(ImmutableSet.copyOf(columns)); + checkConstraintNotExistence(name, uniqueConstraint, constraintMap); + constraintMap.put(name, uniqueConstraint); + } finally { + writeUnlock(); + } + } + + default void addPrimaryKeyConstraint(String name, ImmutableList<Column> columns) { + writeLock(); + try { + Map<String, Constraint> constraintMap = getConstraintMap(); + PrimaryKeyConstraint primaryKeyConstraint = new PrimaryKeyConstraint(ImmutableSet.copyOf(columns)); Review Comment: need to check repeat column and throw exception ########## fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java: ########## @@ -77,6 +78,10 @@ default R visitAlterMTMVCommand(AlterMTMVCommand alterMTMVCommand, C context) { return visitCommand(alterMTMVCommand, context); } + default R visitAddConstraint(AddConstraintCommand addConstraintCommand, C context) { Review Comment: ```suggestion default R visitAddConstraintCommand(AddConstraintCommand addConstraintCommand, C context) { ``` ########## fe/fe-core/src/main/java/org/apache/doris/catalog/TableIf.java: ########## @@ -153,6 +156,80 @@ default int getBaseColumnIdxByName(String colName) { void write(DataOutput out) throws IOException; + default Map<String, Constraint> getConstraintMap() { + throw new RuntimeException("Not implemented constraint for table " + this.toString()); + } + + // Note this function is not thread safe + default void checkConstraintNotExistence(String name, Constraint primaryKeyConstraint, + Map<String, Constraint> constraintMap) { + if (constraintMap.containsKey(name)) { + throw new RuntimeException(String.format("Constraint name %s has existed", name)); + } + for (Map.Entry<String, Constraint> entry : constraintMap.entrySet()) { + if (entry.getValue().equals(primaryKeyConstraint)) { + throw new RuntimeException(String.format( + "Constraint %s has existed, named %s", primaryKeyConstraint, entry.getKey())); + } + } + } + + default void addUniqueConstraint(String name, ImmutableList<Column> columns) { + writeLock(); + try { + Map<String, Constraint> constraintMap = getConstraintMap(); Review Comment: need to check repeat column and throw exception ########## fe/fe-core/src/main/java/org/apache/doris/catalog/ForeignKeyConstraint.java: ########## @@ -0,0 +1,74 @@ +// 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.catalog; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.collect.ImmutableSet; + +import java.util.Objects; + +public class ForeignKeyConstraint extends Constraint { + private final ImmutableMap<Column, Column> foreignToReference; + private final TableIf referencedTable; + + public ForeignKeyConstraint(ImmutableList<Column> columns, + TableIf referencedTable, ImmutableList<Column> referencedColumns) { + this.referencedTable = referencedTable; + ImmutableMap.Builder<Column, Column> builder = new Builder<>(); + for (int i = 0; i < columns.size(); i++) { + builder.put(columns.get(i), referencedColumns.get(i)); + } + this.foreignToReference = builder.build(); + } + + public ImmutableSet<Column> getColumns() { + return foreignToReference.keySet(); + } + + public ImmutableSet<Column> getReferencedColumns() { + return ImmutableSet.copyOf(foreignToReference.values()); + } Review Comment: why we still need this interface? ########## fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4: ########## @@ -96,6 +96,18 @@ statement | (REFRESH (refreshMethod | refreshTrigger | refreshMethod refreshTrigger)) | (SET LEFT_PAREN fileProperties=propertyItemList RIGHT_PAREN)) #alterMTMV | DROP MATERIALIZED VIEW (IF EXISTS)? mvName=multipartIdentifier #dropMTMV + | ALTER TABLE table=relation + ADD CONSTRAINT constraintName=errorCapturingIdentifier + constraint #addConstraint + | ALTER TABLE table=relation + DROP CONSTRAINT constraintName=errorCapturingIdentifier #dropConstraint + ; + +constraint + : PRIMARY KEY LEFT_PAREN slots=namedExpressionSeq RIGHT_PAREN + | UNIQUE LEFT_PAREN slots=namedExpressionSeq RIGHT_PAREN + | FOREIGN KEY LEFT_PAREN slots=namedExpressionSeq RIGHT_PAREN + REFERENCES referenceTable=multipartIdentifier LEFT_PAREN referenceSlots=namedExpressionSeq RIGHT_PAREN Review Comment: indent ########## fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/Constraint.java: ########## @@ -0,0 +1,112 @@ +// 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.nereids.trees.expressions.Slot; +import org.apache.doris.nereids.trees.plans.logical.LogicalPlan; +import org.apache.doris.nereids.trees.plans.logical.LogicalProject; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.Objects; +import javax.annotation.Nullable; + +/** + * Constraint + */ +public class Constraint { Review Comment: this should use inherit too? ########## fe/fe-core/src/main/java/org/apache/doris/catalog/ForeignKeyConstraint.java: ########## @@ -0,0 +1,74 @@ +// 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.catalog; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.collect.ImmutableSet; + +import java.util.Objects; + +public class ForeignKeyConstraint extends Constraint { + private final ImmutableMap<Column, Column> foreignToReference; + private final TableIf referencedTable; + + public ForeignKeyConstraint(ImmutableList<Column> columns, + TableIf referencedTable, ImmutableList<Column> referencedColumns) { + this.referencedTable = referencedTable; + ImmutableMap.Builder<Column, Column> builder = new Builder<>(); + for (int i = 0; i < columns.size(); i++) { Review Comment: check repeat columns ########## fe/fe-core/src/main/java/org/apache/doris/catalog/PrimaryKeyConstraint.java: ########## @@ -0,0 +1,65 @@ +// 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.catalog; + +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +import java.util.ArrayList; +import java.util.List; + +public class PrimaryKeyConstraint extends Constraint { + private final ImmutableSet<Column> columns; + + // record the foreign table which references the primary key + private final List<TableIf> foreignTables = new ArrayList<>(); + + public PrimaryKeyConstraint(ImmutableSet<Column> columns) { + this.columns = columns; Review Comment: check nullable, use copyOf and paramter use Set not ImmutableSet ########## fe/fe-core/src/main/java/org/apache/doris/catalog/Table.java: ########## @@ -291,6 +295,15 @@ public String getQualifiedName() { } } + public Constraint getConstraint(String name) { + return constraintsMap.get(name); + } + + @Override + public Map<String, Constraint> getConstraintMap() { Review Comment: ```suggestion public Map<String, Constraint> getConstraintsMap() { ``` ########## fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/AddConstraintCommand.java: ########## @@ -0,0 +1,102 @@ +// 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.Column; +import org.apache.doris.catalog.Table; +import org.apache.doris.catalog.TableIf; +import org.apache.doris.common.Pair; +import org.apache.doris.nereids.NereidsPlanner; +import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.properties.PhysicalProperties; +import org.apache.doris.nereids.trees.expressions.SlotReference; +import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.PlanType; +import org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel; +import org.apache.doris.nereids.trees.plans.logical.LogicalCatalogRelation; +import org.apache.doris.nereids.trees.plans.logical.LogicalPlan; +import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.StmtExecutor; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Set; + +/** + * create multi table materialized view + */ +public class AddConstraintCommand extends Command implements ForwardWithSync { + + public static final Logger LOG = LogManager.getLogger(AddConstraintCommand.class); + private final String name; + private final Constraint constraint; + + /** + * constructor + */ + public AddConstraintCommand(String name, Constraint constraint) { + super(PlanType.ADD_CONSTRAINT_COMMAND); + this.constraint = constraint; + this.name = name; + } + + @Override + public void run(ConnectContext ctx, StmtExecutor executor) throws Exception { + Pair<ImmutableList<Column>, TableIf> columnsAndTable = extractColumnsAndTable(ctx, constraint.toProject()); + if (constraint.isForeignKey()) { + Pair<ImmutableList<Column>, TableIf> foreignColumnsAndTable + = extractColumnsAndTable(ctx, constraint.toReferenceProject()); + columnsAndTable.second.addForeignConstraint(name, columnsAndTable.first, + foreignColumnsAndTable.second, foreignColumnsAndTable.first); + } else if (constraint.isPrimaryKey()) { + columnsAndTable.second.addPrimaryKeyConstraint(name, columnsAndTable.first); + } else if (constraint.isUnique()) { + columnsAndTable.second.addUniqueConstraint(name, columnsAndTable.first); + } + } + + private Pair<ImmutableList<Column>, TableIf> extractColumnsAndTable(ConnectContext ctx, LogicalPlan plan) { + NereidsPlanner planner = new NereidsPlanner(ctx.getStatementContext()); + Plan analyzedPlan = planner.plan(plan, PhysicalProperties.ANY, ExplainLevel.ANALYZED_PLAN); + Set<LogicalCatalogRelation> logicalCatalogRelationSet = analyzedPlan + .collect(LogicalCatalogRelation.class::isInstance); + if (logicalCatalogRelationSet.size() != 1) { + throw new AnalysisException("Can not found table in constraint" + constraint.toString()); + } + LogicalCatalogRelation catalogRelation = logicalCatalogRelationSet.iterator().next(); + Preconditions.checkArgument(catalogRelation.getTable() instanceof Table, + "We only support table now but we meet ", catalogRelation.getTable()); + ImmutableList<Column> columns = analyzedPlan.getOutput().stream() + .map(s -> { + Preconditions.checkArgument(s instanceof SlotReference + && ((SlotReference) s).getColumn().isPresent(), + "Constraint only supports simple slot but meets ", s); Review Comment: it is better to forbid this in parser ########## fe/fe-core/src/main/java/org/apache/doris/catalog/Table.java: ########## @@ -115,6 +117,9 @@ public abstract class Table extends MetaObject implements Writable, TableIf { // sql for creating this table, default is ""; protected String ddlSql = ""; + @SerializedName(value = "constraints") + private HashMap<String, Constraint> constraintsMap = new HashMap<>(); Review Comment: so, why add SerializedName on some attr? ########## fe/fe-core/src/main/java/org/apache/doris/catalog/TableIf.java: ########## @@ -153,6 +156,80 @@ default int getBaseColumnIdxByName(String colName) { void write(DataOutput out) throws IOException; + default Map<String, Constraint> getConstraintMap() { + throw new RuntimeException("Not implemented constraint for table " + this.toString()); + } + + // Note this function is not thread safe + default void checkConstraintNotExistence(String name, Constraint primaryKeyConstraint, + Map<String, Constraint> constraintMap) { + if (constraintMap.containsKey(name)) { Review Comment: case sensitive? case insensitive? -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org