public static int readValueArray(String args, int startIndex, int len, Consumer<String> func) {
        char closeBracket = ']';
        StringBuilder builder = new StringBuilder();
        for (int i = startIndex; i < len; i++) {
            char ch = args.charAt(i);
            if (ch == '[') {
                startIndex = i + 1;
                break;
            } else if ((ch == '-' || ch == '/') && i + 1 < len) {
                char nextCh = args.charAt(i + 1);
                if (ch == '-' && nextCh == '-') {
                    i = skipSingleLineComment(args, i + 2, len) - 1;
                } else if (ch == '/' && nextCh == '*') {
                    i = skipMultiLineComment(args, i + 2, len) - 1;
                } else {
                    startIndex = i;
                    break;
                }
            } else if (!Character.isWhitespace(ch)) {
                startIndex = i;
                break;
            }
        }

        boolean hasNext = false;
        for (int i = startIndex; i < len; i++) {
            char ch = args.charAt(i);
            if (Character.isWhitespace(ch)) {
                continue;
            } else if (ch == '\'') { // string
                hasNext = false;
                int endIndex = readNameOrQuotedString(args, i, len, builder);
                func.accept(unescape(args.substring(i, endIndex)));
                builder.setLength(0);
                i = endIndex + 1;
            } else if (ch == '[') { // array
                hasNext = false;
                int endIndex = skipContentsUntil(args, i + 1, len, ']');
                func.accept(args.substring(i, endIndex));
                builder.setLength(0);
                i = endIndex;
            } else if (ch == '(') { // tuple
                hasNext = false;
                int endIndex = skipContentsUntil(args, i + 1, len, ')');
                func.accept(args.substring(i, endIndex));
                builder.setLength(0);
                i = endIndex;
            } else if (ch == closeBracket) {
                len = i + 1;
                break;
            } else if (ch == ',') {
                hasNext = true;
                String str = builder.toString();
                func.accept(str.isEmpty() || ClickHouseValues.NULL_EXPR.equalsIgnoreCase(str) ? null : str);
                builder.setLength(0);
            } else if ((ch == '-' || ch == '/') && i + 1 < len) {
                char nextCh = args.charAt(i + 1);
                if (ch == '-' && nextCh == '-') {
                    i = skipSingleLineComment(args, i + 2, len) - 1;
                } else if (ch == '/' && nextCh == '*') {
                    i = skipMultiLineComment(args, i + 2, len) - 1;
                } else {
                    builder.append(ch);
                }
            } else {
                builder.append(ch);
            }
        }

        if (hasNext || builder.length() > 0) {
            String str = builder.toString();
            func.accept(str.isEmpty() || ClickHouseValues.NULL_EXPR.equalsIgnoreCase(str) ? null : str);
        }

        return len;
    }
Skip quoted string, comments, and brackets until seeing all the given
{@code keywords}(with only whitespaces or comments in between) or reaching
end of the given string.

@param args          non-null string to scan
@param startIndex    start index
@param len           end index, usually length of the given string
@param keywords      keywords, null or empty one means any character
@param caseSensitive whether keyword is case sensitive or not
@return index of {@code endChar} or {@code len}
clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseUtils.java