{"record":{"id":"368c7f27b33734c4","repo":"apache/shardingsphere","slug":"only-one-sql-statement-is-allowed","errorCode":null,"errorMessage":"Only one SQL statement is allowed.","messagePattern":"Only one SQL statement is allowed\\.","errorType":"exception","errorClass":"MCPMultipleSQLStatementsException","httpStatus":null,"severity":"error","filePath":"mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementScanner.java","lineNumber":239,"sourceCode":"                continue;\n            }\n            if (startsWithExecutableCommentMarker(text.substring(currentIndex))) {\n                return true;\n            }\n            int commentEndIndex = text.indexOf(\"*/\", currentIndex + 2);\n            currentIndex = -1 == commentEndIndex ? text.length() : commentEndIndex + 2;\n        }\n        return false;\n    }\n    \n    private int findStatementEndIndex(final List<Token> tokens, final int sqlLength) {\n        for (int index = 0; index < tokens.size(); index++) {\n            Token each = tokens.get(index);\n            if (!\";\".equals(each.getText())) {\n                continue;\n            }\n            if (index + 1 < tokens.size()) {\n                throw new MCPMultipleSQLStatementsException();\n            }\n            return each.getStartIndex();\n        }\n        return sqlLength;\n    }\n    \n    private List<Token> getTokensBefore(final List<Token> tokens, final int stopIndex) {\n        List<Token> result = new ArrayList<>();\n        for (Token each : tokens) {\n            if (each.getStartIndex() < stopIndex) {\n                result.add(each);\n            }\n        }\n        return result;\n    }\n    \n    private List<Token> getVisibleTokens(final String sql, final List<Token> tokens, final boolean lineCommentsHandledByLexer) {\n        List<Token> result = new ArrayList<>();","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/apache/shardingsphere/blob/e952770a215630a3659c75d64369168cd3e26b82/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementScanner.java#L221-L257","documentation":"Thrown by SQLStatementScanner.findStatementEndIndex as MCPMultipleSQLStatementsException when a `;` token is followed by any further token. The MCP execute tool intentionally accepts exactly one statement per call; a trailing semicolon is fine only if nothing (except lexer noise already filtered) comes after it.","triggerScenarios":"Any submitted SQL containing a `;` with subsequent tokens — e.g. `SELECT 1; SELECT 2`, `INSERT ...; COMMIT;`, or `SELECT 1; ` followed by a comment token that was not filtered. The loop throws at the first `;` that is not the last visible token.","commonSituations":"Batch scripts and migration files replayed whole into execute_sql; agents concatenating tool outputs with semicolons; clients that always append `;` plus a trailing comment.","solutions":["Split the batch on statement boundaries and submit each statement as its own execute_sql call","Strip trailing semicolons and anything after them (including trailing comments) before submission","Use a purpose-built batch/import path if you truly need multi-statement execution"],"exampleFix":"// before\nexecute_sql(\"UPDATE t SET a=1; UPDATE t SET b=2;\")\n\n// after\nexecute_sql(\"UPDATE t SET a=1\")\nexecute_sql(\"UPDATE t SET b=2\")","handlingStrategy":"validation","validationCode":"// Exactly one statement: only one ';', and it must be last\nString trimmed = sql.strip();\nlong semicolons = trimmed.chars().filter(c -> c == ';').count();\nboolean trailingOnly = trimmed.endsWith(\";\") && trimmed.indexOf(';') == trimmed.length() - 1;\nif (semicolons > 1 || (semicolons == 1 && !trailingOnly)) throw new IllegalArgumentException(\"Split into single statements\");\nif (trailingOnly) sql = trimmed.substring(0, trimmed.length() - 1);","typeGuard":null,"tryCatchPattern":"try {\n    executeSql(sql);\n} catch (MCPMultipleSQLStatementsException e) {\n    for (String each : splitStatements(sql)) executeSql(each);\n}","preventionTips":["Always split batches client-side; the tool accepts exactly one statement","Strip trailing semicolons and trailing comments"],"tags":["mcp","sql-scanner","multiple-statements","shardingsphere"],"backgroundTag":null,"analyzedSha":"e952770a215630a3659c75d64369168cd3e26b82","analyzedAt":"2026-08-14T13:54:53.392Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}