MyCATApache/Mycat-Server · error · java.sql.SQLNonTransientException
ChildTable multi insert not provided
Error message
ChildTable multi insert not provided
What it means
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.
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
Example fix
// before INSERT INTO order_detail(order_id,item) VALUES (1,'a'),(2,'b'); // after INSERT INTO order_detail(order_id,item) VALUES (1,'a'); INSERT INTO order_detail(order_id,item) VALUES (2,'b');
Defensive patterns
Strategy: validation
Validate before calling
// Split multi-row inserts into single-row statements before sending to a child table
List<String> singles = valuesTuples.stream()
.map(t -> "INSERT INTO " + table + " (" + cols + ") VALUES (" + t + ")")
.collect(toList()); Try / catch
catch (SQLNonTransientException e) { if (e.getMessage().equals("ChildTable multi insert not provided")) { /* fallback: per-row insert loop */ } throw e; } Prevention
- 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
When it happens
Trigger: INSERT INTO child (...) VALUES (...),(...)... or INSERT ... SELECT ... against a table configured as childTable with a joinKey; RouterUtil.routeByER's isMultiInsert check.
Common situations: 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.
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
- batch not supported
- joinKey not provided :
- ChildTable multi insert not provided
- can't find (root) parent sharding node for sql:
AI-assisted analysis of MyCATApache/Mycat-Server@65f8d8beb7 (2026-09-11).
Data as JSON: /api/errors/939c55effe523c01.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/io/mycat/route/util/RouterUtil.java:1937
String sql = insertStmt.toString();
final RouteResultset rrs = new RouteResultset(sql, ServerParse.INSERT);
String joinKey = tc.getJoinKey();
//因为是Insert语句,用MySqlInsertStatement进行parse
// MySqlInsertStatement insertStmt = (MySqlInsertStatement) (new MySqlStatementParser(origSQL)).parseInsert();
//判断条件完整性,取得解析后语句列中的joinkey列的index
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);
}
//取得joinkey的值
String joinKeyVal = insertStmt.getValues().getValues().get(joinKeyIndex).toString();
//解决bug #938,当关联字段的值为char类型时,去掉前后"'"
String realVal = joinKeyVal;
if (joinKeyVal.startsWith("'") && joinKeyVal.endsWith("'") && joinKeyVal.length() > 2) {
realVal = joinKeyVal.substring(1, joinKeyVal.length() - 1);
}
// try to route by ER parent partion key
//如果是二级子表(父表不再有父表),并且分片字段正好是joinkey字段,调用routeByERParentKey
RouteResultset theRrs = RouterUtil.routeByERParentKey(sc, schema, ServerParse.INSERT, sql, rrs, tc, realVal);
if (theRrs != null) {
boolean processedInsert=false;
//判断是否需要全局序列号
if ( sc!=null && tc.isAutoIncrement()) {
String primaryKey = tc.getPrimaryKey();
processedInsert=processInsert(sc,schema,ServerParse.INSERT,sql,tc.getName(),primaryKey);
View on GitHub (pinned to 65f8d8beb7)