View Javadoc
1   /*
2    * $Source$
3    * $Revision$
4    *
5    * Copyright (C) 2002 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.Connection;
48  import java.sql.Statement;
49  import java.sql.SQLException;
50  
51  import org.melati.poem.BinaryPoemType;
52  import org.melati.poem.Column;
53  import org.melati.poem.PoemType;
54  import org.melati.poem.StringPoemType;
55  import org.melati.poem.Table;
56  import org.melati.poem.util.StringUtils;
57  
58  /**
59   * A Driver for HSQLDB (http://www.hsqldb.org/).
60   *
61   * Note that HSQLDB uppercases any name that isn't quoted. 
62   *
63   **/
64  
65  public class Hsqldb extends AnsiStandard {
66  
67    // 2010/10/25 Have experienced issues with records no being committed
68    //       st.execute("SET WRITE_DELAY FALSE");
69    // had no effect
70  
71    /**
72     * HSQLDB does not have a pleasant <code>TEXT</code> 
73     * datatype, so we use an arbitrary value in a 
74     * <code>VARCHAR</code>.
75     */
76    public static int hsqldbTextHack = 266;
77    // Version 2.2.8 introduces a text field
78    public static int hsqldbTextSize1 = 1048576;
79    public static int hsqldbTextSize2 = 16777216;
80  
81    /**
82     * Constructor.
83     */
84    public Hsqldb() {
85      setDriverClassName("org.hsqldb.jdbcDriver");
86    }
87  
88    /**
89     * Shut the db down nicely.
90     */
91    @Override
92    public void shutdown(Connection connection)  throws SQLException { 
93      if (!connection.isClosed()) {
94        Statement st = connection.createStatement();
95        try { 
96          st.execute("SHUTDOWN SCRIPT");
97        } catch (SQLException e) { 
98          // This seems to be caused by a shutdownhook race
99          if (!e.getMessage().equals("Access is denied: Session is closed")) 
100           throw e;
101       } finally { 
102         st.close();
103       }
104     }
105   }
106 
107   /** 
108    * The default is to keep everything in memory,
109    * this allows for the db to be written to the disk.
110    * 
111    */
112   @Override
113   public String createTableTypeQualifierSql(Table<?> table) {
114     String tableType;
115     if (table == null || table.getDbmsTableType() == null)
116       tableType = "CACHED ";
117     else
118       tableType = table.getDbmsTableType() + " "; 
119       
120     return tableType;
121   }
122 
123   /*
124    *  0.7.2 and earlier did not have a Boolean type; 
125    *  there is one in 0.7.3 onwards. 
126    *   
127    * @see org.melati.poem.dbms.Dbms#getSqlDefinition(java.lang.String)
128    */
129 
130   /*
131    public String getSqlDefinition(String sqlTypeName) {
132     if (sqlTypeName.equals("BOOLEAN")) {
133       return ("BIT");
134     }
135     return super.getSqlDefinition(sqlTypeName);
136   }
137   */
138   /**
139    * @see org.melati.poem.dbms.AnsiStandard#getStringSqlDefinition(int)
140    */
141   @Override
142   public String getStringSqlDefinition(int size) {
143     if (size < 0)
144       return "LONGVARCHAR";
145       //return "VARCHAR(" + hsqldbTextHack + ")";
146     return "VARCHAR(" + size + ")";
147   }
148 
149   /**
150    * {@inheritDoc}
151    * @see org.melati.poem.dbms.AnsiStandard#getLongSqlDefinition()
152    */
153   @Override
154   public String getLongSqlDefinition() {
155     return "BIGINT";
156   }
157 
158   /**
159    * {@inheritDoc}
160    * @see org.melati.poem.dbms.AnsiStandard#getBinarySqlDefinition(int)
161    */
162   @Override
163   public String getBinarySqlDefinition(int size) throws SQLException {
164     return "LONGVARBINARY";
165   }
166 
167   /**
168    * Accommodate our String size hack. 
169    * {@inheritDoc}
170    * @see org.melati.poem.dbms.AnsiStandard#canRepresent
171    */
172   @Override
173   public <S,O>PoemType<O> canRepresent(PoemType<S> storage, PoemType<O> type) {
174     if (storage instanceof StringPoemType && type instanceof StringPoemType) {
175       if (
176           (((StringPoemType)storage).getSize() == hsqldbTextHack
177           || 
178           ((StringPoemType)storage).getSize() == hsqldbTextSize1
179           || 
180           ((StringPoemType)storage).getSize() == hsqldbTextSize2
181           )
182               && ((StringPoemType)type).getSize() == -1
183               && !(!storage.getNullable() && type.getNullable())  // Nullable may represent not nullable
184       ) {
185         return type;
186       } else {
187         return storage.canRepresent(type);
188       }
189     } else if (storage instanceof BinaryPoemType && type instanceof BinaryPoemType) {
190       if (
191            (
192              (((BinaryPoemType)storage).getSize() == 0)
193              || 
194              (((BinaryPoemType)storage).getSize() > ((BinaryPoemType)type).getSize())
195            )
196            && 
197            !(!storage.getNullable() && type.getNullable())  // Nullable may represent not nullable
198       ) {
199         return type;
200       } else {
201         return storage.canRepresent(type);
202       }
203     } else {
204       return super.canRepresent(storage, type);
205     }
206   }
207 
208   /**
209    * {@inheritDoc}
210    * @see org.melati.poem.dbms.AnsiStandard#unreservedName(java.lang.String)
211    */
212   @Override
213   public String unreservedName(String name) {
214     if(name.equalsIgnoreCase("UNIQUE")) name = "MELATI_" + name.toUpperCase();
215     if(name.equalsIgnoreCase("CONSTRAINT")) name = "MELATI_" + name.toUpperCase();
216     //if(name.equalsIgnoreCase("USERS")) name = "MELATI_" + name.toUpperCase();
217     return name.toUpperCase();
218   }
219 
220   /**
221    * {@inheritDoc}
222    * @see org.melati.poem.dbms.AnsiStandard#melatiName(java.lang.String)
223    */
224   @Override
225   public String melatiName(String name) {
226     if (name == null) return name;
227     if(name.equalsIgnoreCase("MELATI_UNIQUE")) name = "UNIQUE";
228     if(name.equalsIgnoreCase("MELATI_CONSTRAINT")) name = "CONSTRAINT";
229     //if(name.equalsIgnoreCase("MELATI_USERS")) name = "USERS";
230     return name.toLowerCase();
231   }
232   
233   /** 
234    * Note that this is NOT case insensitive.
235    * 
236    * {@inheritDoc}
237    * 
238    * @see org.melati.poem.dbms.Dbms#caseInsensitiveRegExpSQL
239    */
240   @Override
241   public String caseInsensitiveRegExpSQL(String term1, String term2) {
242     if (StringUtils.isQuoted(term2)) {
243       term2 = term2.substring(1, term2.length() - 1);
244     } 
245     term2 = StringUtils.quoted(StringUtils.quoted(term2, '%'), '\'');
246     
247     return term1 + " LIKE " + term2;
248   }
249   
250   /**
251    * {@inheritDoc}
252    * @see org.melati.poem.dbms.AnsiStandard#getForeignKeyDefinition
253    */
254   @Override
255   public String getForeignKeyDefinition(String tableName, String fieldName, 
256                                         String targetTableName, 
257                                         String targetTableFieldName, 
258                                         String fixName) {
259     StringBuffer sb = new StringBuffer();
260     sb.append(" ADD FOREIGN KEY (" + getQuotedName(fieldName) + ") REFERENCES " + 
261               getQuotedName(targetTableName) + 
262               "(" + getQuotedName(targetTableFieldName) + ")");
263     // Not currently implemented by hsqldb, 
264     //another reason for not using the DB to control these things
265     //if (fixName.equals("prevent"))
266     //  sb.append(" ON DELETE NO ACTION");
267     // There is an "ON DELETE SET DEFAULT" 
268     
269     if (fixName.equals("delete"))
270       sb.append(" ON DELETE CASCADE");      
271     if (fixName.equals("clear"))
272       sb.append(" ON DELETE SET NULL");
273     
274     return sb.toString();
275   }
276 
277   /**
278    * Bad smell.
279    * {@inheritDoc}
280    * @see org.melati.poem.dbms.AnsiStandard#getJdbcMetadataName(java.lang.String)
281    */
282   @Override
283   public String getJdbcMetadataName(String name) {
284     return name.toUpperCase();
285   }
286   /**
287    * Our current version does not honour COMMENT. 
288    */
289    @Override
290    public String alterColumnAddCommentSQL(Column<?> column, String comment) {
291      return null;
292    }
293 
294 }