All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.genericSQL.GenericSQLModifier Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2007 Daniel Armbrust 
 * Licensed 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 genericSQL;

import java.sql.Connection;
import java.util.Hashtable;

import org.apache.log4j.Logger;

/**
 * A class to read my quasi-sql and convert it to database specific sql.
 * 
 * Currently supports MySQL, MS Access, PostgreSQL, DB2 (various flavors), HyperSonic SQL and Microsoft SQL
 * Server
 * 
 * @author Dan Armbrust 
 */
public class GenericSQLModifier
{
    private String        databaseType_;
    private Hashtable     datatypeConversion_;
    private String[]      customDatatypes_;
    private String        properQuote_;

    private static Logger log = Logger.getLogger("convert.sqlMod");
    
    //CTS needs to override the LIKE replacement with a different string 
    //to get case insentive queries.
    public static String  mySqlLikeOverride;

    /**
     * @param connection the connection
     * @throws Exception
     */
    public GenericSQLModifier(Connection connection) throws Exception
    {
        this(connection.getMetaData().getDatabaseProductName());
    }

    /**
     * 
     * @param databaseType may be "MySQL", "ACCESS", "PostgreSQL", "DB2*", "Microsoft SQL Server", "HSQL Database Engine"
     * @throws Exception
     */
    public GenericSQLModifier(String databaseType) throws Exception
    {
        // need the sqlLite table, because it uses access booleans, while the others don't
        // because access booleans don't support null
        databaseType_ = databaseType;

        datatypeConversion_ = new Hashtable();
        customDatatypes_ = new String[]{"{boolean}", "{IF NOT EXISTS}", "{limitedText}", "{unlimitedText}", "{bigInt}",
                "{TYPE}", "{DROPFOREIGNKEY}", "{true}", "{false}", "{LIMIT}", "{DEFAULT_INDEX_SIZE}", "{LIKE}",
                "{BINARY}", "{CASCADE}", "{lgCharSet}", "{lgTableCharSet}", "{dateTime}", "{AS}"};
        if (databaseType_.equals("MySQL"))
        {
            properQuote_ = "`";
            datatypeConversion_.put("{boolean}", "tinyint(1)");
            datatypeConversion_.put("{IF NOT EXISTS}", "IF NOT EXISTS");
            datatypeConversion_.put("{limitedText}", "varchar");
            datatypeConversion_.put("{unlimitedText}", "text");
            datatypeConversion_.put("{bigInt}", "bigint(20)");
            datatypeConversion_.put("{TYPE}", "TYPE=InnoDB");
            datatypeConversion_.put("{DROPFOREIGNKEY}", "DROP FOREIGN KEY");
            datatypeConversion_.put("{true}", "1");
            datatypeConversion_.put("{false}", "0");
            datatypeConversion_.put("{LIMIT}", "LIMIT ?, ?");
            datatypeConversion_.put("{DEFAULT_INDEX_SIZE}", "(3)");
            datatypeConversion_.put("{LIKE}", (mySqlLikeOverride == null || mySqlLikeOverride.length() == 0 ? "LIKE" : mySqlLikeOverride));
            datatypeConversion_.put("{BINARY}", "BINARY");
            datatypeConversion_.put("{CASCADE}", "CASCADE");
            datatypeConversion_.put("{lgCharSet}", "CHARACTER SET latin1 COLLATE latin1_bin");
            datatypeConversion_.put("{lgTableCharSet}", "CHARACTER SET latin1 COLLATE latin1_bin");
            datatypeConversion_.put("{dateTime}", "DATETIME");
            datatypeConversion_.put("{AS}", "AS");
        }
        else if (databaseType_.equals("ACCESS"))
        {
            properQuote_ = "`";
            datatypeConversion_.put("{boolean}", "Text(5)"); // access booleans don't allow null...
            datatypeConversion_.put("{IF NOT EXISTS}", "");
            datatypeConversion_.put("{limitedText}", "Text");
            datatypeConversion_.put("{unlimitedText}", "memo");
            datatypeConversion_.put("{bigInt}", "Number");
            datatypeConversion_.put("{TYPE}", "");
            datatypeConversion_.put("{DROPFOREIGNKEY}", "DROP CONSTRAINT");
            datatypeConversion_.put("{true}", "true");
            datatypeConversion_.put("{false}", "false");
            datatypeConversion_.put("{LIMIT}", "");
            datatypeConversion_.put("{DEFAULT_INDEX_SIZE}", "");
            datatypeConversion_.put("{LIKE}", "LIKE");
            datatypeConversion_.put("{BINARY}", "");
            datatypeConversion_.put("{CASCADE}", "CASCADE");
            datatypeConversion_.put("{lgCharSet}", "");
            datatypeConversion_.put("{lgTableCharSet}", "");
            datatypeConversion_.put("{dateTime}", "DATETIME");
            datatypeConversion_.put("{AS}", "AS");
            datatypeConversion_.put("[limitedTextLimit]", "255");  //automatically switch limitedText requests to unlimitedText types if the size is > that this.
        }
        else if (databaseType_.equals("PostgreSQL"))
        {
            properQuote_ = "";
            datatypeConversion_.put("{boolean}", "bool");
            datatypeConversion_.put("{IF NOT EXISTS}", "");
            datatypeConversion_.put("{limitedText}", "varchar");
            datatypeConversion_.put("{unlimitedText}", "text");
            datatypeConversion_.put("{bigInt}", "int8");
            datatypeConversion_.put("{TYPE}", "");
            datatypeConversion_.put("{DROPFOREIGNKEY}", "DROP CONSTRAINT");
            datatypeConversion_.put("{true}", "'true'");
            datatypeConversion_.put("{false}", "'false'");
            datatypeConversion_.put("{LIMIT}", "");
            datatypeConversion_.put("{DEFAULT_INDEX_SIZE}", "");
            datatypeConversion_.put("{LIKE}", "ILIKE");
            datatypeConversion_.put("{BINARY}", "");
            datatypeConversion_.put("{CASCADE}", "CASCADE");
            datatypeConversion_.put("{lgCharSet}", "ENCODING='UTF8'");
            datatypeConversion_.put("{lgTableCharSet}", "");
            datatypeConversion_.put("{dateTime}", "TIMESTAMP");
            datatypeConversion_.put("{AS}", "AS");
        }
        else if (databaseType_.startsWith("DB2"))
        {
            properQuote_ = "";
            datatypeConversion_.put("{boolean}", "smallint");
            datatypeConversion_.put("{IF NOT EXISTS}", "");
            datatypeConversion_.put("{limitedText}", "varchar");
            datatypeConversion_.put("{unlimitedText}", "long varchar");
            datatypeConversion_.put("{bigInt}", "integer");
            datatypeConversion_.put("{TYPE}", "");
            datatypeConversion_.put("{DROPFOREIGNKEY}", "DROP CONSTRAINT");
            datatypeConversion_.put("{true}", "1");
            datatypeConversion_.put("{false}", "0");
            datatypeConversion_.put("{LIMIT}", "");
            datatypeConversion_.put("{DEFAULT_INDEX_SIZE}", "");
            datatypeConversion_.put("{LIKE}", "LIKE");
            datatypeConversion_.put("{BINARY}", "");
            datatypeConversion_.put("{CASCADE}", "");
            datatypeConversion_.put("{lgCharSet}", "");
            datatypeConversion_.put("{lgTableCharSet}", "");
            datatypeConversion_.put("{dateTime}", "TIMESTAMP");
            datatypeConversion_.put("{AS}", "AS");
        }
        else if (databaseType_.equals("Microsoft SQL Server"))
        {
            properQuote_ = "\"";
            datatypeConversion_.put("{boolean}", "tinyint");
            datatypeConversion_.put("{IF NOT EXISTS}", "");
            datatypeConversion_.put("{limitedText}", "varchar");
            datatypeConversion_.put("{unlimitedText}", "varchar(8000)");
            datatypeConversion_.put("{bigInt}", "bigint");
            datatypeConversion_.put("{TYPE}", "");
            datatypeConversion_.put("{DROPFOREIGNKEY}", "DROP CONSTRAINT");
            datatypeConversion_.put("{true}", "1");
            datatypeConversion_.put("{false}", "0");
            datatypeConversion_.put("{LIMIT}", "");
            datatypeConversion_.put("{DEFAULT_INDEX_SIZE}", "");
            datatypeConversion_.put("{LIKE}", "LIKE");
            datatypeConversion_.put("{BINARY}", "");
            datatypeConversion_.put("{CASCADE}", "CASCADE");
            datatypeConversion_.put("{lgCharSet}", "");
            datatypeConversion_.put("{lgTableCharSet}", "");
            datatypeConversion_.put("{dateTime}", "DATETIME");
            datatypeConversion_.put("{AS}", "AS");
        }
        else if (databaseType_.equals("HSQL Database Engine"))
        {
            properQuote_ = "";
            datatypeConversion_.put("{boolean}", "boolean");
            datatypeConversion_.put("{IF NOT EXISTS}", "");
            datatypeConversion_.put("{limitedText}", "varchar");
            datatypeConversion_.put("{unlimitedText}", "varchar_ignorecase");
            datatypeConversion_.put("{bigInt}", "bigint");
            datatypeConversion_.put("{TYPE}", "");
            datatypeConversion_.put("{DROPFOREIGNKEY}", "DROP CONSTRAINT");
            datatypeConversion_.put("{true}", "'true'");
            datatypeConversion_.put("{false}", "'false'");
            datatypeConversion_.put("{LIMIT}", "");
            datatypeConversion_.put("{DEFAULT_INDEX_SIZE}", "");
            datatypeConversion_.put("{LIKE}", "LIKE");
            datatypeConversion_.put("{BINARY}", "");
            datatypeConversion_.put("{CASCADE}", "CASCADE");
            datatypeConversion_.put("{dateTime}", "TIMESTAMP");
            datatypeConversion_.put("{lgCharSet}", "");
            datatypeConversion_.put("{lgTableCharSet}", "");
            datatypeConversion_.put("{AS}", "AS");
        }
        else if (databaseType_.startsWith("Oracle"))
        {
            properQuote_ = "";
            datatypeConversion_.put("{boolean}", "CHAR");
            datatypeConversion_.put("{IF NOT EXISTS}", "");
            datatypeConversion_.put("{limitedText}", "VARCHAR2");
            datatypeConversion_.put("{unlimitedText}", "CLOB");
            datatypeConversion_.put("{bigInt}", "NUMBER(37)");
            datatypeConversion_.put("{TYPE}", "");
            datatypeConversion_.put("{DROPFOREIGNKEY}", "DROP CONSTRAINT");
            datatypeConversion_.put("{true}", "1");
            datatypeConversion_.put("{false}", "0");
            datatypeConversion_.put("{LIMIT}", "");
            datatypeConversion_.put("{DEFAULT_INDEX_SIZE}", "");
            datatypeConversion_.put("{LIKE}", "LIKE");
            datatypeConversion_.put("{BINARY}", "");
            datatypeConversion_.put("{CASCADE}", "");
            datatypeConversion_.put("{lgCharSet}", "");
            datatypeConversion_.put("{lgTableCharSet}", "");
            datatypeConversion_.put("{dateTime}", "TIMESTAMP");
            datatypeConversion_.put("{AS}", "");
            datatypeConversion_.put("[limitedTextLimit]", "4000");
        }
        else
        {
            throw new Exception("Unsupported database type '" + databaseType_ + "' in the GenericSQLModifier.");
        }
    }

