001: /**
002: * Copyright (C) 2006 NetMind Consulting Bt.
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 3 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */package hu.netmind.persistence;
018:
019: import java.sql.*;
020: import javax.sql.*;
021: import java.util.*;
022: import org.apache.log4j.Logger;
023: import hu.netmind.persistence.parser.Expression;
024:
025: /**
026: * Derby database implementation.
027: * Note: Implementation is not finished, do not use! Major problem:
028: * can not test, because can not handle shutdown-restart. Another
029: * problem is, that it can not support limit-offset construct.
030: * @author Brautigam Robert
031: * @version CVS Revision: $Revision$
032: */
033: public class DerbyDatabaseImpl extends GenericDatabase {
034: private static Logger logger = Logger
035: .getLogger(DerbyDatabaseImpl.class);
036:
037: /**
038: * Get the class for an sql type.
039: */
040: protected String getSQLTypeName(int sqltype) {
041: switch (sqltype) {
042: case Types.VARCHAR:
043: return "varchar(32672)";
044: case Types.BOOLEAN:
045: return "numeric(1,0)";
046: default:
047: return super .getSQLTypeName(sqltype);
048: }
049: }
050:
051: /**
052: * Throw exception when String is longer than Oracle can handle.
053: */
054: protected Object getSQLValue(Object value) {
055: if (value instanceof Boolean)
056: return new Integer(((Boolean) value).booleanValue() ? 1 : 0);
057: if (value instanceof Character)
058: return "" + value;
059: return super .getSQLValue(value);
060: }
061:
062: /**
063: * Convert incoming value from database into java format.
064: */
065: protected Object getJavaValue(Object value, int type, Class javaType) {
066: try {
067: if (value == null)
068: return null;
069: logger.debug("transforming value: " + value + ", type: "
070: + type + ", java type: " + javaType);
071: if ((Boolean.class.equals(javaType))
072: || (boolean.class.equals(javaType)))
073: return new Boolean(((Number) value).intValue() > 0);
074: if (value instanceof Blob)
075: return ((Blob) value).getBytes(1, (int) ((Blob) value)
076: .length());
077: return super .getJavaValue(value, type, javaType);
078: } catch (StoreException e) {
079: throw e;
080: } catch (Exception e) {
081: throw new StoreException(
082: "conversion error tried to convert: " + value
083: + ", of sql type: " + type, e);
084: }
085: }
086:
087: /**
088: * Transform 'ilike' to upper case like.
089: * @param expr The expression to possibly transform.
090: * @return A transformed expression.
091: */
092: protected Expression transformExpression(Expression expr) {
093: Expression result = new Expression(expr);
094: result.clear();
095: for (int i = 0; i < expr.size(); i++) {
096: Object item = expr.get(i);
097: if ("ilike".equals(item)) {
098: // Here we need to upper() the argument before and after like
099: Object arg = result.removeLast();
100: result.add("upper(");
101: result.add(arg);
102: result.add(")");
103: result.add("like");
104: result.add("upper(");
105: result.add(expr.get(i + 1));
106: result.add(")");
107: i++; // We used an argument
108: } else {
109: result.add(item);
110: }
111: }
112: return result;
113: }
114:
115: /**
116: * Get the data types of a given table.
117: * @return A map of names with the sql type number as value.
118: */
119: protected HashMap getTableAttributeTypes(Connection connection,
120: String tableName) throws SQLException {
121: return super .getTableAttributeTypes(connection, tableName
122: .toUpperCase());
123: }
124:
125: /**
126: * Fix custom data types not supported by database.
127: */
128: protected int getTableAttributeType(ResultSet rs)
129: throws SQLException {
130: int columnType = rs.getInt("DATA_TYPE");
131: // Recognize boolean type
132: if ((columnType == Types.NUMERIC)
133: && (rs.getInt("COLUMN_SIZE") == 1))
134: columnType = Types.BOOLEAN;
135: return columnType;
136: }
137:
138: /**
139: * Get an unused index name.
140: */
141: protected String getCreateIndexName(Connection connection,
142: String tableName, String field) {
143: return super .getCreateIndexName(connection, tableName
144: .toUpperCase(), field);
145: }
146:
147: /**
148: * Drop a column from a table. Derby does not drop the column if
149: * there are indexes to it, so first remove the indexes in question.
150: * @param connection The connection object.
151: * @param tableName The table to drop column from.
152: * @param columnName The column to drop.
153: */
154: protected DatabaseStatistics dropColumn(Connection connection,
155: String tableName, String columnName) {
156: DatabaseStatistics stats = new DatabaseStatistics();
157: try {
158: logger.debug("detemining indexes before dropping column: "
159: + columnName);
160: // Determine indexes for given column, and remove them mercilessly
161: long startTime = System.currentTimeMillis();
162: DatabaseMetaData dmd = connection.getMetaData();
163: ResultSet rs = dmd.getIndexInfo(null, null, tableName
164: .toUpperCase(), false, false);
165: while (rs.next()) {
166: if (logger.isDebugEnabled())
167: logger.debug("got index '"
168: + rs.getString("INDEX_NAME")
169: + "', it's column is: "
170: + rs.getString("COLUMN_NAME"));
171: // Got index, but only drop it, when it is about our petit column
172: if (columnName.equalsIgnoreCase(rs
173: .getString("COLUMN_NAME"))) {
174: // It is about the column given, so drop it, for great justice
175: executeUpdate(connection, "drop index "
176: + rs.getString("INDEX_NAME"));
177: stats.setSchemaCount(stats.getSchemaCount() + 1);
178: }
179: }
180: long endTime = System.currentTimeMillis();
181: stats.setSchemaTime(endTime - startTime);
182: stats.setSchemaCount(stats.getSchemaCount() + 1);
183: } catch (SQLException e) {
184: logger.error(
185: "error while trying to remove indexes for column: "
186: + tableName + "." + columnName, e);
187: }
188: // Now we can drop the column
189: stats.add(super .dropColumn(connection, tableName, columnName));
190: return stats;
191: }
192:
193: /**
194: * Prepare the sql statment to be executed.
195: */
196: protected void prepareStatement(PreparedStatement pstmt,
197: Limits limits) throws SQLException {
198: if ((limits == null) || (limits.isEmpty()))
199: return;
200: pstmt
201: .setMaxRows((int) (limits.getOffset() + limits
202: .getLimit()));
203: }
204:
205: /**
206: * Prepare the result set to be iterated.
207: */
208: protected void prepareResultSet(ResultSet rs, Limits limits)
209: throws SQLException {
210: if ((limits == null) || (limits.isEmpty()))
211: return;
212: long skip = limits.getOffset(); // Skip this much rows
213: while ((skip > 0) && (rs.next()))
214: skip--;
215: }
216:
217: /**
218: * Shutdown derby.
219: */
220: public void release(ConnectionSource source) {
221: DataSource dataSource = source.getDataSource();
222: if (dataSource instanceof DriverDataSource) {
223: try {
224: // Derby is allocated through url, so we need to
225: // close that database.
226: ((DriverDataSource) dataSource)
227: .getConnection("shutdown=true");
228: } catch (SQLException e) {
229: // Derby issues an exception, even if the shutdown was successful,
230: // so only the message is printed.
231: logger.debug("shutdown derby database resulted in: "
232: + e.getMessage());
233: }
234: }
235: }
236:
237: }
|