This is an automated email from the ASF dual-hosted git repository.

kxiao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 36cea89c22 [Fix](planner)support delete conditions contain non-key 
columns and add check in analyze phase for delete. (#22673)
36cea89c22 is described below

commit 36cea89c2257001e7411a181876194f1ecb8734a
Author: mch_ucchi <[email protected]>
AuthorDate: Mon Aug 7 21:49:53 2023 +0800

    [Fix](planner)support delete conditions contain non-key columns and add 
check in analyze phase for delete. (#22673)
---
 .../java/org/apache/doris/analysis/DeleteStmt.java | 124 +++++++++++++++++++++
 .../java/org/apache/doris/load/DeleteHandler.java  |   2 +-
 .../suites/delete_p0/test_delete.groovy            |  10 ++
 3 files changed, 135 insertions(+), 1 deletion(-)

diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/DeleteStmt.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/DeleteStmt.java
index 2cde4af3e6..f3699af3d5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/DeleteStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/DeleteStmt.java
@@ -19,11 +19,15 @@ package org.apache.doris.analysis;
 
 import org.apache.doris.analysis.CompoundPredicate.Operator;
 import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.Database;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.catalog.KeysType;
 import org.apache.doris.catalog.OlapTable;
+import org.apache.doris.catalog.PrimitiveType;
+import org.apache.doris.catalog.ScalarType;
 import org.apache.doris.catalog.Table;
 import org.apache.doris.catalog.TableIf;
+import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.Config;
 import org.apache.doris.common.ErrorCode;
@@ -42,9 +46,11 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 public class DeleteStmt extends DdlStmt {
 
@@ -121,6 +127,7 @@ public class DeleteStmt extends DdlStmt {
             wherePredicate = exprRewriter.rewrite(wherePredicate, analyzer);
             try {
                 analyzePredicate(wherePredicate, analyzer);
+                checkDeleteConditions();
             } catch (Exception e) {
                 if (!(((OlapTable) targetTable).getKeysType() == 
KeysType.UNIQUE_KEYS)) {
                     throw new AnalysisException(e.getMessage(), e.getCause());
@@ -281,6 +288,123 @@ public class DeleteStmt extends DdlStmt {
         }
     }
 
+    private void checkDeleteConditions() throws AnalysisException {
+        // check condition column is key column and condition value
+        // Here we use "getFullSchema()" to get all columns including VISIBLE 
and SHADOW columns
+
+        // we ensure the db and table exists.
+        Database db = (Database) 
Env.getCurrentEnv().getCurrentCatalog().getDb(getDbName()).get();
+        OlapTable table = ((OlapTable) db.getTable(getTableName()).get());
+
+        Map<String, Column> nameToColumn = 
Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER);
+        for (Column column : table.getFullSchema()) {
+            nameToColumn.put(column.getName(), column);
+        }
+
+        for (Predicate condition : deleteConditions) {
+            SlotRef slotRef = getSlotRef(condition);
+            String columnName = slotRef.getColumnName();
+            if (!nameToColumn.containsKey(columnName)) {
+                throw new AnalysisException(String.format("Unknown column '%s' 
in '%s'", columnName, table.getName()));
+            }
+
+            if (Column.isShadowColumn(columnName)) {
+                throw new AnalysisException("Can not apply delete condition to 
shadow column");
+            }
+
+            // Check if this column is under schema change, if yes, there will 
be a shadow column related to it.
+            // And we don't allow doing delete operation when a condition 
column is under schema change.
+            String shadowColName = Column.getShadowName(columnName);
+            if (nameToColumn.containsKey(shadowColName)) {
+                throw new AnalysisException(String.format("Column '%s' is 
under"
+                        + " schema change operation. Do not allow delete 
operation", columnName));
+            }
+
+            Column column = nameToColumn.get(columnName);
+            // Due to rounding errors, most floating-point numbers end up 
being slightly imprecise,
+            // it also means that numbers expected to be equal often differ 
slightly, so we do not allow compare with
+            // floating-point numbers, floating-point number not allowed in 
where clause
+            if (!column.isKey() && table.getKeysType() != KeysType.DUP_KEYS
+                    || column.getDataType().isFloatingPointType()) {
+                throw new AnalysisException("Column[" + columnName + "] is not 
key column or storage model "
+                        + "is not duplicate or column type is float or 
double.");
+            }
+
+            if (condition instanceof BinaryPredicate) {
+                String value = null;
+                try {
+                    BinaryPredicate binaryPredicate = (BinaryPredicate) 
condition;
+                    // if a bool cond passed to be, be's zone_map cannot 
handle bool correctly,
+                    // change it to a tinyint type here;
+                    value = binaryPredicate.getChild(1).getStringValue();
+                    if (column.getDataType() == PrimitiveType.BOOLEAN) {
+                        if (value.equalsIgnoreCase("true")) {
+                            binaryPredicate.setChild(1, 
LiteralExpr.create("1", Type.TINYINT));
+                        } else if (value.equalsIgnoreCase("false")) {
+                            binaryPredicate.setChild(1, 
LiteralExpr.create("0", Type.TINYINT));
+                        }
+                    } else if (column.getDataType() == PrimitiveType.DATE
+                            || column.getDataType() == PrimitiveType.DATETIME
+                            || column.getDataType() == PrimitiveType.DATEV2) {
+                        DateLiteral dateLiteral = new DateLiteral(value, 
Type.fromPrimitiveType(column.getDataType()));
+                        value = dateLiteral.getStringValue();
+                        binaryPredicate.setChild(1, LiteralExpr.create(value,
+                                Type.fromPrimitiveType(column.getDataType())));
+                    } else if (column.getDataType() == 
PrimitiveType.DATETIMEV2) {
+                        DateLiteral dateLiteral = new DateLiteral(value,
+                                
ScalarType.createDatetimeV2Type(ScalarType.MAX_DATETIMEV2_SCALE));
+                        value = dateLiteral.getStringValue();
+                        binaryPredicate.setChild(1, LiteralExpr.create(value,
+                                
ScalarType.createDatetimeV2Type(ScalarType.MAX_DATETIMEV2_SCALE)));
+                    }
+                    LiteralExpr.create(value, column.getType());
+                } catch (AnalysisException e) {
+                    throw new AnalysisException("Invalid column value[" + 
value + "] for column " + columnName);
+                }
+            } else if (condition instanceof InPredicate) {
+                String value = null;
+                try {
+                    InPredicate inPredicate = (InPredicate) condition;
+                    for (int i = 1; i <= inPredicate.getInElementNum(); i++) {
+                        value = inPredicate.getChild(i).getStringValue();
+                        if (column.getDataType() == PrimitiveType.DATE
+                                || column.getDataType() == 
PrimitiveType.DATETIME
+                                || column.getDataType() == PrimitiveType.DATEV2
+                                || column.getDataType() == 
PrimitiveType.DATETIMEV2) {
+                            DateLiteral dateLiteral = new DateLiteral(value,
+                                    column.getType());
+                            value = dateLiteral.getStringValue();
+                            inPredicate.setChild(i, LiteralExpr.create(value,
+                                    column.getType()));
+                        } else {
+                            LiteralExpr.create(value, 
Type.fromPrimitiveType(column.getDataType()));
+                        }
+                    }
+                } catch (AnalysisException e) {
+                    throw new AnalysisException("Invalid column value[" + 
value + "] for column " + columnName);
+                }
+            }
+
+            // set schema column name
+            slotRef.setCol(column.getName());
+        }
+    }
+
+    private SlotRef getSlotRef(Predicate condition) {
+        SlotRef slotRef = null;
+        if (condition instanceof BinaryPredicate) {
+            BinaryPredicate binaryPredicate = (BinaryPredicate) condition;
+            slotRef = (SlotRef) binaryPredicate.getChild(0);
+        } else if (condition instanceof IsNullPredicate) {
+            IsNullPredicate isNullPredicate = (IsNullPredicate) condition;
+            slotRef = (SlotRef) isNullPredicate.getChild(0);
+        } else if (condition instanceof InPredicate) {
+            InPredicate inPredicate = (InPredicate) condition;
+            slotRef = (SlotRef) inPredicate.getChild(0);
+        }
+        return slotRef;
+    }
+
     @Override
     public String toSql() {
         StringBuilder sb = new StringBuilder();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/load/DeleteHandler.java 
b/fe/fe-core/src/main/java/org/apache/doris/load/DeleteHandler.java
index b67ddca85b..578b3cd4d0 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/load/DeleteHandler.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/load/DeleteHandler.java
@@ -801,7 +801,7 @@ public class DeleteHandler implements Writable {
                 String columnName = slotRef.getColumnName();
                 StringBuilder sb = new StringBuilder();
                 sb.append(columnName).append(" 
").append(binaryPredicate.getOp().name()).append(" \"")
-                        .append(((LiteralExpr) 
binaryPredicate.getChild(1)).getStringValue()).append("\"");
+                        
.append(binaryPredicate.getChild(1).getStringValue()).append("\"");
                 deleteConditions.add(sb.toString());
             } else if (condition instanceof IsNullPredicate) {
                 IsNullPredicate isNullPredicate = (IsNullPredicate) condition;
diff --git a/regression-test/suites/delete_p0/test_delete.groovy 
b/regression-test/suites/delete_p0/test_delete.groovy
index 41aab3b4ab..c19884b411 100644
--- a/regression-test/suites/delete_p0/test_delete.groovy
+++ b/regression-test/suites/delete_p0/test_delete.groovy
@@ -240,4 +240,14 @@ suite("test_delete") {
     sql 'delete from test1 where length(x)=2'
     
     qt_delete_fn 'select * from test1 order by x'
+    
+    sql 'truncate table test1'
+
+    sql 'insert into test1 values("a", "a"), ("bb", "bb"), ("ccc", "ccc")'
+    sql 'delete from test1 where length(id) >= 2'
+
+    test {
+        sql 'select * from test1 order by x'
+        result([['a', 'a']])
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to