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 }