    /**
     * 
     * @return what type of database it is.
     */
    public String getDatabaseType()
    {
        return databaseType_;
    }

    /**
     * 
     * @param sql
     * @param logResult log result if true
     * @return the modified sql statement
     */
    public String modifySQL(String sql, boolean logResult)
    {
        StringBuffer result = new StringBuffer(sql);

        // fix the quotes
        int pos = result.indexOf("^");
        while (pos != -1)
        {
            result.replace(pos, pos + 1, properQuote_);
            pos = result.indexOf("^");
        }

        // UCase DB2 requires UCASE (which requires a varchar cast).
        if (databaseType_.startsWith("DB2"))
        {
            // get rid of multiple spaces
            pos = result.indexOf("  ");
            while (pos != -1)
            {
                result.replace(pos, pos + 2, " ");
                pos = result.indexOf("  ");
            }

            // need to shove 'UCASE(varchar(' onto the front of the variable name preceding the LIKE,
            // and '))' onto the back
            pos = 0;
            int pos2 = result.indexOf("{LIKE}", pos);
            while (pos2 != -1)
            {
                StringBuffer temp = new StringBuffer(result.substring(0, pos2));
                int lastSpace = temp.lastIndexOf(" ");
                temp.setLength(lastSpace);
                int secondLastSpace = temp.lastIndexOf(" ") + 1;

                // don't wrap open parens
                while (temp.charAt(secondLastSpace) == '(')
                {
                    secondLastSpace++;
                }

                result.insert(secondLastSpace, "UCASE(varchar(");
                lastSpace += "UCASE(varchar(".length();
                result.insert(lastSpace, "))");
                pos = pos2 + "UCASE(varchar())".length() + "{LIKE}".length();
                pos2 = result.indexOf("{LIKE}", pos);
            }
        }
        
        // Oracle requires UPPER.
        else if (databaseType_.startsWith("Oracle"))
        {
            // get rid of multiple spaces
            pos = result.indexOf("  ");
            while (pos != -1)
            {
                result.replace(pos, pos + 2, " ");
                pos = result.indexOf("  ");
            }

            // need to shove 'UPPER(' onto the front of the variable name preceding the LIKE,
            // and ')' onto the back
            pos = 0;
            int pos2 = result.indexOf("{LIKE}", pos);
            while (pos2 != -1)
            {
                StringBuffer temp = new StringBuffer(result.substring(0, pos2));
                int lastSpace = temp.lastIndexOf(" ");
                temp.setLength(lastSpace);
                int secondLastSpace = temp.lastIndexOf(" ") + 1;

                // don't wrap open parens
                while (temp.charAt(secondLastSpace) == '(')
                {
                    secondLastSpace++;
                }

                result.insert(secondLastSpace, "UPPER(");
                lastSpace += "UPPER(".length();
                result.insert(lastSpace, ")");
                pos = pos2 + "UPPER()".length() + "{LIKE}".length();
                pos2 = result.indexOf("{LIKE}", pos);
            }
        }

        // modify the datatypes
        for (int i = 0; i < customDatatypes_.length; i++)
        {
            pos = result.indexOf(customDatatypes_[i]);
            while (pos != -1)
            {
                String dataType = (String) datatypeConversion_.get(customDatatypes_[i]);
                if (customDatatypes_[i].equals("{limitedText}") && datatypeConversion_.get("[limitedTextLimit]") != null)
                {
                    try
                    {
                        //automatically change limitedText into unlimited text if the size is greater
                        //than the limit.
                        int max = Integer.parseInt((String)datatypeConversion_.get("[limitedTextLimit]"));
                        //get the (optional) "(100)" size that follows the datatype specification.
                        int start = pos + customDatatypes_[i].length();
                        int end = result.indexOf(")", start);
                        String temp = result.substring(start, end).trim();
                        if (temp.startsWith("("))
                        {
                            temp = temp.substring(1);
                        }
                        int length = Integer.parseInt(temp);
                        if (length > max)
                        {
                            dataType = (String) datatypeConversion_.get("{unlimitedText}");
                            //remove the size variable
                            result.replace(start, end + 1, "");
                        }
                    }
                    catch (Exception e)
                    {
                        //don't want to fail here.  Just do what it used to do.
                        dataType = (String) datatypeConversion_.get(customDatatypes_[i]);
                    }
                    
                    
                }
                result.replace(pos, pos + customDatatypes_[i].length(), dataType);
                pos = result.indexOf(customDatatypes_[i]);
            }
        }

        // remove all the defaults...
        if (databaseType_.equals("ACCESS"))
        {
            pos = result.indexOf(" default ");
            while (pos != -1)
            {
                int pos2 = result.indexOf(",", pos);
                if (pos2 == -1)
                {
                    // not a comma, maybe a close paren?
                    pos2 = result.indexOf(")", pos);
                }
                result.replace(pos, pos2, "");
                pos = result.indexOf(" default ");
            }
        }

        if (logResult)
        {
            log.debug(result.toString());
        }
        return result.toString();
    }

    public String modifySQL(String sql)
    {
        return modifySQL(sql, true);
    }
    
    public boolean requiresLikeQueryTextToBeUpperCased()
    {
        if (databaseType_.startsWith("DB2") || databaseType_.startsWith("Oracle"))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    public static void main(String[] args) throws Exception
    {
        GenericSQLModifier foo = new GenericSQLModifier("DB2/NT");
        System.out.println(foo.modifySQL("Select  * from a where b.c {LIKE} ?"));
        System.out.println(foo.modifySQL("Select * from a where foobar.me {LIKE} ? AND barnone {LIKE} ?"));
        System.out.println(foo.modifySQL("Select * from a where a = ? AND (barnone {LIKE} OR foo {LIKE} ?)"));

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy