MyCATApache/Mycat-Server · error · SQLNonTransientException
"ChildTable multi insert not provided"
Error message
"ChildTable multi insert not provided"
What it means
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.
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
Example fix
// before INSERT INTO order_detail (id, order_id, item) VALUES (1,100,'a'),(2,100,'b'); // after INSERT INTO order_detail (id, order_id, item) VALUES (1,100,'a'); INSERT INTO order_detail (id, order_id, item) VALUES (2,100,'b');
Defensive patterns
Strategy: validation
Validate before calling
// refuse multi-row inserts into child tables before routing
if (extractInsertColumns(sql) != null && countValuesTuples(sql) > 1 && tableConfig.isChildTable()) {
throw new IllegalArgumentException("Child table inserts must be single-row; split the batch");
} Type guard
static boolean isSingleRowInsert(MySqlInsertStatement ins) {
return ins.getValuesList() == null || ins.getValuesList().size() <= 1;
} Try / catch
try {
insertBatch(rows);
} catch (SQLNonTransientException e) {
if ("ChildTable multi insert not provided".equals(e.getMessage())) {
// fall back to per-row inserts inside a transaction
rows.forEach(this::insertSingle);
return;
}
throw e;
} Prevention
- 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
When it happens
Trigger: `INSERT INTO child_table (..., joinKey, ...) VALUES (...), (...), ...` — a batch/multi-row insert — into a table marked as childTable in schema.xml.
Common situations: 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.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- "joinKey not provided :" + tc.getJoinKey()+ "," + insertStmt
- "bad insert sql columnSize != valueSize:" + columnNum + "…
- bad insert sql columnSize != valueSize:values:
- TODO:insert into .... select .... not supported!
- "create table from other table not supported :" + stmt
AI-assisted analysis of MyCATApache/Mycat-Server@65f8d8beb7 (2026-09-11).
Data as JSON: /api/errors/a817f49ff411e56c.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/io/mycat/route/parser/druid/impl/DruidInsertParser.java:133
private boolean isMultiInsert(MySqlInsertStatement insertStmt) {
return (insertStmt.getValuesList() != null && insertStmt.getValuesList().size() > 1) || insertStmt.getQuery() != null;
}
private RouteResultset parserChildTable(SchemaConfig schema, RouteResultset rrs,
String tableName, MySqlInsertStatement insertStmt) throws SQLNonTransientException {
TableConfig tc = schema.getTables().get(tableName);
String joinKey = tc.getJoinKey();
int joinKeyIndex = getJoinKeyIndex(insertStmt.getColumns(), joinKey);
if(joinKeyIndex == -1) {
String inf = "joinKey not provided :" + tc.getJoinKey()+ "," + insertStmt;
LOGGER.warn(inf);
throw new SQLNonTransientException(inf);
}
if(isMultiInsert(insertStmt)) {
String msg = "ChildTable multi insert not provided" ;
LOGGER.warn(msg);
throw new SQLNonTransientException(msg);
}
String joinKeyVal = insertStmt.getValues().getValues().get(joinKeyIndex).toString();
String sql = insertStmt.toString();
// try to route by ER parent partion key
RouteResultset theRrs = RouterUtil.routeByERParentKey(null,schema, ServerParse.INSERT,sql, rrs, tc,joinKeyVal);
if (theRrs != null) {
rrs.setFinishedRoute(true);
return theRrs;
}
// route by sql query root parent's datanode
String findRootTBSql = tc.getLocateRTableKeySql().toLowerCase() + joinKeyVal;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("find root parent's node sql "+ findRootTBSql);View on GitHub (pinned to 65f8d8beb7)