jd-opensource/joyagent-jdgenie · error · RuntimeException

请检查sql是否正确

Error message

请检查sql是否正确

What it means

parseSelectSql pre-validates the input with isSelectSql(sql, dialect) after cleaning the SQL and extracting hints; if the statement is not recognized as a SELECT for the given dialect, it throws this RuntimeException, meaning the SQL text is not a query the parser can process.

Solutions

  1. Print/inspect the cleaned SQL (log at debug shows 待解析sql) to see what actually failed the isSelectSql check
  2. Confirm the statement is a SELECT (or WITH/CTE select) and the dialect parameter matches the SQL flavor
  3. Strip markdown fences, trailing semicolons, and non-SQL text before calling parseSelectSql
  4. If CTE/EXPLAIN statements must be supported, extend the pre-check instead of relying on isSelectSql

Example fix

// before
parseSelectSql(userSql, dialect); // throws 请检查sql是否正确
// after
String cleaned = userSql.replaceAll("```(sql)?|;\\s*$", "").trim();
if (!cleaned.toUpperCase().startsWith("SELECT") && !cleaned.toUpperCase().startsWith("WITH")) {
    throw new IllegalArgumentException("Only SELECT queries are supported");
}
parseSelectSql(cleaned, dialect);
Defensive patterns

Strategy: validation

Validate before calling

String cleaned = sql == null ? "" : sql.trim().replaceFirst("^```(sql)?", "").replaceFirst("```$", "").trim();
if (cleaned.isEmpty() || !(cleaned.toUpperCase().startsWith("SELECT") || cleaned.toUpperCase().startsWith("WITH"))) {
    throw new IllegalArgumentException("Only SELECT statements are supported, got: " + cleaned);
}

Type guard

boolean isReadOnlySelect(String sql) {
    if (sql == null) return false;
    String s = sql.trim().toUpperCase();
    return s.startsWith("SELECT") || s.startsWith("WITH");
}

Try / catch

try {
    SqlModel model = SqlParserUtils.parseSelectSql(sql, dialect);
} catch (RuntimeException e) {
    if ("请检查sql是否正确".equals(e.getMessage())) {
        // surface a user-facing 'please check your SQL' error
    }
}

Prevention

When it happens

Trigger: Calling SqlParserUtils.parseSelectSql with a non-SELECT statement (INSERT/UPDATE/DELETE/DDL/DROP), empty or null SQL after cleanSql strips it, a dialect mismatch that makes isSelectSql's keyword check fail, or a leading comment/CTE/EXPLAIN prefix the isSelectSql check does not recognize.

Common situations: Users paste a write statement into a natural-language-to-SQL chat agent; LLM-generated SQL wrapped in markdown fences or prose that cleanSql does not fully strip; CTE (WITH ...) queries that the isSelectSql heuristic does not treat as selects.

Understand the failure class

Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.

Related errors


AI-assisted analysis of jd-opensource/joyagent-jdgenie@2417e0b8b6 (2026-09-08). Data as JSON: /api/errors/10c45ea872ac1721. Report an issue: GitHub.

Appendix: source

Thrown at genie-backend/src/main/java/com/jd/genie/data/sql/SqlParserUtils.java:396

    private static String hintParse(SqlModel sqlModel, String sql) {
        String regex = "(/\\*\\+)(.*?)(\\*/)";
        Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
        Matcher matcher = pattern.matcher(sql);
        if (matcher.find()) {
            sqlModel.setHint(matcher.group(2));
            return matcher.replaceAll("$1" + SqlModel.HINT_TAG + "$3");
        }
        return sql;
    }

    public static SqlModel parseSelectSql(String sql, String dialect) throws SqlParseException {
        log.debug("待解析sql:{}", sql);
        sql = cleanSql(sql, dialect);
        SqlModel sqlModel = new SqlModel();
        sql = hintParse(sqlModel, sql);

        if (!isSelectSql(sql, dialect)) {
            throw new RuntimeException("请检查sql是否正确");
        }


        sqlModel.setDialect(dialect);
        SqlNode sqlNode = SqlParser.create(sql, parserConfigWithoutQuoted(dialect)).parseQuery();
        SqlSelect selectNode = null;
        sqlModel.setSelectType(sqlNode.getKind().name());
        if (SqlKind.SELECT.equals(sqlNode.getKind())) {
            selectNode = (SqlSelect) sqlNode;
            if (selectNode.getFetch() != null) {
                sqlModel.setFetch(selectNode.getFetch().toString());
            }
        }
        if (SqlKind.ORDER_BY.equals(sqlNode.getKind())) {
            SqlOrderBy orderBy = (SqlOrderBy) sqlNode;
            selectNode = (SqlSelect) orderBy.query;
            List<DataOrderBy> orderByList = parseSelectOrderBy(orderBy.orderList, dialect);
            sqlModel.setOrderByList(orderByList);

View on GitHub (pinned to 2417e0b8b6)