private List extractStoredRoutineColumnsTypeInfo()

in src/main/user-impl/java/com/mysql/cj/jdbc/DatabaseMetaDataMysqlSchema.java [821:993]


    private List<Row> extractStoredRoutineColumnsTypeInfo(String dbName, String routineName, StoredRoutineType routineType, String parameterNamePattern,
            StoredRoutineType targetMetaData) throws SQLException {
        final List<Row> rows = new ArrayList<>();

        boolean isRoutineInAnsiMode = false;
        String openingDelimiters = null;
        String closingDelimiters = null;
        String parameterDef = null;

        ResultSet paramRetrievalRs = null;
        try (Statement paramRetrievalStmt = getJdbcConnection().getMetaDataSafeStatement()) {
            String fieldName = null;
            StringBuilder query = new StringBuilder();
            if (routineType == StoredRoutineType.PROCEDURE) {
                fieldName = "Create Procedure";
                query.append("SHOW CREATE PROCEDURE ");
            } else {
                fieldName = "Create Function";
                query.append("SHOW CREATE FUNCTION ");
            }
            query.append(StringUtils.quoteIdentifier(dbName, getQuoteId(), true));
            query.append('.');
            query.append(StringUtils.quoteIdentifier(routineName, getQuoteId(), true));

            paramRetrievalRs = paramRetrievalStmt.executeQuery(query.toString());
            if (paramRetrievalRs.next()) {
                String routineCode = paramRetrievalRs.getString(fieldName);
                if (!noAccessToProcedureBodiesValue() && StringUtils.isNullOrEmpty(routineCode)) {
                    throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.4"), MysqlErrorNumbers.SQLSTATE_CONNJ_GENERAL_ERROR,
                            getExceptionInterceptor());
                }

                try {
                    String sqlMode = paramRetrievalRs.getString("sql_mode");
                    isRoutineInAnsiMode = StringUtils.indexOfIgnoreCase(sqlMode, "ANSI") != -1;
                } catch (SQLException e) {
                    AssertionFailedException.shouldNotHappen(e);
                }
                String identifierMarkers = isRoutineInAnsiMode ? "`\"" : "`";
                String identifierAndStringMarkers = "'" + identifierMarkers;
                openingDelimiters = "(" + identifierMarkers;
                closingDelimiters = ")" + identifierMarkers;

                if (!StringUtils.isNullOrEmpty(routineCode)) {
                    // Sanitize/normalize by stripping out comments.
                    routineCode = StringUtils.stripCommentsAndHints(routineCode, identifierAndStringMarkers, identifierAndStringMarkers,
                            !getSession().getServerSession().isNoBackslashEscapesSet());
                    int startOfParamDeclaration = StringUtils.indexOfIgnoreCase(0, routineCode, "(", getQuoteId(), getQuoteId(),
                            getSession().getServerSession().isNoBackslashEscapesSet() ? SearchMode.__MRK_COM_MYM_HNT_WS : SearchMode.__FULL);
                    int endOfParamDeclaration = indexOfParameterDeclarationEnd(startOfParamDeclaration, routineCode, identifierMarkers);

                    if (routineType == StoredRoutineType.FUNCTION) {
                        // Grab the return column since it needs to go first in the output result set.
                        int returnsIndex = StringUtils.indexOfIgnoreCase(0, routineCode, " RETURNS ", getQuoteId(), getQuoteId(),
                                getSession().getServerSession().isNoBackslashEscapesSet() ? SearchMode.__MRK_COM_MYM_HNT_WS : SearchMode.__FULL);
                        int endReturnsDef = indexOfEndOfReturnsClause(routineCode, returnsIndex, identifierMarkers);

                        // Trim off whitespace after "RETURNS".
                        int declarationStart = returnsIndex + "RETURNS ".length();
                        while (declarationStart < routineCode.length()) {
                            if (Character.isWhitespace(routineCode.charAt(declarationStart))) {
                                declarationStart++;
                            } else {
                                break;
                            }
                        }

                        String returnsDefn = routineCode.substring(declarationStart, endReturnsDef).trim();
                        TypeDescriptor returnsTypeDescriptor = new TypeDescriptor(returnsDefn, "YES");
                        rows.add(typeDescriptorToStoredRoutineRow(dbName, routineName, "", false, false, true, returnsTypeDescriptor, 0, targetMetaData));
                    }

                    if (startOfParamDeclaration == -1 || endOfParamDeclaration == -1) {
                        throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.5"), MysqlErrorNumbers.SQLSTATE_CONNJ_GENERAL_ERROR,
                                getExceptionInterceptor());
                    }
                    parameterDef = routineCode.substring(startOfParamDeclaration + 1, endOfParamDeclaration);
                }

            }
        } finally {
            if (paramRetrievalRs != null) {
                try {
                    paramRetrievalRs.close();
                } catch (SQLException sqlEx) {
                    AssertionFailedException.shouldNotHappen(sqlEx);
                }
                paramRetrievalRs = null;
            }
        }

        if (parameterDef != null) {
            int ordinal = 1;
            List<String> parseList = StringUtils.split(parameterDef, ",", openingDelimiters, closingDelimiters, true);
            for (String declaration : parseList) {
                if (declaration.trim().length() == 0) {
                    break; // No parameters actually declared, but whitespace spans lines.
                }

                String paramName = null;
                boolean isOutParam = false;
                boolean isInParam = false;

                // 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, " ");
                if (declarationTok.hasMoreTokens()) {
                    String possibleParamName = declarationTok.nextToken();
                    if (possibleParamName.equalsIgnoreCase("OUT")) {
                        isOutParam = true;
                        isInParam = false;
                        if (declarationTok.hasMoreTokens()) {
                            paramName = declarationTok.nextToken();
                        } else {
                            throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.6"), MysqlErrorNumbers.SQLSTATE_CONNJ_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.SQLSTATE_CONNJ_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.SQLSTATE_CONNJ_GENERAL_ERROR,
                                    getExceptionInterceptor());
                        }
                    } else {
                        isOutParam = false;
                        isInParam = true;
                        paramName = possibleParamName;
                    }

                    TypeDescriptor typeDesc = null;
                    if (declarationTok.hasMoreTokens()) {
                        StringBuilder typeInfo = new StringBuilder(declarationTok.nextToken());
                        while (declarationTok.hasMoreTokens()) {
                            typeInfo.append(" ");
                            typeInfo.append(declarationTok.nextToken());
                        }
                        typeDesc = new TypeDescriptor(typeInfo.toString(), "YES");
                    } else {
                        throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.7"), MysqlErrorNumbers.SQLSTATE_CONNJ_GENERAL_ERROR,
                                getExceptionInterceptor());
                    }

                    if (paramName.startsWith("`") && paramName.endsWith("`")) {
                        paramName = StringUtils.unquoteIdentifier(paramName, "`");
                    } else if (isRoutineInAnsiMode && paramName.startsWith("\"") && paramName.endsWith("\"")) {
                        paramName = StringUtils.unquoteIdentifier(paramName, "\"");
                    }

                    final String paramNameFilter = normalizeIdentifierQuoting(parameterNamePattern);
                    if (paramNameFilter == null || StringUtils.wildCompareIgnoreCase(paramName, paramNameFilter)) {
                        rows.add(typeDescriptorToStoredRoutineRow(dbName, routineName, paramName, isOutParam, isInParam, false, typeDesc, ordinal++,
                                targetMetaData));
                    }
                } else {
                    throw SQLError.createSQLException(Messages.getString("DatabaseMetaData.8"), MysqlErrorNumbers.SQLSTATE_CONNJ_GENERAL_ERROR,
                            getExceptionInterceptor());
                }
            }
        }
        return rows;
    }