#6731·druid

[Bug] MySqlLexer.isIdentifierChar throws ArrayIndexOutOfBoundsException when char == 256 (U+0100)

Author: walnut-tomCreated Sep 29, 2026Updated Sep 29, 2026

Description

MySqlLexer.isIdentifierChar(char) has an off-by-one bounds check that throws ArrayIndexOutOfBoundsException when the input character has the exact value 256 (U+0100 Ā).

identifierFlags is declared with a fixed size of 256:

java
private static final boolean[] identifierFlags = new boolean[256];

and the method guards the array access with <=:

java
public static boolean isIdentifierChar(char c) {
    if (c <= identifierFlags.length) {   // identifierFlags.length == 256
        return identifierFlags[c];        // c == 256 -> index out of bounds
    }
    return c != ' ' && c != ',';
}

Valid indices are 0..255, but c <= 256 lets c == 256 through to the array access, throwing:

java.lang.ArrayIndexOutOfBoundsException: Index 256 out of bounds for length 256

A single such statement aborts the whole parse (the exception is a RuntimeException, not a ParserException), which is especially harmful in Kafka-listener / streaming SQL-sniffing use cases where one malformed statement crashes the listener thread.

Which positions trigger it

Only U+0100 in an identifier position (table / column / alias) triggers the crash; the same character inside a string literal or comment is fine:

Position Example Result
String literal WHERE name = 'Ā' OK
Double-quoted string WHERE name = "Ā" OK
Block comment /* Ā */ OK
Line comment -- Ā OK
Column SELECT Ā FROM t throws
WHERE left-hand column WHERE Ā = 1 throws
Table FROM tĀ throws
Alias FROM t AS Ā throws

And only the exact code point 256 is affected (neighboring values are fine):

Code point Result
U+00FF (255) OK
U+0100 (256) throws
U+0101 (257) OK
CJK e.g. 张 (U+5F20) OK

Steps to Reproduce

java
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;

public class Repro {
    public static void main(String[] args) {
        String sql = "select * from t\u0100 where id = 1"; // \u0100 == U+0100 'Ā'
        SQLUtils.parseStatements(sql, DbType.mysql);
    }
}

Observed (druid 1.2.28):

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 256 out of bounds for length 256
	at com.alibaba.druid.sql.dialect.mysql.parser.MySqlLexer.isIdentifierChar(MySqlLexer.java:964)
	at com.alibaba.druid.sql.dialect.mysql.parser.MySqlLexer.scanIdentifier(MySqlLexer.java:445)
	at com.alibaba.druid.sql.parser.Lexer.nextTokenIdent(Lexer.java:684)
	at com.alibaba.druid.sql.dialect.mysql.parser.MySqlSelectParser.parseFrom(MySqlSelectParser.java:68)
	at com.alibaba.druid.sql.dialect.mysql.parser.MySqlSelectParser.query(MySqlSelectParser.java:237)
	at com.alibaba.druid.sql.parser.SQLSelectParser.select(SQLSelectParser.java:78)
	at com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser.parseSelect(MySqlStatementParser.java:120)
	at com.alibaba.druid.sql.parser.SQLStatementParser.parseStatementList(SQLStatementParser.java:238)
	at com.alibaba.druid.sql.SQLUtils.parseStatements(SQLUtils.java:668)
	at com.alibaba.druid.sql.SQLUtils.parseStatements(SQLUtils.java:689)
	at Repro.main(Repro.java:7)

Expected Behavior

Parsing should not throw; the U+0100 character should fall through to the default branch (return c != ' ' && c != ',';).

Environment

  • Druid version: 1.2.27 and 1.2.28 (both affected; isIdentifierChar is unchanged between them)
  • Java: 21
  • DbType: mysql

Suggested Fix

Change the bounds check from <= to <:

java
public static boolean isIdentifierChar(char c) {
    if (c < identifierFlags.length) {
        return identifierFlags[c];
    }
    return c != ' ' && c != ',';
}

(or equivalently use c >= identifierFlags.length on the fallback side).