private void getCallStmtParameterTypes()

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