Skip to content

Commit

Permalink
Fix for Bug#73775 (19531384), DBMD.getProcedureColumns()/.getFunction…
Browse files Browse the repository at this point in the history
…Columns() fail to filter by columnPattern.
  • Loading branch information
fjssilva committed Apr 11, 2017
1 parent 34cfe3b commit 7474677
Show file tree
Hide file tree
Showing 6 changed files with 541 additions and 66 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# $Id$
mm-dd-yy - Version 5.1.42

- Fix for Bug#73775 (19531384), DBMD.getProcedureColumns()/.getFunctionColumns() fail to filter by columnPattern.

- Fix for Bug#84324 (25321524), CallableStatement.extractProcedureName() not work when catalog name with dash.

- Fix for Bug#79561 (22333996), NullPointerException when calling a fully qualified stored procedure.
Expand Down
6 changes: 2 additions & 4 deletions src/com/mysql/jdbc/DatabaseMetaData.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
The MySQL Connector/J is licensed under the terms of the GPLv2
<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
Expand Down Expand Up @@ -1839,9 +1839,7 @@ private void getCallStmtParameterTypes(String catalog, String quotedProcName, Pr
paramName = paramName.substring(1, paramName.length() - 1);
}

int wildCompareRes = StringUtils.wildCompare(paramName, parameterNamePattern);

