View Javadoc

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