{"record":{"id":"a817f49ff411e56c","repo":"MyCATApache/Mycat-Server","slug":"childtable-multi-insert-not-provided-a817f4","errorCode":null,"errorMessage":"\"ChildTable multi insert not provided\"","messagePattern":"\"ChildTable multi insert not provided\"","errorType":"exception","errorClass":"SQLNonTransientException","httpStatus":null,"severity":"error","filePath":"src/main/java/io/mycat/route/parser/druid/impl/DruidInsertParser.java","lineNumber":133,"sourceCode":"\tprivate boolean isMultiInsert(MySqlInsertStatement insertStmt) {\n\t\treturn (insertStmt.getValuesList() != null && insertStmt.getValuesList().size() > 1) || insertStmt.getQuery() != null;\n\t}\n\t\n\tprivate RouteResultset parserChildTable(SchemaConfig schema, RouteResultset rrs,\n\t\t\tString tableName, MySqlInsertStatement insertStmt) throws SQLNonTransientException {\n\t\tTableConfig tc = schema.getTables().get(tableName);\n\t\t\n\t\tString joinKey = tc.getJoinKey();\n\t\tint joinKeyIndex = getJoinKeyIndex(insertStmt.getColumns(), joinKey);\n\t\tif(joinKeyIndex == -1) {\n\t\t\tString inf = \"joinKey not provided :\" + tc.getJoinKey()+ \",\" + insertStmt;\n\t\t\tLOGGER.warn(inf);\n\t\t\tthrow new SQLNonTransientException(inf);\n\t\t}\n\t\tif(isMultiInsert(insertStmt)) {\n\t\t\tString msg = \"ChildTable multi insert not provided\" ;\n\t\t\tLOGGER.warn(msg);\n\t\t\tthrow new SQLNonTransientException(msg);\n\t\t}\n\t\t\n\t\tString joinKeyVal = insertStmt.getValues().getValues().get(joinKeyIndex).toString();\n\n\t\t\n\t\tString sql = insertStmt.toString();\n\t\t\n\t\t// try to route by ER parent partion key\n\t\tRouteResultset theRrs = RouterUtil.routeByERParentKey(null,schema, ServerParse.INSERT,sql, rrs, tc,joinKeyVal);\n\t\tif (theRrs != null) {\n\t\t\trrs.setFinishedRoute(true);\n\t\t\treturn theRrs;\n\t\t}\n\n\t\t// route by sql query root parent's datanode\n\t\tString findRootTBSql = tc.getLocateRTableKeySql().toLowerCase() + joinKeyVal;\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"find root parent's node sql \"+ findRootTBSql);","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/MyCATApache/Mycat-Server/blob/65f8d8beb752f935752f2a0eec0ab017facab9ef/src/main/java/io/mycat/route/parser/druid/impl/DruidInsertParser.java#L115-L151","documentation":"Mycat's child-table (ER partition) insert routing only supports single-row inserts. When parserChildTable detects a multi-row VALUES clause (isMultiInsert), it cannot distribute individual rows per parent lookup and throws SQLNonTransientException.","triggerScenarios":"`INSERT INTO child_table (..., joinKey, ...) VALUES (...), (...), ...` — a batch/multi-row insert — into a table marked as childTable in schema.xml.","commonSituations":"ORM batch flush (Hibernate/JDBC batch rewriting as multi-values); bulk data seeding scripts copying many child rows at once; MyBatis foreach-generated multi-row inserts.","solutions":["Split the multi-row insert into separate single-row INSERT statements, one per child row","If the table does not truly need ER partitioning, remove isChildTable/joinKey config and use normal sharding (then batch inserts go through parserBatchInsert)","Perform the batch insert directly on backend nodes when the parent node is known","Wrap rows in a transaction of individual inserts to keep atomicity"],"exampleFix":"// before\nINSERT INTO order_detail (id, order_id, item) VALUES (1,100,'a'),(2,100,'b');\n// after\nINSERT INTO order_detail (id, order_id, item) VALUES (1,100,'a');\nINSERT INTO order_detail (id, order_id, item) VALUES (2,100,'b');","handlingStrategy":"validation","validationCode":"// refuse multi-row inserts into child tables before routing\nif (extractInsertColumns(sql) != null && countValuesTuples(sql) > 1 && tableConfig.isChildTable()) {\n    throw new IllegalArgumentException(\"Child table inserts must be single-row; split the batch\");\n}","typeGuard":"static boolean isSingleRowInsert(MySqlInsertStatement ins) {\n    return ins.getValuesList() == null || ins.getValuesList().size() <= 1;\n}","tryCatchPattern":"try {\n    insertBatch(rows);\n} catch (SQLNonTransientException e) {\n    if (\"ChildTable multi insert not provided\".equals(e.getMessage())) {\n        // fall back to per-row inserts inside a transaction\n        rows.forEach(this::insertSingle);\n        return;\n    }\n    throw e;\n}","preventionTips":["Split child-table batches into single-row inserts within a transaction","Configure ORMs (MyBatis foreach, Hibernate JDBC batch) not to rewrite into multi-values for child tables","Check isChildTable config before choosing a batch-insert strategy"],"tags":["sql","mycat","er-partition","batch-insert"],"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"}