Applied Tony Rippy's patch for DDLUTILS-246: JdbcModelReader merges metadata between tables, with minor modifications

git-svn-id: https://svn.apache.org/repos/asf/db/ddlutils/trunk@1002945 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/ddlutils/platform/DatabaseMetaDataWrapper.java b/src/main/java/org/apache/ddlutils/platform/DatabaseMetaDataWrapper.java
index 8e512a1..861a7e4 100644
--- a/src/main/java/org/apache/ddlutils/platform/DatabaseMetaDataWrapper.java
+++ b/src/main/java/org/apache/ddlutils/platform/DatabaseMetaDataWrapper.java
@@ -22,14 +22,15 @@
 import java.sql.DatabaseMetaData;

 import java.sql.ResultSet;

 import java.sql.SQLException;

+import java.util.regex.Pattern;

 

 /**

  * Wrapper class for database meta data that stores additional info.

- * 

- * @version $Revision: 329426 $

  */

 public class DatabaseMetaDataWrapper

 {

+    /** Matches the characters not allowed in search strings. */

+    private final Pattern searchStringPattern = Pattern.compile("[_%]");

     /** The database meta data. */

     private DatabaseMetaData _metaData;

     /** The catalog to acess in the database. */

@@ -137,6 +138,49 @@
             System.arraycopy(types, 0, _tableTypes, 0, types.length);

         }

     }

+    

+    /**

+     * Escape a string literal so that it can be used as a search pattern.

+     * 

+     * @param literalString The string to escape.

+     * @return A string that can be properly used as a search string.

+     * @throws SQLException If an error occurred retrieving the meta data

+     */

+    public String escapeForSearch(String literalString) throws SQLException

