in src/main/user-impl/java/com/mysql/cj/jdbc/DatabaseMetaData.java [1374:1651]
private void getCallStmtParameterTypes(String db, String quotedProcName, ProcedureType procType, String parameterNamePattern, List<Row> resultRows,
boolean forGetFunctionColumns) throws SQLException {
java.sql.Statement paramRetrievalStmt = null;
java.sql.ResultSet paramRetrievalRs = null;
String parameterDef = null;
byte[] procNameAsBytes = null;
byte[] procCatAsBytes = null;
boolean isProcedureInAnsiMode = false;
String storageDefnDelims = null;
String storageDefnClosures = null;
try {
paramRetrievalStmt = this.conn.getMetadataSafeStatement();
String oldDb = this.conn.getDatabase();
if (this.conn.lowerCaseTableNames() && db != null && db.length() != 0 && oldDb != null && oldDb.length() != 0) {
// Workaround for bug in server wrt. to SHOW CREATE PROCEDURE not respecting lower-case table names
ResultSet rs = null;
try {
this.conn.setDatabase(StringUtils.unQuoteIdentifier(db, this.quotedId));
rs = paramRetrievalStmt.executeQuery("SELECT DATABASE()");
rs.next();
db = rs.getString(1);
} finally {
this.conn.setDatabase(oldDb);
if (rs != null) {
rs.close();
}
}
}
if (paramRetrievalStmt.getMaxRows() != 0) {
paramRetrievalStmt.setMaxRows(0);
}
int dotIndex = " ".equals(this.quotedId) ? quotedProcName.indexOf(".")
: StringUtils.indexOfIgnoreCase(0, quotedProcName, ".", this.quotedId, this.quotedId,
this.session.getServerSession().isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL);
String dbName = null;
if (dotIndex != -1 && (dotIndex + 1) < quotedProcName.length()) {
dbName = quotedProcName.substring(0, dotIndex);
quotedProcName = quotedProcName.substring(dotIndex + 1);
} else {
dbName = StringUtils.quoteIdentifier(db, this.quotedId, this.pedantic);
}
// Moved from above so that procName is *without* database as expected by the rest of code
// Removing QuoteChar to get output as it was before PROC_CAT fixes
String tmpProcName = StringUtils.unQuoteIdentifier(quotedProcName, this.quotedId);
procNameAsBytes = StringUtils.getBytes(tmpProcName, "UTF-8");
tmpProcName = StringUtils.unQuoteIdentifier(dbName, this.quotedId);
procCatAsBytes = StringUtils.getBytes(tmpProcName, "UTF-8");
// there is no need to quote the identifier here since 'dbName' and 'procName' are guaranteed to be already quoted.
StringBuilder procNameBuf = new StringBuilder();
procNameBuf.append(dbName);
procNameBuf.append('.');
procNameBuf.append(quotedProcName);
String fieldName = null;
if (procType == PROCEDURE) {
paramRetrievalRs = paramRetrievalStmt.executeQuery("SHOW CREATE PROCEDURE " + procNameBuf.toString());
fieldName = "Create Procedure";
} else {
paramRetrievalRs = paramRetrievalStmt.executeQuery("SHOW CREATE FUNCTION " + procNameBuf.toString());
fieldName = "Create Function";
}
if (paramRetrievalRs.next()) {
String procedureDef = paramRetrievalRs.getString(fieldName);
if (!this.conn.getPropertySet().getBooleanProperty(PropertyKey.noAccessToProcedureBodies).getValue()
&& (procedureDef == null || procedureDef.length() == 0)) {
throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.4"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR,
getExceptionInterceptor());
}
try {
String sqlMode = paramRetrievalRs.getString("sql_mode");
if (StringUtils.indexOfIgnoreCase(sqlMode, "ANSI") != -1) {
isProcedureInAnsiMode = true;
}
} catch (SQLException sqlEx) {
// doesn't exist
}
String identifierMarkers = isProcedureInAnsiMode ? "`\"" : "`";
String identifierAndStringMarkers = "'" + identifierMarkers;
storageDefnDelims = "(" + identifierMarkers;
storageDefnClosures = ")" + identifierMarkers;
if (procedureDef != null && procedureDef.length() != 0) {
// sanitize/normalize by stripping out comments
procedureDef = StringUtils.stripComments(procedureDef, identifierAndStringMarkers, identifierAndStringMarkers, true, false, true, true);
int openParenIndex = StringUtils.indexOfIgnoreCase(0, procedureDef, "(", this.quotedId, this.quotedId,
this.session.getServerSession().isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL);
int endOfParamDeclarationIndex = 0;
endOfParamDeclarationIndex = endPositionOfParameterDeclaration(openParenIndex, procedureDef, this.quotedId);
if (procType == FUNCTION) {
// Grab the return column since it needs
// to go first in the output result set
int returnsIndex = StringUtils.indexOfIgnoreCase(0, procedureDef, " RETURNS ", this.quotedId, this.quotedId,
this.session.getServerSession().isNoBackslashEscapesSet() ? StringUtils.SEARCH_MODE__MRK_COM_WS : StringUtils.SEARCH_MODE__ALL);
int endReturnsDef = findEndOfReturnsClause(procedureDef, returnsIndex);
// Trim off whitespace after "RETURNS"
int declarationStart = returnsIndex + "RETURNS ".length();
while (declarationStart < procedureDef.length()) {
if (Character.isWhitespace(procedureDef.charAt(declarationStart))) {
declarationStart++;
} else {
break;
}
}
String returnsDefn = procedureDef.substring(declarationStart, endReturnsDef).trim();
TypeDescriptor returnDescriptor = new TypeDescriptor(returnsDefn, "YES");
resultRows.add(convertTypeDescriptorToProcedureRow(procNameAsBytes, procCatAsBytes, "", false, false, true, returnDescriptor,
forGetFunctionColumns, 0));
}
if ((openParenIndex == -1) || (endOfParamDeclarationIndex == -1)) {
// parse error?
throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.5"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR,
getExceptionInterceptor());
}
parameterDef = procedureDef.substring(openParenIndex + 1, endOfParamDeclarationIndex);
}
}
} finally {
SQLException sqlExRethrow = null;
if (paramRetrievalRs != null) {
try {
paramRetrievalRs.close();
} catch (SQLException sqlEx) {
sqlExRethrow = sqlEx;
}
paramRetrievalRs = null;
}
if (paramRetrievalStmt != null) {
try {
paramRetrievalStmt.close();
} catch (SQLException sqlEx) {
sqlExRethrow = sqlEx;
}
paramRetrievalStmt = null;
}
if (sqlExRethrow != null) {
throw sqlExRethrow;
}
}
if (parameterDef != null) {
int ordinal = 1;
List<String> parseList = StringUtils.split(parameterDef, ",", storageDefnDelims, storageDefnClosures, true);
int parseListLen = parseList.size();
for (int i = 0; i < parseListLen; i++) {
String declaration = parseList.get(i);
if (declaration.trim().length() == 0) {
break; // no parameters actually declared, but whitespace spans lines
}
// Bug#52167, tokenizer will break if declaration contains special characters like \n
declaration = declaration.replaceAll("[\\t\\n\\x0B\\f\\r]", " ");
StringTokenizer declarationTok = new StringTokenizer(declaration, " \t");
String paramName = null;
boolean isOutParam = false;
boolean isInParam = false;
if (declarationTok.hasMoreTokens()) {
String possibleParamName = declarationTok.nextToken();
if (possibleParamName.equalsIgnoreCase("OUT")) {
isOutParam = true;
if (declarationTok.hasMoreTokens()) {
paramName = declarationTok.nextToken();
} else {
throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.6"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR,
getExceptionInterceptor());
}
} else if (possibleParamName.equalsIgnoreCase("INOUT")) {
isOutParam = true;
isInParam = true;
if (declarationTok.hasMoreTokens()) {
paramName = declarationTok.nextToken();
} else {
throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.6"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR,
getExceptionInterceptor());
}
} else if (possibleParamName.equalsIgnoreCase("IN")) {
isOutParam = false;
isInParam = true;
if (declarationTok.hasMoreTokens()) {
paramName = declarationTok.nextToken();
} else {
throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.6"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR,
getExceptionInterceptor());
}
} else {
isOutParam = false;
isInParam = true;
paramName = possibleParamName;
}
TypeDescriptor typeDesc = null;
if (declarationTok.hasMoreTokens()) {
StringBuilder typeInfoBuf = new StringBuilder(declarationTok.nextToken());
while (declarationTok.hasMoreTokens()) {
typeInfoBuf.append(" ");
typeInfoBuf.append(declarationTok.nextToken());
}
String typeInfo = typeInfoBuf.toString();
typeDesc = new TypeDescriptor(typeInfo, "YES");
} else {
throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.7"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR,
getExceptionInterceptor());
}
if ((paramName.startsWith("`") && paramName.endsWith("`"))
|| (isProcedureInAnsiMode && paramName.startsWith("\"") && paramName.endsWith("\""))) {
paramName = paramName.substring(1, paramName.length() - 1);
}
if (parameterNamePattern == null || StringUtils.wildCompareIgnoreCase(paramName, parameterNamePattern)) {
Row row = convertTypeDescriptorToProcedureRow(procNameAsBytes, procCatAsBytes, paramName, isOutParam, isInParam, false, typeDesc,
forGetFunctionColumns, ordinal++);
resultRows.add(row);
}
} else {
throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.8"), MysqlErrorNumbers.SQL_STATE_GENERAL_ERROR,
getExceptionInterceptor());
}
}
} else {
// Is this an error? JDBC spec doesn't make it clear if stored procedure doesn't exist, is it an error....
}
}