MyCATApache/Mycat-Server · error · SQLNonTransientException

"TODO:insert into .... select .... not supported!"

Error message

"TODO:insert into .... select .... not supported!"

What it means

Mycat does not implement routing for INSERT INTO ... SELECT .... parserBatchInsert detects insertStmt.getQuery() != null and throws SQLNonTransientException with a TODO message, marking the feature as unimplemented.

Solutions

  1. Split into SELECT the data in the application, then batch INSERT the rows via Mycat
  2. Run the INSERT...SELECT directly on the backend MySQL node(s) if both tables live on the same datanode
  3. Precompute the SELECT into a temp staging file/table and insert from the application
  4. Check newer Mycat forks/versions that may add INSERT...SELECT support

Example fix

// before
INSERT INTO t2 (a,b) SELECT a,b FROM t1 WHERE x > 10;
// after
-- app-side: rows = SELECT a,b FROM t1 WHERE x > 10
INSERT INTO t2 (a,b) VALUES (?,?),(?,?); -- batch insert of fetched rows
Defensive patterns

Strategy: try-catch

Validate before calling

// reject INSERT ... SELECT before routing
String normalized = sql.replaceAll("\\s+", " ").toUpperCase();
if (normalized.startsWith("INSERT") && normalized.contains("SELECT")) {
    throw new IllegalArgumentException("INSERT ... SELECT not supported via Mycat; split it");
}

Type guard

static boolean isInsertSelect(SQLStatement stmt) {
    return stmt instanceof MySqlInsertStatement
        && ((MySqlInsertStatement) stmt).getQuery() != null;
}

Try / catch

try {
    execute(sql);
} catch (SQLNonTransientException e) {
    if (e.getMessage().contains("insert into .... select .... not supported")) {
        // fallback: fetch rows client-side, then batch insert
        List<Row> rows = selectAll(sourceSql);
        batchInsert(target, rows);
        return;
    }
    throw e;
}

Prevention

When it happens

Trigger: Executing `INSERT INTO t1 (cols) SELECT ... FROM t2` through Mycat — the insert statement carries a query body instead of VALUES.

Common situations: Data-migration/ETL scripts copying between tables; dedup/archival jobs (INSERT INTO archive SELECT ...); porting stored-procedure-style SQL from plain MySQL to Mycat.

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


AI-assisted analysis of MyCATApache/Mycat-Server@65f8d8beb7 (2026-09-11). Data as JSON: /api/errors/556176c80b673018. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/io/mycat/route/parser/druid/impl/DruidInsertParser.java:324

						nodes[count] = new RouteResultsetNode(tableConfig.getDataNodes().get(nodeIndex),
								rrs.getSqlType(),insertStmt.toString());
					}
					
					if(algorithm instanceof SlotFunction) {
						nodes[count].setSlot(slotsMap.get(nodeIndex));
						nodes[count].setStatement(ParseUtil.changeInsertAddSlot(nodes[count].getStatement(),nodes[count].getSlot()));
					}
					nodes[count++].setSource(rrs);

				}
				rrs.setNodes(nodes);
				rrs.setFinishedRoute(true);

			}
		} else if(insertStmt.getQuery() != null) { // insert into .... select ....
			String msg = "TODO:insert into .... select .... not supported!";
			LOGGER.warn(msg);
			throw new SQLNonTransientException(msg);
		}
	}

	private String getShardingValue(SQLExpr expr) throws SQLNonTransientException {
		String shardingValue = null;
		if(expr instanceof SQLIntegerExpr) {
			SQLIntegerExpr intExpr = (SQLIntegerExpr)expr;
			shardingValue = intExpr.getNumber() + "";
		} else if (expr instanceof SQLCharExpr) {
			SQLCharExpr charExpr = (SQLCharExpr)expr;
			shardingValue = charExpr.getText();
		} else if (expr instanceof SQLMethodInvokeExpr) {
			SQLMethodInvokeExpr methodInvokeExpr = (SQLMethodInvokeExpr)expr;
			try {
				shardingValue = tryInvokeSQLMethod(methodInvokeExpr);
			}catch (Exception e){
				LOGGER.error("",e);
			}

View on GitHub (pinned to 65f8d8beb7)