if (wildCompareRes != StringUtils.WILD_COMPARE_NO_MATCH) {
if (StringUtils.wildCompareIgnoreCase(paramName, parameterNamePattern)) {
ResultSetRow row = convertTypeDescriptorToProcedureRow(procNameAsBytes, procCatAsBytes, paramName, isOutParam, isInParam, false,
typeDesc, forGetFunctionColumns, ordinal++);

Expand Down
117 changes: 58 additions & 59 deletions src/com/mysql/jdbc/StringUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,13 @@ public enum SearchMode {

private static Method toPlainStringMethod;

static final int WILD_COMPARE_MATCH_NO_WILD = 0;
private static final int WILD_COMPARE_MATCH = 0;
private static final int WILD_COMPARE_CONTINUE_WITH_WILD = 1;
private static final int WILD_COMPARE_NO_MATCH = -1;

static final int WILD_COMPARE_MATCH_WITH_WILD = 1;

static final int WILD_COMPARE_NO_MATCH = -1;
static final char WILDCARD_MANY = '%';
static final char WILDCARD_ONE = '_';
static final char WILDCARD_ESCAPE = '\\';

private static final ConcurrentHashMap<String, Charset> charsetsByAlias = new ConcurrentHashMap<String, Charset>();

Expand Down Expand Up @@ -1566,138 +1568,135 @@ public static String toAsciiString(byte[] buffer, int startPos, int length) {
}

/**
* Compares searchIn against searchForWildcard with wildcards (heavily
* borrowed from strings/ctype-simple.c in the server sources)
* Compares searchIn against searchForWildcard with wildcards, in a case insensitive manner.
*
* @param searchIn
* the string to search in
* @param searchFor
* the string to search for, using the 'standard' SQL wildcard chars of '%' and '_'
*/
public static boolean wildCompareIgnoreCase(String searchIn, String searchFor) {
return wildCompareInternal(searchIn, searchFor) == WILD_COMPARE_MATCH;
}

/**
* Compares searchIn against searchForWildcard with wildcards (heavily borrowed from strings/ctype-simple.c in the server sources)
*
* This method does a single passage matching for normal characters and WILDCARD_ONE (_), and recursive matching for WILDCARD_MANY (%) which may be repeated
* for as many anchor chars are found.
*
* @param searchIn
* the string to search in
* @param searchForWildcard
* the string to search for, using the 'standard' SQL wildcard
* chars of '%' and '_'
* @param searchFor
* the string to search for, using the 'standard' SQL wildcard chars of '%' and '_'
*
* @return WILD_COMPARE_MATCH_NO_WILD if matched, WILD_COMPARE_NO_MATCH if
* not matched with wildcard, WILD_COMPARE_MATCH_WITH_WILD if
* matched with wildcard
* @return WILD_COMPARE_MATCH if matched, WILD_COMPARE_NO_MATCH if not matched, WILD_COMPARE_CONTINUE_WITH_WILD if not matched yet, but it may in one of
* following recursion rounds
*/
public static int wildCompare(String searchIn, String searchForWildcard) {
if ((searchIn == null) || (searchForWildcard == null)) {
private static int wildCompareInternal(String searchIn, String searchFor) {
if ((searchIn == null) || (searchFor == null)) {
return WILD_COMPARE_NO_MATCH;
}

if (searchForWildcard.equals("%")) {

return WILD_COMPARE_MATCH_WITH_WILD;
if (searchFor.equals("%")) {
return WILD_COMPARE_MATCH;
}

int result = WILD_COMPARE_NO_MATCH; /* Not found, using wildcards */

char wildcardMany = '%';
char wildcardOne = '_';
char wildcardEscape = '\\';

int searchForPos = 0;
int searchForEnd = searchForWildcard.length();
int searchForEnd = searchFor.length();

int searchInPos = 0;
int searchInEnd = searchIn.length();

while (searchForPos != searchForEnd) {
char wildstrChar = searchForWildcard.charAt(searchForPos);
int result = WILD_COMPARE_NO_MATCH; /* Not found, using wildcards */

while ((searchForWildcard.charAt(searchForPos) != wildcardMany) && (wildstrChar != wildcardOne)) {
if ((searchForWildcard.charAt(searchForPos) == wildcardEscape) && ((searchForPos + 1) != searchForEnd)) {
while (searchForPos != searchForEnd) {
while ((searchFor.charAt(searchForPos) != WILDCARD_MANY) && (searchFor.charAt(searchForPos) != WILDCARD_ONE)) {
if ((searchFor.charAt(searchForPos) == WILDCARD_ESCAPE) && ((searchForPos + 1) != searchForEnd)) {
searchForPos++;
}

if ((searchInPos == searchInEnd)
|| (Character.toUpperCase(searchForWildcard.charAt(searchForPos++)) != Character.toUpperCase(searchIn.charAt(searchInPos++)))) {
return WILD_COMPARE_MATCH_WITH_WILD; /* No match */
|| (Character.toUpperCase(searchFor.charAt(searchForPos++)) != Character.toUpperCase(searchIn.charAt(searchInPos++)))) {
return WILD_COMPARE_CONTINUE_WITH_WILD; /* No match */
}

if (searchForPos == searchForEnd) {
return ((searchInPos != searchInEnd) ? WILD_COMPARE_MATCH_WITH_WILD : WILD_COMPARE_MATCH_NO_WILD); /* Match if both are at end */
return ((searchInPos != searchInEnd) ? WILD_COMPARE_CONTINUE_WITH_WILD : WILD_COMPARE_MATCH); /* Match if both are at end */
}

result = WILD_COMPARE_MATCH_WITH_WILD; /* Found an anchor char */
result = WILD_COMPARE_CONTINUE_WITH_WILD; /* Found an anchor char */
}

if (searchForWildcard.charAt(searchForPos) == wildcardOne) {
if (searchFor.charAt(searchForPos) == WILDCARD_ONE) {
do {
if (searchInPos == searchInEnd) { /* Skip one char if possible */

return (result);
return result;
}

searchInPos++;
} while ((++searchForPos < searchForEnd) && (searchForWildcard.charAt(searchForPos) == wildcardOne));
} while ((++searchForPos < searchForEnd) && (searchFor.charAt(searchForPos) == WILDCARD_ONE));

if (searchForPos == searchForEnd) {
break;
}
}

if (searchForWildcard.charAt(searchForPos) == wildcardMany) { /* Found w_many */

char cmp;

if (searchFor.charAt(searchForPos) == WILDCARD_MANY) { /* Found w_many */
searchForPos++;

/* Remove any '%' and '_' from the wild search string */
for (; searchForPos != searchForEnd; searchForPos++) {
if (searchForWildcard.charAt(searchForPos) == wildcardMany) {
if (searchFor.charAt(searchForPos) == WILDCARD_MANY) {
continue;
}

if (searchForWildcard.charAt(searchForPos) == wildcardOne) {
if (searchInPos == searchInEnd) {
return (WILD_COMPARE_NO_MATCH);
if (searchFor.charAt(searchForPos) == WILDCARD_ONE) {
if (searchInPos == searchInEnd) { /* Skip one char if possible */
return WILD_COMPARE_NO_MATCH;
}

searchInPos++;

continue;
}

break; /* Not a wild character */
}

if (searchForPos == searchForEnd) {
return WILD_COMPARE_MATCH_NO_WILD; /* Ok if w_many is last */
return WILD_COMPARE_MATCH; /* Ok if w_many is last */
}

if (searchInPos == searchInEnd) {
return WILD_COMPARE_NO_MATCH;
}

if (((cmp = searchForWildcard.charAt(searchForPos)) == wildcardEscape) && ((searchForPos + 1) != searchForEnd)) {
cmp = searchForWildcard.charAt(++searchForPos);
char cmp;
if (((cmp = searchFor.charAt(searchForPos)) == WILDCARD_ESCAPE) && ((searchForPos + 1) != searchForEnd)) {
cmp = searchFor.charAt(++searchForPos);
}

searchForPos++;

do {
while ((searchInPos != searchInEnd) && (Character.toUpperCase(searchIn.charAt(searchInPos)) != Character.toUpperCase(cmp))) {
searchInPos++;
}
} /* Searches for an anchor char */

if (searchInPos++ == searchInEnd) {
return WILD_COMPARE_NO_MATCH;
}

{
int tmp = wildCompare(searchIn, searchForWildcard);

if (tmp <= 0) {
return (tmp);
}
int tmp = wildCompareInternal(searchIn.substring(searchInPos), searchFor.substring(searchForPos));
if (tmp <= 0) {
return tmp;
}
} while ((searchInPos != searchInEnd) && (searchForWildcard.charAt(0) != wildcardMany));

} while (searchInPos != searchInEnd);

return WILD_COMPARE_NO_MATCH;
}
}

return ((searchInPos != searchInEnd) ? WILD_COMPARE_MATCH_WITH_WILD : WILD_COMPARE_MATCH_NO_WILD);
return ((searchInPos != searchInEnd) ? WILD_COMPARE_CONTINUE_WITH_WILD : WILD_COMPARE_MATCH);
}

static byte[] s2b(String s, MySQLConnection conn) throws SQLException {
Expand Down
80 changes: 79 additions & 1 deletion src/testsuite/regression/MetaDataRegressionTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
The MySQL Connector/J is licensed under the terms of the GPLv2
<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
Expand Down Expand Up @@ -4226,4 +4226,82 @@ public void testBug23212347() throws Exception {
assertEquals(testCase, "id", rsmd.getColumnName(1));
} while (useSPS = !useSPS);
}

/**
* Tests fix for Bug#73775 - DBMD.getProcedureColumns()/.getFunctionColumns() fail to filter by columnPattern
*
* Test duplicated in testsuite.regression.jdbc4.MetaDataRegressionTest.
*/
public void testBug73775() throws Exception {
createFunction("testBug73775f", "(param1 CHAR(20), param2 CHAR(20)) RETURNS CHAR(40) DETERMINISTIC RETURN CONCAT(param1, param2)");
createProcedure("testBug73775p", "(INOUT param1 CHAR(20), IN param2 CHAR(20)) BEGIN SELECT CONCAT(param1, param2) INTO param1; END");

boolean useIS = false;
do {
final String testCase = String.format("Case: [useIS: %s]", useIS ? "Y" : "N");

final Properties props = new Properties();
final Connection testConn = getConnectionWithProps(props);
props.setProperty("useInformationSchema", Boolean.toString(useIS));
final DatabaseMetaData dbmd = testConn.getMetaData();

this.rs = dbmd.getProcedureColumns(null, "", "testBug73775%", "%");
assertTrue(testCase, this.rs.next());
assertEquals(testCase, "testBug73775f", this.rs.getString(3));
assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
assertEquals(testCase, DatabaseMetaData.procedureColumnReturn, this.rs.getInt(5));
assertTrue(testCase, this.rs.next());
assertEquals(testCase, "testBug73775f", this.rs.getString(3));
assertEquals(testCase, "param1", this.rs.getString(4));
assertTrue(testCase, this.rs.next());
assertEquals(testCase, "testBug73775f", this.rs.getString(3));
assertEquals(testCase, "param2", this.rs.getString(4));
assertTrue(testCase, this.rs.next());
assertEquals(testCase, "testBug73775p", this.rs.getString(3));
assertEquals(testCase, "param1", this.rs.getString(4));
assertTrue(testCase, this.rs.next());
assertEquals(testCase, "testBug73775p", this.rs.getString(3));
assertEquals(testCase, "param2", this.rs.getString(4));
assertFalse(testCase, this.rs.next());

for (String ptn : new String[] { "param1", "_____1", "%1", "p_r_m%1" }) {
this.rs = dbmd.getProcedureColumns(null, "", "testBug73775%", ptn);
assertTrue(this.rs.next());
assertEquals(testCase, "testBug73775f", this.rs.getString(3));
assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
assertEquals(testCase, DatabaseMetaData.procedureColumnReturn, this.rs.getInt(5));
assertTrue(testCase, this.rs.next());
assertEquals(testCase, "testBug73775f", this.rs.getString(3));
assertEquals(testCase, "param1", this.rs.getString(4));
assertTrue(testCase, this.rs.next());
assertEquals(testCase, "testBug73775p", this.rs.getString(3));
assertEquals(testCase, "param1", this.rs.getString(4));
assertFalse(testCase, this.rs.next());
}

for (String ptn : new String[] { "param2", "_____2", "%2", "p_r_m%2" }) {
this.rs = dbmd.getProcedureColumns(null, "", "testBug73775%", "param2");
assertTrue(this.rs.next());
assertEquals(testCase, "testBug73775f", this.rs.getString(3));
assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
assertEquals(testCase, DatabaseMetaData.procedureColumnReturn, this.rs.getInt(5));
assertTrue(testCase, this.rs.next());
assertEquals(testCase, "testBug73775f", this.rs.getString(3));
assertEquals(testCase, "param2", this.rs.getString(4));
assertTrue(testCase, this.rs.next());
assertEquals(testCase, "testBug73775p", this.rs.getString(3));
assertEquals(testCase, "param2", this.rs.getString(4));
assertFalse(testCase, this.rs.next());
}

this.rs = dbmd.getProcedureColumns(null, "", "testBug73775%", "");
assertTrue(testCase, this.rs.next());
assertEquals(testCase, "testBug73775f", this.rs.getString(3));
assertEquals(testCase, "", this.rs.getString(4)); // Function return param is always returned.
assertEquals(testCase, DatabaseMetaData.procedureColumnReturn, this.rs.getInt(5));
assertFalse(testCase, this.rs.next());

testConn.close();
} while (useIS = !useIS);
}
}
Loading

0 comments on commit 7474677

Please sign in to comment.