{"record":{"id":"7d966ee9cb0f56e4","repo":"alibaba/nacos","slug":"unsupported-sql-s-nacos-only-support-dml-and-so","errorCode":null,"errorMessage":"Unsupported SQL: %s. Nacos only support DML and some DDL SQL.","messagePattern":"Unsupported SQL: (.+?)\\. Nacos only support DML and some DDL SQL\\.","errorType":"validation","errorClass":"SQLException","httpStatus":null,"severity":"error","filePath":"persistence/src/main/java/com/alibaba/nacos/persistence/repository/embedded/sql/limiter/SqlTypeLimiter.java","lineNumber":133,"sourceCode":"        }\n        if (!allowedDdlSqls.contains(firstToken)) {\n            throwException(trimmedSql);\n        }\n        checkSqlForSecondToken(firstTokenIndex, trimmedSql);\n    }\n    \n    @Override\n    public void doLimit(List<String> sql) throws SQLException {\n        if (null == sql || !enabledLimit) {\n            return;\n        }\n        for (String each : sql) {\n            doLimit(each);\n        }\n    }\n    \n    private void throwException(String sql) throws SQLException {\n        throw new SQLException(\n            String.format(\"Unsupported SQL: %s. Nacos only support DML and some DDL SQL.\", sql));\n    }\n    \n    private void checkSqlForSecondToken(int firstTokenIndex, String trimmedSql)\n        throws SQLException {\n        int secondTokenIndex = trimmedSql.indexOf(\" \", firstTokenIndex + 1);\n        if (-1 == secondTokenIndex) {\n            secondTokenIndex = trimmedSql.length();\n        }\n        String secondToken =\n            trimmedSql.substring(firstTokenIndex + 1, secondTokenIndex).toUpperCase();\n        if (!allowedDdlScopes.contains(secondToken)) {\n            throwException(trimmedSql);\n        }\n    }\n}\n","sourceCodeStart":115,"sourceCodeEnd":150,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/persistence/src/main/java/com/alibaba/nacos/persistence/repository/embedded/sql/limiter/SqlTypeLimiter.java#L115-L150","documentation":"Thrown by SqlTypeLimiter.throwException (private) as a SQLException when an SQL statement's first token is not an allowed DML verb (INSERT/UPDATE/DELETE/SELECT) nor an allowed DDL verb (CREATE/ALTER), or when CREATE/ALTER is followed by a second token not in {SCHEMA, TABLE, INDEX}. The limiter guards embedded Derby SQL execution and is enabled by default (nacos.persistence.sql.derby.limit.enabled=true).","triggerScenarios":"Executing SQL against the embedded Derby store whose leading verb is disallowed (e.g. DROP, TRUNCATE, GRANT), or a CREATE/ALTER whose target is not SCHEMA/TABLE/INDEX (e.g. CREATE VIEW). Reached via ModifyRequest/SelectRequest SQL passed to databaseOperate.","commonSituations":"Custom plugins or maintenance code issuing raw SQL to the embedded store; migrations that use DROP or DCL; trying to run a CREATE PROCEDURE or CREATE VIEW which the limiter rejects.","solutions":["Restrict SQL to INSERT/UPDATE/DELETE/SELECT, or CREATE SCHEMA/TABLE/INDEX, or ALTER TABLE.","If you legitimately need a disallowed statement, re-evaluate: the limiter is a safety control for embedded Derby, not a toggle for arbitrary DDL.","To disable the limiter for a controlled maintenance operation set nacos.persistence.sql.derby.limit.enabled=false (do this only in a trusted, isolated maintenance window)."],"exampleFix":"// before: DROP is not allowed by the limiter\ndoLimit(\"DROP TABLE config_info\");\n\n// after: use the supported DDL scope, or disable limiter for trusted maintenance\n// only if strictly necessary\n// System.setProperty(\"nacos.persistence.sql.derby.limit.enabled\", \"false\");","handlingStrategy":"validation","validationCode":"private static final Set<String> OK_VERBS = Set.of(\n    \"INSERT\",\"UPDATE\",\"DELETE\",\"SELECT\",\"CREATE\",\"ALTER\");\nprivate static final Set<String> OK_DDL_SCOPE = Set.of(\"SCHEMA\",\"TABLE\",\"INDEX\");\nstatic boolean isAllowedByLimiter(String sql) {\n    String t = sql.trim().toUpperCase();\n    String first = t.contains(\" \") ? t.substring(0, t.indexOf(' ')) : t;\n    if (!OK_VERBS.contains(first)) return false;\n    if (Set.of(\"CREATE\",\"ALTER\").contains(first)) {\n        int s = t.indexOf(' '), e = t.indexOf(' ', s + 1);\n        String second = t.substring(s + 1, e < 0 ? t.length() : e);\n        return OK_DDL_SCOPE.contains(second);\n    }\n    return true;\n}","typeGuard":"static boolean isAllowedByLimiter(String sql) { /* see validationCode */ return false; }","tryCatchPattern":"try {\n    databaseOperate.update(sqlCtx);\n} catch (SQLException e) {\n    if (e.getMessage().startsWith(\"Unsupported SQL:\")) {\n        // rejected by SqlTypeLimiter; do not retry with the same SQL\n        throw new IllegalStateException(\"blocked SQL\", e);\n    }\n    throw e;\n}","preventionTips":["Only issue INSERT/UPDATE/DELETE/SELECT or CREATE SCHEMA/TABLE/INDEX or ALTER TABLE to embedded Derby.","Do not send DROP/TRUNCATE/GRANT/DCL through the embedded store.","Keep nacos.persistence.sql.derby.limit.enabled=true in production."],"tags":["persistence","embedded","sql","limiter","security"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}