{"record":{"id":"939c55effe523c01","repo":"MyCATApache/Mycat-Server","slug":"childtable-multi-insert-not-provided-939c55","errorCode":null,"errorMessage":"ChildTable multi insert not provided","messagePattern":"ChildTable multi insert not provided","errorType":"exception","errorClass":"java.sql.SQLNonTransientException","httpStatus":null,"severity":"error","filePath":"src/main/java/io/mycat/route/util/RouterUtil.java","lineNumber":1937,"sourceCode":"\r\n\t\t\t\tString sql = insertStmt.toString();\r\n\r\n\t\t\t\tfinal RouteResultset rrs = new RouteResultset(sql, ServerParse.INSERT);\r\n\t\t\t\tString joinKey = tc.getJoinKey();\r\n\t\t\t\t//因为是Insert语句，用MySqlInsertStatement进行parse\r\n//\t\t\t\tMySqlInsertStatement insertStmt = (MySqlInsertStatement) (new MySqlStatementParser(origSQL)).parseInsert();\r\n\t\t\t\t//判断条件完整性，取得解析后语句列中的joinkey列的index\r\n\t\t\t\tint joinKeyIndex = getJoinKeyIndex(insertStmt.getColumns(), joinKey);\r\n\t\t\t\tif (joinKeyIndex == -1) {\r\n\t\t\t\t\tString inf = \"joinKey not provided :\" + tc.getJoinKey() + \",\" + insertStmt;\r\n\t\t\t\t\tLOGGER.warn(inf);\r\n\t\t\t\t\tthrow new SQLNonTransientException(inf);\r\n\t\t\t\t}\r\n\t\t\t\t//子表不支持批量插入\r\n\t\t\t\tif (isMultiInsert(insertStmt)) {\r\n\t\t\t\t\tString msg = \"ChildTable multi insert not provided\";\r\n\t\t\t\t\tLOGGER.warn(msg);\r\n\t\t\t\t\tthrow new SQLNonTransientException(msg);\r\n\t\t\t\t}\r\n\t\t\t\t//取得joinkey的值\r\n\t\t\t\tString joinKeyVal = insertStmt.getValues().getValues().get(joinKeyIndex).toString();\r\n\t\t\t\t//解决bug #938，当关联字段的值为char类型时，去掉前后\"'\"\r\n\t\t\t\tString realVal = joinKeyVal;\r\n\t\t\t\tif (joinKeyVal.startsWith(\"'\") && joinKeyVal.endsWith(\"'\") && joinKeyVal.length() > 2) {\r\n\t\t\t\t\trealVal = joinKeyVal.substring(1, joinKeyVal.length() - 1);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// try to route by ER parent partion key\r\n\t\t\t\t//如果是二级子表（父表不再有父表）,并且分片字段正好是joinkey字段，调用routeByERParentKey\r\n\t\t\t\tRouteResultset theRrs = RouterUtil.routeByERParentKey(sc, schema, ServerParse.INSERT, sql, rrs, tc, realVal);\r\n\t\t\t\tif (theRrs != null) {\r\n\t\t\t\t\tboolean processedInsert=false;\r\n\t\t\t\t\t//判断是否需要全局序列号\r\n\t\t\t\t\tif ( sc!=null && tc.isAutoIncrement()) {\r\n\t\t\t\t\t\tString primaryKey = tc.getPrimaryKey();\r\n\t\t\t\t\t\tprocessedInsert=processInsert(sc,schema,ServerParse.INSERT,sql,tc.getName(),primaryKey);\r","sourceCodeStart":1919,"sourceCodeEnd":1955,"githubUrl":"https://github.com/MyCATApache/Mycat-Server/blob/65f8d8beb752f935752f2a0eec0ab017facab9ef/src/main/java/io/mycat/route/util/RouterUtil.java#L1919-L1955","documentation":"Child (ER) tables support only single-row inserts: routing depends on one joinKey value taken from the single VALUES tuple. isMultiInsert detects multi-row VALUES or insert-from-select, and Mycat refuses with SQLNonTransientException because it cannot route a batch to potentially multiple parent nodes in one statement.","triggerScenarios":"INSERT INTO child (...) VALUES (...),(...)... or INSERT ... SELECT ... against a table configured as childTable with a joinKey; RouterUtil.routeByER's isMultiInsert check.","commonSituations":"Bulk data-load scripts and seed scripts using multi-row inserts; ORM batch inserts with per-row values; migration tools (mysqldump replay) inserting many rows per statement into ER child tables.","solutions":["Rewrite as one single-row INSERT per values tuple (batch them as separate statements/pipelined requests)","Use a stored procedure or Mycat-compatible bulk path for loads, inserting child rows one at a time","Temporarily disable the ER (childTable) config and shard the table directly if single-row inserts are unacceptable","Split the application's batch insert into a loop sending individual statements"],"exampleFix":"// before\nINSERT INTO order_detail(order_id,item) VALUES (1,'a'),(2,'b');\n// after\nINSERT INTO order_detail(order_id,item) VALUES (1,'a');\nINSERT INTO order_detail(order_id,item) VALUES (2,'b');","handlingStrategy":"validation","validationCode":"// Split multi-row inserts into single-row statements before sending to a child table\nList<String> singles = valuesTuples.stream()\n    .map(t -> \"INSERT INTO \" + table + \" (\" + cols + \") VALUES (\" + t + \")\")\n    .collect(toList());","typeGuard":null,"tryCatchPattern":"catch (SQLNonTransientException e) { if (e.getMessage().equals(\"ChildTable multi insert not provided\")) { /* fallback: per-row insert loop */ } throw e; }","preventionTips":["Detect child-table inserts in your DAO layer and always emit single-row statements","Avoid replaying mysqldump multi-row inserts into ER child tables unmodified","Document ER-table insert limitations in the team's DB guidelines"],"tags":["er-table","insert","batch","unsupported-operation","child-table"],"backgroundTag":"unsupported-operation","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"}