{"record":{"id":"3a2069523a5a9436","repo":"MyCATApache/Mycat-Server","slug":"sql-ast-nodes-type-mismatch","errorCode":null,"errorMessage":"SQL AST nodes type mismatch!","messagePattern":"SQL AST nodes type mismatch!","errorType":"exception","errorClass":"SQLNonTransientException","httpStatus":null,"severity":"error","filePath":"src/main/java/io/mycat/route/parser/druid/impl/DruidUpdateParser.java","lineNumber":180,"sourceCode":"            * 条件中有or或者xor的，如果分片字段出现在or/xor的一个子句中，则此update\n            * 语句无法执行\n             */\n            if ((nodeOpExpr.getOperator() == SQLBinaryOperator.BooleanOr) ||\n                    (nodeOpExpr.getOperator() == SQLBinaryOperator.BooleanXor)) {\n                parentHasOR = true;\n            }\n            // 发现类似 col = value 的子句\n            if (nodeOpExpr.getOperator() == SQLBinaryOperator.Equality) {\n                boolean foundCol;\n                SQLExpr leftExpr = nodeOpExpr.getLeft();\n                SQLExpr rightExpr = nodeOpExpr.getRight();\n\n                foundCol = columnInExpr(leftExpr, column);\n\n                // 发现col = value子句，col刚好是分片字段，比较value与update要更新的值是否一样，并且是否在or/xor子句中\n                if (foundCol) {\n                    if (rightExpr.getClass() != value.getClass()) {\n                        throw new SQLNonTransientException(\"SQL AST nodes type mismatch!\");\n                    }\n\n                    canUpdate = rightExpr.toString().equals(value.toString()) && (!hasOR) && (!parentHasOR);\n                }\n            } else if (nodeOpExpr.getOperator().isLogical()) {\n                if (nodeOpExpr.getLeft() != null) {\n                    if (nodeOpExpr.getLeft() instanceof SQLBinaryOpExpr) {\n                        canUpdate = shardColCanBeUpdated(nodeOpExpr.getLeft(), column, value, parentHasOR);\n                    }\n                    // else\n                    // 此子语句不是 =,>,<等关系运算符(对应的类是SQLBinaryOpExpr)。比如between X and Y\n                    // 或者 NOT，或者单独的子查询，这些情况，我们不做处理\n                }\n                if ((!canUpdate) && nodeOpExpr.getRight() != null) {\n                    if (nodeOpExpr.getRight() instanceof SQLBinaryOpExpr) {\n                        canUpdate = shardColCanBeUpdated(nodeOpExpr.getRight(), column, value, parentHasOR);\n                    }\n                    // else","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/MyCATApache/Mycat-Server/blob/65f8d8beb752f935752f2a0eec0ab017facab9ef/src/main/java/io/mycat/route/parser/druid/impl/DruidUpdateParser.java#L162-L198","documentation":"When a comparison clause in the UPDATE statement matches col = value where col is the sharding column, Mycat checks whether the right side equals the value being SET. It first requires both sides to be the same AST node class; if e.g. the WHERE literal is an integer and the SET value is a string (or vice versa), it throws 'SQL AST nodes type mismatch!' instead of silently allowing or denying the update.","triggerScenarios":"confirmShardColumnNotUpdated -> shardColCanBeUpdated finds leftExpr column equals the partition column, and rightExpr.getClass() != value.getClass() — e.g. WHERE id='5' but SET id=5 (string literal vs SQLIntegerExpr), or any literal/parameter-type difference between the two sides.","commonSituations":"Quoting numeric sharding keys in one clause but not the other; ORM-generated SQL mixing typed and untyped literals; implicit MySQL type coercion assumptions that the Druid AST does not share.","solutions":["Make the literal types consistent: use the same form of the value in the WHERE clause and the SET clause (both quoted or both unquoted)","Quote consistently for string sharding keys, e.g. SET id='5' ... WHERE id='5'","Avoid updating the sharding column at all; update other columns only"],"exampleFix":"// before\nUPDATE t SET id=5 WHERE id='5';\n// after\nUPDATE t SET id='5' WHERE id='5';","handlingStrategy":"validation","validationCode":"// keep literal types consistent between WHERE and SET for the shard key\nString setVal = \"'5'\", whereVal = \"'5'\"; // both quoted for string keys\nif (isNumericShardKey) { setVal = setVal.replace(\"'\",\"\"); whereVal = whereVal.replace(\"'\",\"\"); }","typeGuard":"boolean sameLiteralKind(SQLExpr a, SQLExpr b) {\n    return a.getClass().equals(b.getClass());\n}","tryCatchPattern":"try { router.route(...) } catch (SQLNonTransientException e) { if (\"SQL AST nodes type mismatch!\".equals(e.getMessage())) { normalizeLiteralsAndRetry(); } }","preventionTips":["Quote consistently: string keys always quoted, numeric keys never quoted","Don't update the sharding column in application code","Parameterize values with consistent types from the ORM"],"tags":["mysql","sharding","type-mismatch"],"backgroundTag":"type-mismatch","analyzedSha":"65f8d8beb752f935752f2a0eec0ab017facab9ef","analyzedAt":"2026-09-11T00:12:21.696Z","contentChangedAt":"2026-09-11T00:12:21.696Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}