001    /**
002     * Copyright (c) 2010 Yahoo! Inc. All rights reserved.
003     * Licensed under the Apache License, Version 2.0 (the "License");
004     * you may not use this file except in compliance with the License.
005     * You may obtain a copy of the License at
006     *
007     *   http://www.apache.org/licenses/LICENSE-2.0
008     *
009     *  Unless required by applicable law or agreed to in writing, software
010     *  distributed under the License is distributed on an "AS IS" BASIS,
011     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012     *  See the License for the specific language governing permissions and
013     *  limitations under the License. See accompanying LICENSE file.
014     */
015    package org.apache.oozie.util.db;
016    
017    import java.sql.Connection;
018    import java.sql.SQLException;
019    
020    import java.sql.Blob;
021    import java.sql.Timestamp;
022    import java.util.List;
023    
024    public class Schema {
025        /**
026         * Interface for DB Table
027         */
028        public static interface Table {
029            /**
030             * Name of the Table
031             *
032             * @return
033             */
034            String name();
035        }
036    
037        /**
038         * Interface or table columns
039         */
040        public static interface Column {
041            /**
042             * Table to which the column belongs
043             *
044             * @return table name
045             */
046            Table table();
047    
048            /**
049             * Alias to be used by the select statement for this column
050             *
051             * @return alias for column
052             */
053            String asLabel();
054    
055            /**
056             * Name of the column
057             *
058             * @return column name
059             */
060            String columnName();
061    
062            /**
063             * Returns the datatype of the column
064             *
065             * @return column type
066             */
067            Class<?> getType();
068    
069            /**
070             * Returns the length of the column
071             *
072             * @return
073             */
074            int getLength();
075    
076            /**
077             * Returns if the field is a primary key or not
078             *
079             * @return true if field is a primary key
080             */
081            boolean isPrimaryKey();
082        }
083    
084        /**
085         * Interface for Index
086         */
087        public static interface Index {
088            /**
089             * Column that is to be indexed
090             *
091             * @return
092             */
093            Column column();
094        }
095    
096        /**
097         * DB types
098         */
099        public static enum DBType {
100            HSQL, MySQL, ORACLE;
101        }
102    
103        //TODO Add the SQL Change catalog for different DBMS.
104        /**
105         * Returns the appropriate DB type for given column according to the DB Type
106         *
107         * @param column
108         * @param dbType
109         * @return column type
110         */
111        public static String getDbDataType(Column column, DBType dbType) {
112            String retVal = null;
113            if (String.class.equals(column.getType())) {
114                if (column.getLength() < 0) {
115                    retVal = (dbType.equals(DBType.HSQL) ? "VARCHAR" : (dbType.equals(DBType.ORACLE) ? "CLOB" : "TEXT"));
116                }
117                else {
118                    retVal = (dbType.equals(DBType.ORACLE) ? "VARCHAR2(" + column.getLength() + ")" : "VARCHAR(" + column.getLength() + ")");
119                }
120            }
121            else {
122                if (Timestamp.class.equals(column.getType())) {
123                    retVal = (dbType.equals(DBType.ORACLE) ? "DATE" : "DATETIME");
124                }
125                else {
126                    if (Boolean.class.equals(column.getType())) {
127                        retVal = (dbType.equals(DBType.ORACLE) ? "NUMBER(3, 0)" : "BOOLEAN");
128                    }
129                    else {
130                        if (Long.class.equals(column.getType())) {
131                            retVal = (dbType.equals(DBType.ORACLE) ? "NUMBER (19,0)" : "BIGINT");
132                        }
133                        else {
134                            if (Blob.class.equals(column.getType())) {
135                                retVal = (dbType.equals(DBType.MySQL) ? "MEDIUMBLOB" : (dbType.equals(DBType.ORACLE) ? "BLOB" : "LONGVARBINARY"));
136                            }
137                            else {
138                                throw new RuntimeException("Column Type[" + column.getType() + "] not mapped to any DB Data Type !!");
139                            }
140                        }
141                    }
142                }
143            }
144            return retVal;
145        }
146    
147        /**
148         * Generates the SQL Statement for creating the table
149         *
150         * @param table
151         * @param dbType
152         * @param tableColumns
153         * @return CREATE TABLE SQL Statement
154         */
155        public static String generateCreateTableScript(Table table, DBType dbType, List<Column> tableColumns) {
156            StringBuilder sb = new StringBuilder("CREATE TABLE ").append(table).append(" ( ");
157            StringBuilder pk = new StringBuilder(", PRIMARY KEY ( ");
158            boolean pkFlag = false;
159            String sep = "";
160            String psep = "";
161            for (Column column : tableColumns) {
162                sb.append(sep).append(column.columnName() + " ").append(Schema.getDbDataType(column, dbType));
163                if (column.isPrimaryKey()) {
164                    pkFlag = true;
165                    pk.append(psep).append(column.columnName());
166                    psep = ", ";
167                }
168                sep = ", ";
169            }
170            if (pkFlag) {
171                pk.append(" )");
172                sb.append(pk.toString());
173            }
174            sb.append(" )");
175            if (dbType == DBType.MySQL) {
176                sb.append(" ENGINE=InnoDB");
177            }
178            return sb.toString();
179        }
180    
181        /**
182         * Generates the SQL Statement for droping the table
183         *
184         * @param table
185         * @param dbType
186         * @return DROP TABLE SQL Statement
187         */
188        public static String generateDropTableScript(Table table, DBType dbType) {
189            StringBuilder sb = new StringBuilder("DROP TABLE ").append(table);
190            if (dbType == DBType.ORACLE) {
191                sb.append(" purge");
192            }
193            return sb.toString();
194        }
195    
196    
197        /**
198         * Generates the SQL statement for creating the Index
199         *
200         * @param index
201         * @param dbType
202         * @return CREATE INDEX SQL Statement
203         */
204        public static String generateCreateIndexScript(Index index, DBType dbType) {
205            StringBuilder sb = new StringBuilder("CREATE INDEX ").append(index).append(" ON ").append(
206                    index.column().table()).append("( " + index.column().columnName() + " )");
207            return sb.toString();
208        }
209    
210        /**
211         * Checks if the given connection's driver is HSQL Database Driver
212         *
213         * @param conn
214         * @return true if the driver is HSQL
215         * @throws SQLException
216         */
217        public static boolean isHsqlConnection(Connection conn) throws SQLException {
218            if (conn.getMetaData().getDriverName().toLowerCase().contains(DBType.HSQL.name().toLowerCase())) {
219                return true;
220            }
221            return false;
222        }
223    
224        /**
225         * Checks if the given connection's driver is MySQL Database Driver
226         *
227         * @param conn
228         * @return true if the driver is MySQL
229         * @throws SQLException
230         */
231        public static boolean isMySqlConnection(Connection conn) throws SQLException {
232            if (conn.getMetaData().getDriverName().toLowerCase().contains(DBType.MySQL.name().toLowerCase())) {
233                return true;
234            }
235            return false;
236        }
237    }