View Javadoc
1   /*
2    * Copyright (C) 2004 Tim Pizey
3    * 
4    * Part of Melati (http://melati.org), a framework for the rapid
5    * development of clean, maintainable web applications.
6    *
7    * Melati is free software; Permission is granted to copy, distribute
8    * and/or modify this software under the terms either:
9    *
10   * a) the GNU General Public License as published by the Free Software
11   *    Foundation; either version 2 of the License, or (at your option)
12   *    any later version,
13   *
14   *    or
15   *
16   * b) any version of the Melati Software License, as published
17   *    at http://melati.org
18   *
19   * You should have received a copy of the GNU General Public License and
20   * the Melati Software License along with this program;
21   * if not, write to the Free Software Foundation, Inc.,
22   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
23   * GNU General Public License and visit http://melati.org to obtain the
24   * Melati Software License.
25   *
26   * Feel free to contact the Developers of Melati (http://melati.org),
27   * if you would like to work out a different arrangement than the options
28   * outlined here.  It is our intention to allow Melati to be used by as
29   * wide an audience as possible.
30   *
31   * This program is distributed in the hope that it will be useful,
32   * but WITHOUT ANY WARRANTY; without even the implied warranty of
33   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
34   * GNU General Public License for more details.
35   *
36   * Contact details for copyright holder:
37   *
38   *     Tim Pizey (timp At paneris.org)
39   *
40   */
41  
42  package org.melati.poem.dbms;
43  
44  import org.melati.poem.*;
45  import org.melati.poem.SQLType;
46  
47  import java.sql.*;
48  
49  /**
50   * A Driver for Oracle (http://www.oracle.com/).
51   */
52  public class Oracle extends AnsiStandard {
53  
54    /**
55     * Oracle does not have a pleasant <code>TEXT</code>
56     * datatype, so we use an arbetary value in a
57     * <code>VARCHAR</code>.
58     */
59    public static int oracleTextHack = 4000;
60  
61    /**
62     * Constructor.
63     */
64    public Oracle() {
65      setDriverClassName("oracle.jdbc.OracleDriver");
66    }
67  
68    /*
69  
70    public String preparedStatementPlaceholder(PoemType type) {
71      if (type instanceof IntegerPoemType)
72        return "CAST(? AS INT4)";
73      else if (type instanceof LongPoemType)
74        return "CAST(? AS INT8)";
75      else if (type instanceof DoublePoemType)
76        return "CAST(? AS FLOAT8)";
77      else 
78        return "?";
79    }
80  */
81  
82    /**
83     * Accommodate String/CLOB distinction.
84     *
85     * @param size the string length (-1 means no limit)
86     * @return the SQL definition for a string of this size
87     * @see org.melati.poem.dbms.AnsiStandard#getStringSqlDefinition(int)
88     */
89    @Override
90    public String getStringSqlDefinition(int size) throws SQLException {
91      if (size < 0) {
92        return "CLOB";
93      }
94      return super.getStringSqlDefinition(size);
95    }
96  
97    @Override
98    public String getLongSqlDefinition() {
99      return "NUMBER";
100   }
101 
102   @Override
103   public String getSqlDefinition(String sqlTypeName) {
104     if (sqlTypeName.equals("BOOLEAN")) {
105       return ("CHAR(1)");
106     }
107     return super.getSqlDefinition(sqlTypeName);
108   }
109 
110   @Override
111   public String sqlBooleanValueOfRaw(Object raw) {
112     if ((Boolean) raw)
113       return "1";
114     else
115       return "0";
116   }
117 
118   /**
119    * Translates a Oracle String into a Poem <code>StringPoemType</code>.
120    */
121   public static class OracleStringPoemType extends StringPoemType {
122 
123     /**
124      * Constructor.
125      *
126      * @param nullable nullability
127      * @param size     size
128      */
129     public OracleStringPoemType(boolean nullable, int size) {
130       super(nullable, size);
131     }
132 
133     protected boolean _canRepresent(SQLPoemType<?> other) {
134       return ((sqlTypeCode() == Types.VARCHAR || sqlTypeCode() == Types.CLOB)
135           && (other.sqlTypeCode() == Types.VARCHAR || other.sqlTypeCode() == Types.CLOB)
136       ) &&
137           (
138               (getSize() == oracleTextHack && ((StringPoemType) other).getSize() == -1)
139                   ||
140                   (getSize() == -1 &&
141                       ((StringPoemType) other).getSize() == oracleTextHack)
142                   ||
143                   ((getSize() >= ((StringPoemType) other).getSize())));
144     }
145 
146     /**
147      * {@inheritDoc}
148      *
149      * @see org.melati.poem.BasePoemType#canRepresent(PoemType)
150      */
151     public <O> PoemType<O> canRepresent(PoemType<O> other) {
152       return other instanceof StringPoemType &&
153           _canRepresent((StringPoemType) other) &&
154           !(!getNullable() && (other).getNullable()) ?
155           other : null;
156     }
157 
158   }
159 
160   @Override
161   public String getBinarySqlDefinition(int size) throws SQLException {
162     return "BLOB";
163   }
164 
165   @Override
166   public String unreservedName(String name) {
167     if (name.equalsIgnoreCase("user")) name = "melati_" + name;
168     if (name.equalsIgnoreCase("group")) name = "melati_" + name;
169     return name.toUpperCase();
170   }
171 
172   @Override
173   public String melatiName(String name) {
174     if (name == null) return null;
175     if (name.equalsIgnoreCase("melati_user")) name = "user";
176     if (name.equalsIgnoreCase("melati_group")) name = "group";
177     return name.toLowerCase();
178   }
179 
180   @Override
181   public <S, O> PoemType<O> canRepresent(PoemType<S> storage, PoemType<O> type) {
182     if ((storage instanceof IntegerPoemType &&
183         type instanceof BigDecimalPoemType) &&
184         !(!storage.getNullable() && type.getNullable())) {
185       return type;
186     }
187     if ((storage instanceof IntegerPoemType &&
188         type instanceof LongPoemType) &&
189         !(!storage.getNullable() && type.getNullable())) {
190       return type;
191     } else {
192       return storage.canRepresent(type);
193     }
194   }
195 
196   @Override
197   public SQLPoemType<?> defaultPoemTypeOfColumnMetaData(ResultSet md)
198       throws SQLException {
199     System.err.println("TYPE_NAME:" + md.getString("TYPE_NAME"));
200     //ResultSetMetaData rsmd = md.getMetaData();
201     //int cols = rsmd.getColumnCount();
202     //for (int i = 1; i <= cols; i++) {
203     //String table = rsmd.getTableName(i);
204     //System.err.println("table name: " + table);
205     //String column = rsmd.getColumnName(i);
206     //System.err.println("column name: " + column);
207     //int type = rsmd.getColumnType(i);
208     //System.err.println("type: " + type);
209     //String typeName = rsmd.getColumnTypeName(i);
210     //System.err.println("type Name: " + typeName);
211     //String className = rsmd.getColumnClassName(i);
212     //System.err.println("class Name: " + className);
213     //System.err.println("String val: " + md.getString(i));
214     //System.err.println("");
215     //}
216 
217     if (md.getString("TYPE_NAME").equals("VARCHAR2"))
218       return
219           new OracleStringPoemType(
220               md.getInt("NULLABLE") ==
221                   DatabaseMetaData.columnNullable,
222               md.getInt("COLUMN_SIZE"));
223     if (md.getString("TYPE_NAME").equals("CLOB"))
224       return
225           new OracleStringPoemType(
226               md.getInt("NULLABLE") ==
227                   DatabaseMetaData.columnNullable,
228               md.getInt("COLUMN_SIZE"));
229     if (md.getString("TYPE_NAME").equals("CHAR"))
230       return
231           new OracleBooleanPoemType(
232               md.getInt("NULLABLE") ==
233                   DatabaseMetaData.columnNullable);
234     if (md.getString("TYPE_NAME").equals("BLOB"))
235       return new BinaryPoemType(
236           md.getInt("NULLABLE") == DatabaseMetaData.columnNullable,
237           md.getInt("COLUMN_SIZE"));
238     if (md.getString("TYPE_NAME").equals("FLOAT"))
239       return new DoublePoemType(
240           md.getInt("NULLABLE") == DatabaseMetaData.columnNullable);
241     if (
242         md.getString("TYPE_NAME").equals("NUMBER"))
243       return new IntegerPoemType(
244           md.getInt("NULLABLE") == DatabaseMetaData.columnNullable);
245 
246     return super.defaultPoemTypeOfColumnMetaData(md);
247   }
248 
249   @Override
250   public String getForeignKeyDefinition(String tableName, String fieldName,
251                                         String targetTableName, String targetTableFieldName, String fixName) {
252     String q = " ADD (CONSTRAINT FK_" + tableName + "_" + fieldName + ") " +
253         "FOREIGN KEY (" + getQuotedName(fieldName) + ") " +
254         "REFERENCES " + getQuotedName(targetTableName) +
255         "(" + getQuotedName(targetTableFieldName) + ")";
256     // Not currently implemented by Oracle,
257     // another reason for not using the DB to control these things
258     //if (fixName.equals("prevent"))
259     //  q += " ON DELETE NO ACTION";
260     if (fixName.equals("delete"))
261       q += " ON DELETE CASCADE";
262     if (fixName.equals("clear"))
263       q += " ON DELETE SET NULL";
264     return q;
265   }
266 
267   @Override
268   public String getPrimaryKeyDefinition(String fieldName) {
269     return " ADD (CONSTRAINT PK_" + fieldName +
270         " PRIMARY KEY(" + getQuotedName(fieldName) + "))";
271   }
272 
273   @Override
274   public String booleanTrueExpression(Column<Boolean> booleanColumn) {
275     return booleanColumn.fullQuotedName() + "=1";
276   }
277 
278   @Override
279   public String getSqlDefaultValue(SQLType<?> sqlType) {
280     if (sqlType instanceof BooleanPoemType) {
281       return ("0");
282     }
283     return super.getSqlDefaultValue(sqlType);
284   }
285 
286   /**
287    * Translates an Oracle Boolean into a Poem <code>BooleanPoemType</code>.
288    */
289   public static class OracleBooleanPoemType extends BooleanPoemType {
290 
291     /**
292      * Constructor.
293      *
294      * @param nullable nullability
295      */
296     public OracleBooleanPoemType(boolean nullable) {
297       super(nullable);
298     }
299 
300     protected Boolean _getRaw(ResultSet rs, int col) throws SQLException {
301       synchronized (rs) {
302         boolean v = rs.getBoolean(col);
303         return rs.wasNull() ? null : (v ? Boolean.TRUE : Boolean.FALSE);
304       }
305     }
306 
307     protected void _setRaw(PreparedStatement ps, int col, Object bool)
308         throws SQLException {
309       ps.setInt(col, (Boolean) bool ? 1 : 0);
310     }
311 
312   }
313 }