public QueryInfo()

in src/main/core-api/java/com/mysql/cj/QueryInfo.java [91:438]


    public QueryInfo(String sql, Session session, String encoding) {
        if (sql == null) {
            throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("QueryInfo.NullSql"), session.getExceptionInterceptor());
        }

        this.baseQueryInfo = this;

        this.sql = sql;
        this.encoding = encoding;

        boolean noBackslashEscapes = session.getServerSession().isNoBackslashEscapesSet();
        boolean rewriteBatchedStatements = session.getPropertySet().getBooleanProperty(PropertyKey.rewriteBatchedStatements).getValue();
        boolean dontCheckOnDuplicateKeyUpdateInSQL = session.getPropertySet().getBooleanProperty(PropertyKey.dontCheckOnDuplicateKeyUpdateInSQL).getValue();

        this.queryReturnType = getQueryReturnType(this.sql, noBackslashEscapes);
        this.queryLength = this.sql.length();

        StringInspector strInspector = new StringInspector(this.sql, OPENING_MARKERS, CLOSING_MARKERS, OVERRIDING_MARKERS,
                noBackslashEscapes ? SearchMode.__MRK_COM_MYM_HNT_WS : SearchMode.__BSE_MRK_COM_MYM_HNT_WS);

        this.queryStartPos = strInspector.indexOfNextAlphanumericChar(); // Skip comments at the beginning of queries.
        if (this.queryStartPos == -1) {
            this.queryStartPos = this.queryLength;
        } else {
            this.numberOfQueries = 1;
            this.statementFirstChar = Character.toUpperCase(strInspector.getChar());

            // Capture the statement keyword.
            int endStatementKeyword = 0;
            int nextChar = this.queryStartPos;
            StringBuilder sbStatementKeyword = new StringBuilder();
            do {
                sbStatementKeyword.append(Character.toUpperCase(strInspector.getChar()));
                endStatementKeyword = nextChar + 1;
                strInspector.incrementPosition();
                nextChar = strInspector.indexOfNextChar();
            } while (nextChar == endStatementKeyword);
            this.statementKeyword = sbStatementKeyword.toString();
        }

        // Check if should look for LIMIT and OFFSET clauses, i.e., if it is a SELECT or TABLE statement.
        boolean lookForLimitAndOffset = false;

        // Only INSERT and REPLACE statements support multi-values clause rewriting.
        boolean isInsert = false;
        boolean isReplace = false;

        switch (this.statementKeyword) {
            case SELECT_STATEMENT:
            case TABLE_STATEMENT:
                lookForLimitAndOffset = true;
                break;
            case INSERT_STATEMENT:
                isInsert = true;
                break;
            case REPLACE_STATEMENT:
                isReplace = true;
                break;
        }

        // Check if the statement has potential to be rewritten as a multi-values clause statement, i.e., if it is an INSERT or REPLACE statement and
        // 'rewriteBatchedStatements' is enabled.
        boolean rewritableAsMultiValues = (isInsert || isReplace) && rewriteBatchedStatements;

        // Check if should look for ON DUPLICATE KEY UPDATE clause, i.e., if it is an INSERT statement and 'dontCheckOnDuplicateKeyUpdateInSQL' is disabled.
        // 'rewriteBatchedStatements=true' cancels any value specified in 'dontCheckOnDuplicateKeyUpdateInSQL'.
        boolean lookForOnDuplicateKeyUpdate = isInsert && (!dontCheckOnDuplicateKeyUpdateInSQL || rewriteBatchedStatements);

        // Scan placeholders.
        int generalEndpointStart = 0;
        int valuesEndpointStart = 0;
        int valuesClauseBegin = -1;
        boolean valuesClauseBeginFound = false;
        int valuesClauseEnd = -1;
        boolean valuesClauseEndFound = false;
        boolean withinValuesClause = false;
        boolean valueStrMayBeTableName = true;
        boolean matchedLimitClause = false;
        boolean withinLimitClause = false;
        int parensLevel = 0;
        int matchEnd = -1;
        int lastPos = -1;
        char lastChar = 0;

        // Endpoints for the satement's static sections (parts around placeholders).
        ArrayList<Integer> staticEndpoints = new ArrayList<>();

        while (strInspector.indexOfNextChar() != -1) {
            int currPos = strInspector.getPosition();
            char currChar = strInspector.getChar();

            if (currChar == '?') { // Process placeholder.
                valueStrMayBeTableName = false; // At this point a string "VALUE" cannot be a table name.

                this.numberOfPlaceholders++;
                int endpointEnd = strInspector.getPosition();
                staticEndpoints.add(generalEndpointStart);
                staticEndpoints.add(endpointEnd);
                this.placeholderPurposes.add(withinValuesClause ? INSERT_VALUES : withinLimitClause ? LIMIT_AND_OFFSET : GENERIC);
                strInspector.incrementPosition();
                generalEndpointStart = strInspector.getPosition(); // Next section starts after the placeholder.

                if (rewritableAsMultiValues) {
                    if (!valuesClauseBeginFound) { // There's a placeholder before the VALUE[S] clause.
                        rewritableAsMultiValues = false;
                    } else if (valuesClauseEndFound) { // There's a placeholder after the end of the VALUE[S] clause.
                        rewritableAsMultiValues = false;
                    } else if (withinValuesClause) {
                        this.valuesEndpoints.add(valuesEndpointStart);
                        this.valuesEndpoints.add(endpointEnd);
                        valuesEndpointStart = generalEndpointStart;
                    }
                }

            } else if (currChar == ';') { // Multi-query SQL.
                valueStrMayBeTableName = false; // At this point a string "VALUE" cannot be a table name.
                matchedLimitClause = false;
                withinLimitClause = false;

                strInspector.incrementPosition();
                if (strInspector.indexOfNextNonWsChar() != -1) {
                    this.numberOfQueries++;

                    if (rewritableAsMultiValues) {
                        rewritableAsMultiValues = false;
                        valuesClauseBeginFound = false;
                        valuesClauseBegin = -1;
                        valuesClauseEndFound = false;
                        valuesClauseEnd = -1;
                        withinValuesClause = false;
                        parensLevel = 0;
                    }

                    isInsert = false;
                    isReplace = false;
                    // Check if continue looking for ON DUPLICATE KEY UPDATE.
                    if (dontCheckOnDuplicateKeyUpdateInSQL || this.containsOnDuplicateKeyUpdate) {
                        lookForOnDuplicateKeyUpdate = false;
                    } else {
                        isInsert = strInspector.matchesIgnoreCase(INSERT_STATEMENT) != -1;
                        if (isInsert) {
                            strInspector.incrementPosition(INSERT_STATEMENT.length() - 1); // Advance to the end of "INSERT" and capture last character.
                            currPos = strInspector.getPosition();
                            currChar = strInspector.getChar();
                            strInspector.incrementPosition();
                        }
                        lookForOnDuplicateKeyUpdate = isInsert;
                    }

                    // Check if continue looking for LIMIT and OFFSET.
                    if (!isInsert && !isReplace && ((matchEnd = strInspector.matchesIgnoreCase(SELECT_STATEMENT)) != -1
                            || (matchEnd = strInspector.matchesIgnoreCase(TABLE_STATEMENT)) != -1)) {
                        strInspector.incrementPosition(matchEnd - strInspector.getPosition() - 1); // Advance to the end and capture last character.
                        currPos = strInspector.getPosition();
                        currChar = strInspector.getChar();
                        strInspector.incrementPosition();

                        lookForLimitAndOffset = true;
                    } else {
                        lookForLimitAndOffset = false;
                    }
                }

            } else {
                if (rewritableAsMultiValues) {
                    if ((!valuesClauseBeginFound || valueStrMayBeTableName) && strInspector.matchesIgnoreCase(VALUE_CLAUSE) != -1) { // VALUE[S] clause found.
                        boolean leftBounded = currPos > lastPos + 1 || lastChar == ')'; // ')' would mark the ending of the columns list: "...)VALUES...".

                        strInspector.incrementPosition(VALUE_CLAUSE.length() - 1); // Advance to the end of "VALUE" and capture last character.
                        currPos = strInspector.getPosition();
                        currChar = strInspector.getChar();
                        strInspector.incrementPosition();
                        boolean matchedValues = false;
                        if (strInspector.matchesIgnoreCase("S") != -1) { // Check for the "S" in "VALUE(S)" and advance 1 more character if so.
                            currPos = strInspector.getPosition();
                            currChar = strInspector.getChar();
                            strInspector.incrementPosition();
                            matchedValues = true;
                        }

                        int endPos = strInspector.getPosition();
                        int nextPos = strInspector.indexOfNextChar(); // Position on the first meaningful character after VALUE[S].
                        boolean rightBounded = nextPos > endPos || strInspector.getChar() == '('; // '(' would mark the beginning of VALUE[S]: "... VALUES(...".

                        if (leftBounded && rightBounded) { // VALUE[S] keyword must not be part of another string, such as a table or column name.
                            if (matchedValues) {
                                valueStrMayBeTableName = false; // At this point a string "VALUE" cannot be a table name.
                            }
                            if (matchedValues && this.containsOnDuplicateKeyUpdate) { // VALUES after ODKU is a function, not a clause.
                                rewritableAsMultiValues = false;
                            } else {
                                withinValuesClause = true;
                                valuesClauseBegin = strInspector.getPosition();
                                valuesClauseBeginFound = true;
                                valuesEndpointStart = valuesClauseBegin;
                            }
                        }

                    } else if (withinValuesClause && currChar == '(') {
                        parensLevel++;
                        strInspector.incrementPosition();

                    } else if (withinValuesClause && currChar == ')') {
                        parensLevel--;
                        if (parensLevel < 0) {
                            parensLevel = 0; // Keep going, not checking for syntax validity.
                        }
                        strInspector.incrementPosition();
                        valuesClauseEnd = strInspector.getPosition(); // It may not be the end of the VALUE[S] clause yet but save it for later.

                    } else if (withinValuesClause && parensLevel == 0 && isInsert //
                            && strInspector.matchesIgnoreCase(AS_CLAUSE) != -1) { // End of VALUE[S] clause.
                        valueStrMayBeTableName = false; // At this point a string "VALUE" cannot be a table name.

                        if (valuesClauseEnd == -1) {
                            valuesClauseEnd = strInspector.getPosition();
                        }
                        valuesClauseEndFound = true;
                        withinValuesClause = false;
                        strInspector.incrementPosition(AS_CLAUSE.length() - 1); // Advance to the end of "AS" and capture last character.
                        currPos = strInspector.getPosition();
                        currChar = strInspector.getChar();
                        strInspector.incrementPosition();

                        this.valuesEndpoints.add(valuesEndpointStart);
                        this.valuesEndpoints.add(valuesClauseEnd);

                    } else if (withinValuesClause && parensLevel == 0 && isInsert //
                            && (matchEnd = strInspector.matchesIgnoreCase(ODKU_CLAUSE)) != -1) { // End of VALUE[S] clause.
                        valueStrMayBeTableName = false; // At this point a string "VALUE" cannot be a table name.

                        if (valuesClauseEnd == -1) {
                            valuesClauseEnd = strInspector.getPosition();
                        }
                        valuesClauseEndFound = true;
                        withinValuesClause = false;
                        strInspector.incrementPosition(matchEnd - strInspector.getPosition() - 1); // Advance to the end of "ODKU" and capture last character.
                        currPos = strInspector.getPosition();
                        currChar = strInspector.getChar();
                        strInspector.incrementPosition();

                        this.valuesEndpoints.add(valuesEndpointStart);
                        this.valuesEndpoints.add(valuesClauseEnd);

                        this.containsOnDuplicateKeyUpdate = true;
                        lookForOnDuplicateKeyUpdate = false;

                    } else if (strInspector.matchesIgnoreCase(LAST_INSERT_ID_FUNC) != -1) { // Can't rewrite as multi-values if LAST_INSERT_ID function is used.
                        rewritableAsMultiValues = false;
                        strInspector.incrementPosition(LAST_INSERT_ID_FUNC.length() - 1); // Advance to the end of "LAST_INSERT_ID" and capture last character.
                        currPos = strInspector.getPosition();
                        currChar = strInspector.getChar();
                        strInspector.incrementPosition();
                    }
                }

                if (lookForOnDuplicateKeyUpdate && currPos == strInspector.getPosition() && (matchEnd = strInspector.matchesIgnoreCase(ODKU_CLAUSE)) != -1) {
                    strInspector.incrementPosition(matchEnd - strInspector.getPosition() - 1); // Advance to the end of "ODKU" and capture last character.
                    currPos = strInspector.getPosition();
                    currChar = strInspector.getChar();
                    strInspector.incrementPosition();

                    this.containsOnDuplicateKeyUpdate = true;
                    lookForOnDuplicateKeyUpdate = false;
                }

                if (lookForLimitAndOffset) {
                    if (!matchedLimitClause && currPos == strInspector.getPosition() && (matchEnd = strInspector.matchesIgnoreCase(LIMIT_CLAUSE)) != -1) {
                        boolean leftBounded = currPos > lastPos + 1 || lastChar == ')'; // ')' would mark the ending of an expression: "... (1=1)LIMIT ...".

                        strInspector.incrementPosition(matchEnd - strInspector.getPosition() - 1); // Advance to the end of "LIMIT" and capture last character.
                        currPos = strInspector.getPosition();
                        currChar = strInspector.getChar();
                        strInspector.incrementPosition();

                        int endPos = strInspector.getPosition();
                        int nextPos = strInspector.indexOfNextChar(); // Position on the first meaningful character after LIMIT.
                        boolean rightBounded = nextPos > endPos;

                        if (leftBounded && rightBounded) { // LIMIT keyword must not be part of another string, such as a table or column name.
                            matchedLimitClause = true;
                            withinLimitClause = true;
                        }
                    } else if (withinLimitClause && currPos == strInspector.getPosition() && (matchEnd = strInspector.matchesIgnoreCase(OFFSET_CLAUSE)) != -1) {
                        boolean leftBounded = currPos > lastPos + 1;

                        strInspector.incrementPosition(matchEnd - strInspector.getPosition() - 1); // Advance to the end of "OFFSET" and capture last character.
                        currPos = strInspector.getPosition();
                        currChar = strInspector.getChar();
                        strInspector.incrementPosition();

                        int endPos = strInspector.getPosition();
                        int nextPos = strInspector.indexOfNextChar(); // Position on the first meaningful character after OFFSET.
                        boolean rightBounded = nextPos > endPos;

                        if (!leftBounded || !rightBounded) { // OFFSET keyword must not be part of another string, such as a table or column name.
                            withinLimitClause = false;
                        }
                    } else if (withinLimitClause) {
                        // If LIMIT was previously found, it's still possible to find a placeholder while digits or comma keep coming: "LIMIT [_digits_|?], ?".
                        withinLimitClause = withinLimitClause && currPos == strInspector.getPosition() && (currChar == ',' || Character.isDigit(currChar));
                    }
                }

                if (currPos == strInspector.getPosition()) {
                    strInspector.incrementPosition();
                }
            }

            lastPos = currPos;
            lastChar = currChar;
        }
        staticEndpoints.add(generalEndpointStart);
        staticEndpoints.add(this.queryLength);
        if (rewritableAsMultiValues) {
            if (!valuesClauseEndFound) {
                if (valuesClauseEnd == -1) {
                    valuesClauseEnd = this.queryLength;
                }
                valuesClauseEndFound = true;
                withinValuesClause = false;

                this.valuesEndpoints.add(valuesEndpointStart);
                this.valuesEndpoints.add(valuesClauseEnd);
            }

            if (valuesClauseBeginFound && valuesClauseEndFound) {
                this.valuesClauseLength = valuesClauseEnd - valuesClauseBegin;
            } else {
                rewritableAsMultiValues = false;
            }
        } else {
            this.valuesEndpoints.clear();
        }
        this.isRewritableWithMultiValuesClause = rewritableAsMultiValues;

        this.staticSqlParts = new byte[this.numberOfPlaceholders + 1][];
        for (int i = 0, j = 0; i <= this.numberOfPlaceholders; i++) {
            int begin = staticEndpoints.get(j++);
            int end = staticEndpoints.get(j++);
            int length = end - begin;
            this.staticSqlParts[i] = StringUtils.getBytes(this.sql, begin, length, this.encoding);
        }

        if (this.numberOfQueries > 1) {
            this.statementKeyword = MULTIPLE_QUERIES_TAG;
        }
    }