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;
}
}