+    {

+        String escape = getMetaData().getSearchStringEscape();

+

+        if (escape == "")

+        {

+            // No escape string, so nothing to do...

+            return literalString;

+        }

+        else

+        {

+            // with Java 5, we would just use Matcher.quoteReplacement

+            StringBuffer quotedEscape = new StringBuffer();

+

+            for (int idx = 0; idx < escape.length(); idx++)

+            {

+                char c = escape.charAt(idx);

+

+                switch (c)

+                {

+                    case '\\':

+                        quotedEscape.append("\\\\");

+                        break;

+                    case '$':

+                        quotedEscape.append("\\$");

+                        break;

+                    default:

+                        quotedEscape.append(c);

+                }

+            }

+            quotedEscape.append("$0");

+

+            return searchStringPattern.matcher(literalString).replaceAll(quotedEscape.toString());

+        }

+    }

 

     /**

      * Convenience method to return the table meta data using the configured catalog,

diff --git a/src/main/java/org/apache/ddlutils/platform/JdbcModelReader.java b/src/main/java/org/apache/ddlutils/platform/JdbcModelReader.java
index 667b635..a21e8a8 100644
--- a/src/main/java/org/apache/ddlutils/platform/JdbcModelReader.java
+++ b/src/main/java/org/apache/ddlutils/platform/JdbcModelReader.java
@@ -773,7 +773,7 @@
 
         try
         {
-            columnData = metaData.getColumns(tableName, getDefaultColumnPattern());
+            columnData = metaData.getColumns(metaData.escapeForSearch(tableName), getDefaultColumnPattern());
 
             List columns = new ArrayList();
 
@@ -856,7 +856,7 @@
 
         try
         {
-            pkData = metaData.getPrimaryKeys(tableName);
+            pkData = metaData.getPrimaryKeys(metaData.escapeForSearch(tableName));
             while (pkData.next())
             {
                 Map values = readColumns(pkData, getColumnsForPK());
@@ -897,7 +897,7 @@
 
         try
         {
-            fkData = metaData.getForeignKeys(tableName);
+            fkData = metaData.getForeignKeys(metaData.escapeForSearch(tableName));
 
             while (fkData.next())
             {
@@ -1004,7 +1004,7 @@
 
         try 
         {
-            indexData = metaData.getIndices(tableName, false, false);
+            indexData = metaData.getIndices(metaData.escapeForSearch(tableName), false, false);
 
             while (indexData.next())
             {
@@ -1236,7 +1236,7 @@
                 tablePattern = tablePattern.toUpperCase();
             }
 
-            tableData = metaData.getTables(tablePattern);
+            tableData = metaData.getTables(metaData.escapeForSearch(tablePattern));
 
             boolean found  = false;
             String  schema = null;
@@ -1249,7 +1249,7 @@
                 if ((tableName != null) && (tableName.length() > 0))
                 {
                     schema     = (String)values.get("TABLE_SCHEM");
-                    columnData = metaData.getColumns(tableName, getDefaultColumnPattern());
+                    columnData = metaData.getColumns(metaData.escapeForSearch(tableName), getDefaultColumnPattern());
                     found      = true;
 
                     while (found && columnData.next())
diff --git a/src/main/java/org/apache/ddlutils/platform/db2/Db2ModelReader.java b/src/main/java/org/apache/ddlutils/platform/db2/Db2ModelReader.java
index 00c50b5..4f86e61 100644
--- a/src/main/java/org/apache/ddlutils/platform/db2/Db2ModelReader.java
+++ b/src/main/java/org/apache/ddlutils/platform/db2/Db2ModelReader.java
@@ -233,7 +233,7 @@
 

             try

             {

-                pkData = metaData.getPrimaryKeys(table.getName());

+                pkData = metaData.getPrimaryKeys(metaData.escapeForSearch(table.getName()));

                 while (pkData.next())

                 {

                     Map values = readColumns(pkData, getColumnsForPK());

diff --git a/src/main/java/org/apache/ddlutils/platform/firebird/FirebirdModelReader.java b/src/main/java/org/apache/ddlutils/platform/firebird/FirebirdModelReader.java
index afb282a..eeee0ed 100644
--- a/src/main/java/org/apache/ddlutils/platform/firebird/FirebirdModelReader.java
+++ b/src/main/java/org/apache/ddlutils/platform/firebird/FirebirdModelReader.java
@@ -106,7 +106,7 @@
         	}

         	else

         	{

-        		columnData = metaData.getColumns(tableName, getDefaultColumnPattern());

+        		columnData = metaData.getColumns(metaData.escapeForSearch(tableName), getDefaultColumnPattern());

 

         		while (columnData.next())

                 {

@@ -221,7 +221,7 @@
         	}

             else

             {

-	            pkData = metaData.getPrimaryKeys(tableName);

+	            pkData = metaData.getPrimaryKeys(metaData.escapeForSearch(tableName));

 	            while (pkData.next())

 	            {

 	                Map values = readColumns(pkData, getColumnsForPK());

@@ -265,7 +265,7 @@
         	}

             else

             {

-	            fkData = metaData.getForeignKeys(tableName);

+	            fkData = metaData.getForeignKeys(metaData.escapeForSearch(tableName));

 	            while (fkData.next())

 	            {

 	                Map values = readColumns(fkData, getColumnsForFK());

@@ -410,7 +410,7 @@
                 tablePattern = tablePattern.toUpperCase();

             }

 

-            tableData = metaData.getTables(tablePattern);

+            tableData = metaData.getTables(metaData.escapeForSearch(tablePattern));

 

             boolean found  = false;

             String  schema = null;

@@ -434,7 +434,7 @@
                     }

                     else

                     {

-                        columnData = metaData.getColumns(tableName, getDefaultColumnPattern());

+                        columnData = metaData.getColumns(metaData.escapeForSearch(tableName), getDefaultColumnPattern());

                     }

 

                     while (found && columnData.next())

diff --git a/src/main/java/org/apache/ddlutils/platform/interbase/InterbaseModelReader.java b/src/main/java/org/apache/ddlutils/platform/interbase/InterbaseModelReader.java
index 3f88258..135700c 100644
--- a/src/main/java/org/apache/ddlutils/platform/interbase/InterbaseModelReader.java
+++ b/src/main/java/org/apache/ddlutils/platform/interbase/InterbaseModelReader.java
@@ -109,7 +109,7 @@
             }

             else

             {

-                columnData = metaData.getColumns(tableName, getDefaultColumnPattern());

+                columnData = metaData.getColumns(metaData.escapeForSearch(tableName), getDefaultColumnPattern());

 

                 while (columnData.next())

                 {

@@ -303,7 +303,7 @@
             }

             else

             {

-                pkData = metaData.getPrimaryKeys(tableName);

+                pkData = metaData.getPrimaryKeys(metaData.escapeForSearch(tableName));

                 while (pkData.next())

                 {

                     Map values = readColumns(pkData, getColumnsForPK());

@@ -347,7 +347,7 @@
             }

             else

             {

-                fkData = metaData.getForeignKeys(tableName);

+                fkData = metaData.getForeignKeys(metaData.escapeForSearch(tableName));

                 while (fkData.next())

                 {

                     Map values = readColumns(fkData, getColumnsForFK());

@@ -449,7 +449,7 @@
                 tablePattern = tablePattern.toUpperCase();

             }

 

-            tableData = metaData.getTables(tablePattern);

+            tableData = metaData.getTables(metaData.escapeForSearch(tablePattern));

 

             boolean found  = false;

             String  schema = null;

@@ -473,7 +473,7 @@
                     }

                     else

                     {

-                        columnData = metaData.getColumns(tableName, getDefaultColumnPattern());

+                        columnData = metaData.getColumns(metaData.escapeForSearch(tableName), getDefaultColumnPattern());

                     }

 

                     while (found && columnData.next())

diff --git a/src/main/java/org/apache/ddlutils/platform/mssql/MSSqlModelReader.java b/src/main/java/org/apache/ddlutils/platform/mssql/MSSqlModelReader.java
index c4aa27b..f385140 100644
--- a/src/main/java/org/apache/ddlutils/platform/mssql/MSSqlModelReader.java
+++ b/src/main/java/org/apache/ddlutils/platform/mssql/MSSqlModelReader.java
@@ -147,7 +147,7 @@
 

         try

         {

-            pks = metaData.getPrimaryKeys(table.getName());

+            pks = metaData.getPrimaryKeys(metaData.escapeForSearch(table.getName()));

 

             while (pks.next())

             {

diff --git a/src/test/java/org/apache/ddlutils/platform/TestDatabaseMetaDataWrapper.java b/src/test/java/org/apache/ddlutils/platform/TestDatabaseMetaDataWrapper.java
new file mode 100644
index 0000000..4ab20ef
--- /dev/null
+++ b/src/test/java/org/apache/ddlutils/platform/TestDatabaseMetaDataWrapper.java
@@ -0,0 +1,77 @@
+package org.apache.ddlutils.platform;
+
+/*
+ * 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.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.sql.DatabaseMetaData;
+import org.apache.ddlutils.TestBase;
+
+/**
+ * Tests for the utility methods in the {@link DatabaseMetaDataWrapper} class.
+ */
+public class TestDatabaseMetaDataWrapper extends TestBase
+{
+    /**
+     * Helper method to create a proxied DatabaseMetaData instance using the given invocation handler.
+     * 
+     * @param handler The handler
+     * @return The proxy object
+     */
+    private DatabaseMetaData createMockDatabaseMetaData(final InvocationHandler handler)
+    {
+        return (DatabaseMetaData)Proxy.newProxyInstance(getClass().getClassLoader(),
+                                                        new Class[] { DatabaseMetaData.class },
+                                                        handler);
+    }
+
+    /**
+     * Tests the {@link DatabaseMetaDataWrapper#escapeForSearch(String)} method (see DDLUTILS-246).
+     */
+    public void testEscapeSearchString() throws Exception
+    {
+        DatabaseMetaData metaData = createMockDatabaseMetaData(new InvocationHandler()
+            {
+                /**
+                 * {@inheritDoc}
+                 */
+                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+                {
+                    if ("getSearchStringEscape".equals(method.getName()))
+                    {
+                        return "\\";
+                    }
+                    else
+                    {
+                        throw new UnsupportedOperationException();
+                    }
+                }
+            });
+
+        DatabaseMetaDataWrapper wrapper = new DatabaseMetaDataWrapper();
+
+        wrapper.setMetaData(metaData);
+
+        assertEquals("FOOMATIC", wrapper.escapeForSearch("FOOMATIC"));
+        assertEquals("FOO\\_MATIC", wrapper.escapeForSearch("FOO_MATIC"));
+        assertEquals("FOO\\%MATIC", wrapper.escapeForSearch("FOO%MATIC"));
+    }
+}