You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2019/01/24 12:29:24 UTC

[ignite] branch master updated: IGNITE-10874: JDBC Thin Driver: fixed underscore handling in metadata. This closes #5899.

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 <pa...@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);
     }
 
     /**