This is an automated email from the ASF dual-hosted git repository. vozerov pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push: new 4d7a076 IGNITE-10874: JDBC Thin Driver: fixed underscore handling in metadata. This closes #5899. new 6a61ec9 Merge remote-tracking branch 'origin/master' 4d7a076 is described below commit 4d7a0760278083236b31b732d5f054f2a8fcc88d Author: Pavel Kuznetsov <palmi...@gmail.com> AuthorDate: Thu Jan 24 15:28:57 2019 +0300 IGNITE-10874: JDBC Thin Driver: fixed underscore handling in metadata. This closes #5899. --- .../ignite/jdbc/JdbcThinMetadataSqlMatchTest.java | 150 +++++++++++++++++++++ .../jdbc/suite/IgniteJdbcDriverTestSuite.java | 2 + .../thin/JdbcThinMetadataPrimaryKeysSelfTest.java | 2 +- .../internal/processors/odbc/SqlListenerUtils.java | 18 +++ .../processors/odbc/jdbc/JdbcRequestHandler.java | 16 ++- .../internal/processors/odbc/odbc/OdbcUtils.java | 11 +- 6 files changed, 186 insertions(+), 13 deletions(-) diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcThinMetadataSqlMatchTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcThinMetadataSqlMatchTest.java new file mode 100644 index 0000000..d3d207a --- /dev/null +++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcThinMetadataSqlMatchTest.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.jdbc; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.testframework.GridTestUtils; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; + +/** + * Verify we are able to escape "_" character in the metadata request. + */ +@RunWith(JUnit4.class) +public class JdbcThinMetadataSqlMatchTest extends GridCommonAbstractTest { + /** Connection. */ + private Connection conn; + + /** {@inheritDoc} */ + @Override protected void beforeTestsStarted() throws Exception { + super.beforeTestsStarted(); + + IgniteEx ign = startGrid(0); + + conn = GridTestUtils.connect(ign, null); + } + + /** + * Execute ddl query via jdbc driver. + */ + protected void executeDDl(String sql) throws SQLException { + try (PreparedStatement upd = conn.prepareStatement(sql)) { + upd.executeUpdate(); + } + } + + /** + * Get tables by name pattern using jdbc metadata request. + * + * @param tabNamePtrn Table name pattern. + */ + protected List<String> getTableNames(String tabNamePtrn) throws SQLException { + ArrayList<String> names = new ArrayList<>(); + + try (ResultSet tabsRs = + conn.getMetaData().getTables(null, null, tabNamePtrn, new String[] {"TABLE"})) { + while (tabsRs.next()) + names.add(tabsRs.getString("TABLE_NAME")); + } + + // Actually metadata should be sorted by TABLE_NAME but it's broken. + Collections.sort(names); + + return names; + } + + /** Create tables. */ + @Before + public void createTables() throws Exception { + executeDDl("CREATE TABLE MY_FAV_TABLE (id INT PRIMARY KEY, val VARCHAR)"); + executeDDl("CREATE TABLE MY0FAV0TABLE (id INT PRIMARY KEY, val VARCHAR)"); + executeDDl("CREATE TABLE OTHER_TABLE (id INT PRIMARY KEY, val VARCHAR)"); + } + + /** Drop tables. */ + @After + public void dropTables() throws Exception { + // two tables that both matched by "TABLE MY_FAV_TABLE" sql pattern: + executeDDl("DROP TABLE MY_FAV_TABLE"); + executeDDl("DROP TABLE MY0FAV0TABLE"); + + // and another one that doesn't: + executeDDl("DROP TABLE OTHER_TABLE"); + } + + /** + * Test for escaping the "_" character in the table metadata request + */ + @Test + public void testTablesMatch() throws SQLException { + assertEqualsCollections(asList("MY0FAV0TABLE", "MY_FAV_TABLE"), getTableNames("MY_FAV_TABLE")); + assertEqualsCollections(singletonList("MY_FAV_TABLE"), getTableNames("MY\\_FAV\\_TABLE")); + + assertEqualsCollections(Collections.emptyList(), getTableNames("\\%")); + assertEqualsCollections(asList("MY0FAV0TABLE", "MY_FAV_TABLE", "OTHER_TABLE"), getTableNames("%")); + + assertEqualsCollections(Collections.emptyList(), getTableNames("")); + assertEqualsCollections(asList("MY0FAV0TABLE", "MY_FAV_TABLE", "OTHER_TABLE"), getTableNames(null)); + } + + /** + * Assert that collections contains the same elements regardless their order. Each element from the second + * collection should be met in the first one exact the same times. This method is required in this test because + * + * @param exp Expected. + * @param actual Actual. + */ + private void assertEqNoOrder(Collection<String> exp, Collection<String> actual) { + ArrayList<String> expSorted = new ArrayList<>(exp); + ArrayList<String> actSorted = new ArrayList<>(exp); + + Collections.sort(expSorted); + Collections.sort(actSorted); + + assertEqualsCollections(expSorted, actSorted); + } + + /** {@inheritDoc} */ + @Override protected void afterTestsStopped() throws Exception { + try { + conn.close(); + + conn = null; + + stopAllGrids(); + } + finally { + super.afterTestsStopped(); + } + } +} diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java index 6f8da20..d78e7b5 100644 --- a/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java +++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/suite/IgniteJdbcDriverTestSuite.java @@ -33,6 +33,7 @@ import org.apache.ignite.jdbc.JdbcPojoQuerySelfTest; import org.apache.ignite.jdbc.JdbcPreparedStatementSelfTest; import org.apache.ignite.jdbc.JdbcResultSetSelfTest; import org.apache.ignite.jdbc.JdbcStatementSelfTest; +import org.apache.ignite.jdbc.JdbcThinMetadataSqlMatchTest; import org.apache.ignite.jdbc.thin.JdbcThinAuthenticateConnectionSelfTest; import org.apache.ignite.jdbc.thin.JdbcThinAutoCloseServerCursorTest; import org.apache.ignite.jdbc.thin.JdbcThinBatchSelfTest; @@ -165,6 +166,7 @@ import org.junit.runners.Suite; JdbcThinEmptyCacheSelfTest.class, JdbcThinMetadataSelfTest.class, JdbcThinMetadataPrimaryKeysSelfTest.class, + JdbcThinMetadataSqlMatchTest.class, JdbcThinErrorsSelfTest.class, JdbcThinStatementCancelSelfTest.class, JdbcThinStatementTimeoutSelfTest.class, diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMetadataPrimaryKeysSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMetadataPrimaryKeysSelfTest.java index a4ef311..ee2f234 100644 --- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMetadataPrimaryKeysSelfTest.java +++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinMetadataPrimaryKeysSelfTest.java @@ -139,7 +139,7 @@ public class JdbcThinMetadataPrimaryKeysSelfTest extends GridCommonAbstractTest try (Connection conn = DriverManager.getConnection(URL)) { DatabaseMetaData md = conn.getMetaData(); - ResultSet rs = md.getPrimaryKeys(conn.getCatalog(), "", tabName); + ResultSet rs = md.getPrimaryKeys(conn.getCatalog(), null, tabName); List<String> colNames = new ArrayList<>(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlListenerUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlListenerUtils.java index 306e1be..0c3b2c3 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlListenerUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/SqlListenerUtils.java @@ -26,6 +26,7 @@ import org.apache.ignite.internal.binary.BinaryReaderExImpl; import org.apache.ignite.internal.binary.BinaryUtils; import org.apache.ignite.internal.binary.BinaryWriterExImpl; import org.apache.ignite.internal.binary.GridBinaryMarshaller; +import org.apache.ignite.internal.util.typedef.F; import org.jetbrains.annotations.Nullable; /** @@ -252,4 +253,21 @@ public abstract class SqlListenerUtils { || cls == Timestamp[].class || cls == java.util.Date[].class || cls == java.sql.Date[].class; } + + /** + * Converts sql pattern wildcards into java regex wildcards. + * Translates "_" to "." and "%" to ".*" if those are not escaped with "\" ("\_" or "\%"). + */ + public static String translateSqlWildcardsToRegex(String sqlPtrn) { + if (F.isEmpty(sqlPtrn)) + return sqlPtrn; + + String toRegex = ' ' + sqlPtrn; + + toRegex = toRegex.replaceAll("([^\\\\])%", "$1.*"); + toRegex = toRegex.replaceAll("([^\\\\])_", "$1."); + toRegex = toRegex.replaceAll("\\\\(.)", "$1"); + + return toRegex.substring(1); + } } \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java index b48c827..7f19491 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/jdbc/JdbcRequestHandler.java @@ -54,6 +54,7 @@ import org.apache.ignite.internal.processors.odbc.ClientListenerRequest; import org.apache.ignite.internal.processors.odbc.ClientListenerRequestHandler; import org.apache.ignite.internal.processors.odbc.ClientListenerResponse; import org.apache.ignite.internal.processors.odbc.ClientListenerResponseSender; +import org.apache.ignite.internal.processors.odbc.SqlListenerUtils; import org.apache.ignite.internal.processors.odbc.odbc.OdbcQueryGetColumnsMetaRequest; import org.apache.ignite.internal.processors.query.GridQueryCancel; import org.apache.ignite.internal.processors.query.GridQueryIndexDescriptor; @@ -1220,12 +1221,19 @@ public class JdbcRequestHandler implements ClientListenerRequestHandler { * Checks whether string matches SQL pattern. * * @param str String. - * @param ptrn Pattern. + * @param sqlPtrn Pattern. * @return Whether string matches pattern. */ - private static boolean matches(String str, String ptrn) { - return str != null && (F.isEmpty(ptrn) || - str.matches(ptrn.replace("%", ".*").replace("_", "."))); + private static boolean matches(String str, String sqlPtrn) { + if (str == null) + return false; + + if (sqlPtrn == null) + return true; + + String regex = SqlListenerUtils.translateSqlWildcardsToRegex(sqlPtrn); + + return str.matches(regex); } /** diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcUtils.java index d294ac2..c55ff1d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/odbc/OdbcUtils.java @@ -24,6 +24,7 @@ import org.apache.ignite.cache.query.QueryCursor; import org.apache.ignite.internal.processors.cache.QueryCursorImpl; import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode; import org.apache.ignite.internal.processors.odbc.SqlListenerDataTypes; +import org.apache.ignite.internal.processors.odbc.SqlListenerUtils; import org.apache.ignite.internal.processors.query.IgniteSQLException; import org.apache.ignite.internal.util.typedef.F; @@ -67,15 +68,9 @@ public class OdbcUtils { if (F.isEmpty(ptrn)) return ptrn; - String ptrn0 = ' ' + removeQuotationMarksIfNeeded(ptrn.toUpperCase()); + String ptrn0 = removeQuotationMarksIfNeeded(ptrn.toUpperCase()); - ptrn0 = ptrn0.replaceAll("([^\\\\])%", "$1.*"); - - ptrn0 = ptrn0.replaceAll("([^\\\\])_", "$1."); - - ptrn0 = ptrn0.replaceAll("\\\\(.)", "$1"); - - return ptrn0.substring(1); + return SqlListenerUtils.translateSqlWildcardsToRegex(ptrn0); } /**