Coverage Report - org.melati.poem.dbms.AnsiStandard
 
Classes in this File Line Coverage Branch Coverage Complexity
AnsiStandard
77%
174/225
70%
87/123
4.289
 
 1  
 /*
 2  
  * $Source$
 3  
  * $Revision$
 4  
  *
 5  
  * Copyright (C) 2000 David Warnock
 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  
  *     David Warnock (david At sundayta.co.uk)
 42  
  *     Sundayta Ltd
 43  
  *     International House, 
 44  
  *     174 Three Bridges Road, 
 45  
  *     Crawley, 
 46  
  *     West Sussex RH10 1LE, UK
 47  
  *
 48  
  */
 49  
 
 50  
 package org.melati.poem.dbms;
 51  
 
 52  
 import java.math.BigDecimal;
 53  
 import java.sql.Connection;
 54  
 import java.sql.DatabaseMetaData;
 55  
 import java.sql.Date;
 56  
 import java.sql.Driver;
 57  
 import java.sql.DriverManager;
 58  
 import java.sql.PreparedStatement;
 59  
 import java.sql.ResultSet;
 60  
 import java.sql.SQLException;
 61  
 import java.sql.Time;
 62  
 import java.sql.Timestamp;
 63  
 import java.sql.Types;
 64  
 import java.util.Enumeration;
 65  
 import java.util.Properties;
 66  
 
 67  
 import org.melati.poem.BigDecimalPoemType;
 68  
 import org.melati.poem.BinaryPoemType;
 69  
 import org.melati.poem.BooleanPoemType;
 70  
 import org.melati.poem.Column;
 71  
 import org.melati.poem.DatePoemType;
 72  
 import org.melati.poem.DoublePoemType;
 73  
 import org.melati.poem.ExecutingSQLPoemException;
 74  
 import org.melati.poem.IntegerPoemType;
 75  
 import org.melati.poem.IntegrityFixPoemType;
 76  
 import org.melati.poem.LongPoemType;
 77  
 import org.melati.poem.PasswordPoemType;
 78  
 import org.melati.poem.PoemBugPoemException;
 79  
 import org.melati.poem.PoemType;
 80  
 import org.melati.poem.SQLPoemException;
 81  
 import org.melati.poem.SQLPoemType;
 82  
 import org.melati.poem.SQLType;
 83  
 import org.melati.poem.StandardIntegrityFix;
 84  
 import org.melati.poem.StringPoemType;
 85  
 import org.melati.poem.Table;
 86  
 import org.melati.poem.TimePoemType;
 87  
 import org.melati.poem.TimestampPoemType;
 88  
 import org.melati.poem.UnexpectedExceptionPoemException;
 89  
 import org.melati.poem.util.StringUtils;
 90  
 
 91  
 import sun.reflect.ReflectionFactory.GetReflectionFactoryAction;
 92  
 
 93  
 /**
 94  
  * An SQL 92 compliant Database Management System. 
 95  
  * <p>
 96  
  * Should there ever be such a
 97  
  * thing then you wouldn't need to extend this, but all DBs used with Melati so
 98  
  * far have needed to extend the standard with their own variations.
 99  
  */
 100  16
 public class AnsiStandard implements Dbms {
 101  
   
 102  16
   private boolean driverLoaded = false;
 103  16
   private String driverClassName = null;
 104  16
   private Driver driver = null;
 105  
   protected String schema;
 106  
 
 107  
   protected synchronized void setDriverClassName(String name) {
 108  14
     driverClassName = name;
 109  14
   }
 110  
   
 111  
   protected synchronized String getDriverClassName() {
 112  1
     if (driverClassName == null)
 113  0
       throw new PoemBugPoemException(
 114  
           "No Driver Classname set in dbms specific class");
 115  
 
 116  1
     return driverClassName;
 117  
   }
 118  
 
 119  
   protected synchronized void setDriverLoaded(boolean loaded) {
 120  421
     driverLoaded = loaded;
 121  421
   }
 122  
 
 123  
   /**
 124  
    * @see org.melati.poem.dbms.Dbms#unloadDriver()
 125  
    */
 126  
   public void unloadDriver() {
 127  420
     driver = null;
 128  420
     setDriverLoaded(false);
 129  420
   }
 130  
   
 131  
   protected synchronized boolean getDriverLoaded() {
 132  397
     return driverLoaded;
 133  
   }
 134  
 
 135  
   /**
 136  
    * @see org.melati.poem.dbms.Dbms#getSchema()
 137  
    */
 138  
   public String getSchema() {
 139  2401
     return null;
 140  
   }
 141  
 
 142  
   /**
 143  
    * @see org.melati.poem.dbms.Dbms#shutdown(java.sql.Connection)
 144  
    */
 145  
   public void shutdown(Connection connection)  
 146  
     throws SQLException{    
 147  0
   }
 148  
 
 149  
   /**
 150  
    * {@inheritDoc}
 151  
    * @see org.melati.poem.dbms.Dbms#canDropColumns()
 152  
    */
 153  
   public boolean canDropColumns(){
 154  1522
     return true;
 155  
   }
 156  
 
 157  
   /** 
 158  
    * {@inheritDoc}
 159  
    * @see org.melati.poem.dbms.Dbms#canStoreBlobs()
 160  
    */
 161  
   public boolean canStoreBlobs(){
 162  1
     return true;
 163  
   }
 164  
 
 165  
   protected synchronized void loadDriver() {
 166  
     Class<?> driverClass;
 167  
     try {
 168  1
       driverClass = Class.forName(getDriverClassName());
 169  0
     } catch (ClassNotFoundException e) {
 170  0
       throw new UnexpectedExceptionPoemException(e);
 171  1
     }
 172  
     
 173  1
     setDriverLoaded(true);
 174  
 
 175  
     try {
 176  1
       driver = (Driver)driverClass.newInstance();
 177  0
     } catch (java.lang.Exception e) {
 178  
       // ... otherwise, "something went wrong" and I don't here care what
 179  
       // or have the wherewithal to do anything about it :)
 180  0
       throw new UnexpectedExceptionPoemException(e);
 181  1
     }
 182  1
   }
 183  
 
 184  
   /**
 185  
    * The default windows installation of MySQL has autocommit set true, 
 186  
    * which throws an SQLException when one issues a commit.
 187  
    * 
 188  
    * @see org.melati.poem.dbms.Dbms#getConnection(java.lang.String, java.lang.String, java.lang.String)
 189  
    */
 190  
   public Connection getConnection(String url, String user, String password)
 191  
       throws ConnectionFailurePoemException {
 192  397
     schema = user;
 193  
     try { 
 194  397
       synchronized (driverClassName) {
 195  397
         if (!getDriverLoaded()) loadDriver();
 196  397
       }
 197  
 
 198  397
       Connection c = null;
 199  397
       if (driver != null) {
 200  397
         Properties info = new Properties();
 201  397
         if (user != null)
 202  397
           info.put("user", user);
 203  397
         if (password != null)
 204  397
           info.put("password", password);
 205  
 
 206  397
         c = driver.connect(url, info);
 207  397
         if (c == null) 
 208  0
           throw new SQLException(
 209  
                     "Null connection from driver using url: " + 
 210  
                       url + 
 211  
                       " user: " + 
 212  
                       user + 
 213  
                       " password: " + password);
 214  397
       } else { 
 215  0
         c = DriverManager.getConnection(url, user, password);
 216  0
         if (c == null) 
 217  0
           throw new SQLException(
 218  
                     "Null connection from DriverManager using url: " + 
 219  
                     url + 
 220  
                     " user: " + 
 221  
                     user + 
 222  
                     " password: " + password);
 223  
       }
 224  397
       if (c.getAutoCommit())
 225  397
         c.setAutoCommit(false);
 226  
         //c.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); 
 227  397
       return c;
 228  0
     } catch (Exception e) { 
 229  0
       throw new ConnectionFailurePoemException(e);
 230  
     }
 231  
   }
 232  
   
 233  
   /**
 234  
    * {@inheritDoc}
 235  
    * @see org.melati.poem.dbms.Dbms#preparedStatementPlaceholder(org.melati.poem.PoemType)
 236  
    */
 237  
   public String preparedStatementPlaceholder(PoemType<?> type) {
 238  336
     return "?";
 239  
   }
 240  
   
 241  
   @Override
 242  
   public String createTableSql(Table<?> table) {
 243  294
     StringBuffer sqb = new StringBuffer();
 244  588
     sqb.append("CREATE " + createTableTypeQualifierSql(table) + 
 245  294
                "TABLE " + table.quotedName() + " (");
 246  294
     Enumeration<Column<?>> columns = table.columns();
 247  294
     int colCount = 0;
 248  2607
     while (columns.hasMoreElements()) { 
 249  2313
       Column<?> col = (Column<?>)columns.nextElement();
 250  2313
       if (colCount != 0)
 251  2019
         sqb.append(", ");
 252  2313
       colCount++;
 253  4626
       sqb.append(col.quotedName() + " " +
 254  2313
               col.getSQLType().sqlDefinition(this));
 255  
       
 256  2313
     }
 257  294
     sqb.append(")");
 258  294
     sqb.append(createTableOptionsSql());
 259  294
     return sqb.toString();
 260  
   }
 261  
   
 262  
   /** 
 263  
    * {@inheritDoc}
 264  
    * @see org.melati.poem.dbms.Dbms#createTableTypeQualifierSql(org.melati.poem.Table)
 265  
    */
 266  
   public String createTableTypeQualifierSql(Table<?> table) {
 267  0
     return "";
 268  
   }
 269  
 
 270  
   /**
 271  
    * {@inheritDoc}
 272  
    * @see org.melati.poem.dbms.Dbms#createTableOptionsSql()
 273  
    */
 274  
   public String createTableOptionsSql() {
 275  305
     return "";
 276  
   }
 277  
 
 278  
   /** 
 279  
    * {@inheritDoc}
 280  
    * @see org.melati.poem.dbms.Dbms#tableInitialisationSql(org.melati.poem.Table)
 281  
    */
 282  
   public String tableInitialisationSql(Table<?> table) {
 283  293
     return null;
 284  
   }
 285  
   
 286  
   /**
 287  
    * {@inheritDoc}
 288  
    * @see org.melati.poem.dbms.Dbms#getSqlDefinition(java.lang.String)
 289  
    */
 290  
   public String getSqlDefinition(String sqlTypeName) {
 291  1712
     return sqlTypeName;
 292  
   }
 293  
 
 294  
   /**
 295  
    * {@inheritDoc}
 296  
    * @see org.melati.poem.dbms.Dbms#getStringSqlDefinition(int)
 297  
    */
 298  
   public String getStringSqlDefinition(int size) throws SQLException {
 299  13
     if (size < 0)
 300  3
       throw new SQLException(
 301  
           "unlimited length not supported in AnsiStandard STRINGs");
 302  
 
 303  10
     return "VARCHAR(" + size + ")";
 304  
   }
 305  
 
 306  
   /**
 307  
    * {@inheritDoc}
 308  
    * @see org.melati.poem.dbms.Dbms#getLongSqlDefinition()
 309  
    */
 310  
   public String getLongSqlDefinition() {
 311  7
     return "INT8";
 312  
   }
 313  
 
 314  
   /**
 315  
    * {@inheritDoc}
 316  
    * @see org.melati.poem.dbms.Dbms#getBinarySqlDefinition(int)
 317  
    */
 318  
   public String getBinarySqlDefinition(int size) throws SQLException {
 319  9
     if (size < 0)
 320  5
       throw new SQLException(
 321  
           "unlimited length not supported in AnsiStandard BINARYs");
 322  
 
 323  4
     return "LONGVARBINARY(" + size + ")";
 324  
   }
 325  
 
 326  
   /**
 327  
    * {@inheritDoc}
 328  
    * @see org.melati.poem.dbms.Dbms#getFixedPtSqlDefinition(int, int)
 329  
    */
 330  
   public String getFixedPtSqlDefinition(int scale, int precision)
 331  
       throws SQLException {
 332  46
     if (scale < 0)
 333  13
       throw new SQLException(
 334  
           "negative scale not supported " + 
 335  
           "in AnsiStandard DECIMALs");
 336  33
     if (precision <= 0)
 337  11
       throw new SQLException(
 338  
           "nonpositive precision not supported " + 
 339  
           "in AnsiStandard DECIMALs");
 340  
 
 341  22
     return "DECIMAL(" + precision + "," + scale + ")";
 342  
   }
 343  
 
 344  
   /**
 345  
    * @see org.melati.poem.dbms.Dbms#sqlBooleanValueOfRaw(java.lang.Object)
 346  
    */
 347  
   public String sqlBooleanValueOfRaw(Object raw) {
 348  28
     return raw.toString();
 349  
   }
 350  
 
 351  
   /**
 352  
    * @see org.melati.poem.dbms.Dbms#canRepresent(org.melati.poem.PoemType, org.melati.poem.PoemType)
 353  
    */
 354  
   public <S,O>PoemType<O> canRepresent(PoemType<S> storage, PoemType<O> type) {
 355  3078
     return storage.canRepresent(type);
 356  
   }
 357  
 
 358  
   private SQLPoemType<?> unsupported(String sqlTypeName, ResultSet md)
 359  
       throws UnsupportedTypePoemException {
 360  
     UnsupportedTypePoemException e;
 361  
     try {
 362  0
       e = new UnsupportedTypePoemException(md.getString("TABLE_NAME"), 
 363  0
           md.getString("COLUMN_NAME"), 
 364  0
           md.getShort("DATA_TYPE"), 
 365  0
           sqlTypeName, md.getString("TYPE_NAME"));
 366  0
     } catch (SQLException ee) {
 367  0
       throw new UnsupportedTypePoemException(sqlTypeName);
 368  0
     }
 369  
 
 370  0
     throw e;
 371  
   }
 372  
 
 373  
   /**
 374  
    * @see org.melati.poem.dbms.Dbms#defaultPoemTypeOfColumnMetaData(java.sql.ResultSet)
 375  
    */
 376  
   public SQLPoemType<?> defaultPoemTypeOfColumnMetaData(ResultSet columnsMetaData)
 377  
       throws SQLException {
 378  4091
     int typeCode = columnsMetaData.getShort("DATA_TYPE");
 379  4091
     boolean nullable = columnsMetaData.getInt("NULLABLE") == DatabaseMetaData.columnNullable;
 380  4091
     int width = columnsMetaData.getInt("COLUMN_SIZE");
 381  4091
     int scale = columnsMetaData.getInt("DECIMAL_DIGITS");
 382  
 
 383  
     //System.err.println("defaultPoemTypeOfColumnMetaData:" + typeCode);
 384  4091
     switch (typeCode) {
 385  
       case Types.BIT :
 386  0
         return new BooleanPoemType(nullable);
 387  
       case Types.TINYINT :
 388  0
         return unsupported("TINYINT", columnsMetaData);
 389  
       case Types.SMALLINT :
 390  0
         return unsupported("SMALLINT", columnsMetaData);
 391  
       case Types.INTEGER :
 392  1817
         return new IntegerPoemType(nullable);
 393  
       case Types.BIGINT :
 394  120
         return new LongPoemType(nullable);
 395  
 
 396  
       case Types.FLOAT :
 397  0
         return unsupported("FLOAT", columnsMetaData);
 398  
       case Types.REAL :
 399  0
         return new DoublePoemType(nullable);
 400  
       case Types.DOUBLE :
 401  120
         return new DoublePoemType(nullable);
 402  
 
 403  
       case Types.NUMERIC :
 404  0
         return new BigDecimalPoemType(nullable, width, scale);
 405  
       case Types.DECIMAL :
 406  120
         return new BigDecimalPoemType(nullable, width, scale);
 407  
 
 408  
       case Types.CHAR :
 409  0
         return unsupported("CHAR", columnsMetaData);
 410  
       case Types.VARCHAR :
 411  600
         return new StringPoemType(nullable, width == 0 ? -1 : width);
 412  
       case Types.LONGVARCHAR :
 413  415
         return new StringPoemType(nullable, width == 0 ? -1 : width);
 414  
 
 415  
       case Types.DATE :
 416  120
         return new DatePoemType(nullable);
 417  
       case Types.TIME :
 418  0
         return new TimePoemType(nullable);
 419  
       case Types.TIMESTAMP :
 420  120
         return new TimestampPoemType(nullable);
 421  
 
 422  
       case Types.BINARY :
 423  0
         return unsupported("BINARY", columnsMetaData);
 424  
       case Types.VARBINARY :
 425  0
         return new BinaryPoemType(nullable, width);
 426  
       case Types.LONGVARBINARY :
 427  140
         return new BinaryPoemType(nullable, width);
 428  
 
 429  
       case Types.NULL :
 430  0
         return unsupported("NULL", columnsMetaData);
 431  
 
 432  
       case Types.OTHER :
 433  0
         return unsupported("OTHER", columnsMetaData);
 434  
 
 435  
 
 436  
         // Following introduced since 1.1
 437  
       case Types.JAVA_OBJECT : 
 438  0
         return unsupported("JAVA_OBJECT", columnsMetaData);
 439  
       case Types.DISTINCT : 
 440  0
         return unsupported("DISTINCT", columnsMetaData);
 441  
       case Types.STRUCT : 
 442  0
         return unsupported("STRUCT", columnsMetaData);
 443  
       case Types.ARRAY : 
 444  0
         return unsupported("ARRAY", columnsMetaData);
 445  
       case Types.BLOB : 
 446  0
         return unsupported("BLOB", columnsMetaData);
 447  
       case Types.CLOB : 
 448  0
         return unsupported("CLOB", columnsMetaData);
 449  
       case Types.REF : 
 450  0
         return unsupported("REF", columnsMetaData);
 451  
       case Types.DATALINK : 
 452  0
         return unsupported("DATLINK", columnsMetaData);
 453  
 
 454  
       case Types.BOOLEAN : 
 455  519
         return new BooleanPoemType(nullable);
 456  
       default :
 457  0
         return unsupported("<code not in Types.java!>", columnsMetaData);
 458  
     }
 459  
   }
 460  
 
 461  
   /**
 462  
    * {@inheritDoc}
 463  
    * @see org.melati.poem.dbms.Dbms#exceptionForUpdate
 464  
    */
 465  
   public SQLPoemException exceptionForUpdate(Table<?> table, String sql,
 466  
       boolean insert, SQLException e) {
 467  2
     return new ExecutingSQLPoemException(sql, e);
 468  
   }
 469  
 
 470  
   /**
 471  
    * {@inheritDoc}
 472  
    * @see Dbms#exceptionForUpdate(org.melati.poem.Table, 
 473  
    *                              java.sql.PreparedStatement, 
 474  
    *                              boolean, java.sql.SQLException)
 475  
    */
 476  
   public SQLPoemException exceptionForUpdate(Table<?> table, PreparedStatement ps,
 477  
       boolean insert, SQLException e) {
 478  0
     return exceptionForUpdate(table, ps == null ? null : ps.toString(), insert,
 479  
         e);
 480  
   }
 481  
 
 482  
   /**
 483  
    * {@inheritDoc}
 484  
    * @see org.melati.poem.dbms.Dbms#getQuotedName(java.lang.String)
 485  
    */
 486  
   public String getQuotedName(String name) {
 487  8083
     StringBuffer b = new StringBuffer();
 488  8083
     StringUtils.appendQuoted(b, unreservedName(name), '"');
 489  8083
     return b.toString();
 490  
   }
 491  
   
 492  
   public String getQuotedValue(SQLType<?> sqlType, String value) {
 493  17
     if (sqlType instanceof BooleanPoemType) {
 494  2
       return value;
 495  
     }
 496  15
     if (sqlType instanceof DoublePoemType) {
 497  1
       return value;
 498  
     }
 499  14
     if (sqlType instanceof LongPoemType) {
 500  1
       return value;
 501  
     }
 502  13
     if (sqlType instanceof BinaryPoemType) {
 503  1
       return StringUtils.quoted(value,'\'');
 504  
     }
 505  12
     if (sqlType instanceof BigDecimalPoemType) {
 506  1
       return value;
 507  
     }
 508  11
     if (sqlType instanceof DatePoemType) {
 509  1
       return StringUtils.quoted(value,'\'');
 510  
     }
 511  10
     if (sqlType instanceof TimestampPoemType) {
 512  1
       return StringUtils.quoted(value,'\'');
 513  
     }
 514  9
     if (sqlType instanceof TimePoemType) {
 515  0
       return StringUtils.quoted(value,'\'');
 516  
     }
 517  9
     if (sqlType instanceof PasswordPoemType) {
 518  1
       return StringUtils.quoted(value,'\'');
 519  
     }
 520  8
     if (sqlType instanceof StringPoemType) {
 521  3
       return StringUtils.quoted(value,'\'');
 522  
     }
 523  5
     if (sqlType instanceof IntegrityFixPoemType) {
 524  1
       return value;
 525  
     }
 526  4
     if (sqlType instanceof IntegerPoemType) {
 527  4
       return value;
 528  
     }
 529  0
     throw new PoemBugPoemException("Unrecognised sqlType: " + sqlType);
 530  
     
 531  
   }
 532  
 
 533  
   /**
 534  
    * {@inheritDoc}
 535  
    * @see org.melati.poem.dbms.Dbms#getJdbcMetadataName(java.lang.String)
 536  
    */
 537  
   public String getJdbcMetadataName(String name) {
 538  10
     return name;
 539  
   }
 540  
 
 541  
   /**
 542  
    * A pair of functions for getting around keywords which make your 
 543  
    * JDBC driver barf, as 'group' does for MySQL.
 544  
    * 
 545  
    * {@inheritDoc}
 546  
    * @see org.melati.poem.dbms.Dbms#unreservedName(java.lang.String)
 547  
    * @see org.melati.poem.dbms.MySQL#unreservedName
 548  
    * @see org.melati.poem.dbms.MySQL#melatiName
 549  
    */
 550  
   public String unreservedName(String name) {
 551  157
     return name;
 552  
   }
 553  
   
 554  
   /**
 555  
    * {@inheritDoc}
 556  
    * @see org.melati.poem.dbms.Dbms#melatiName(java.lang.String)
 557  
    */
 558  
   public String melatiName(String name) {
 559  18
     return name;
 560  
   }
 561  
 
 562  
   /**
 563  
    * MySQL requires a length argument when creating an index on a BLOB or TEXT
 564  
    * column.
 565  
    * 
 566  
    * {@inheritDoc}
 567  
    * @see org.melati.poem.dbms.Dbms#getIndexLength(org.melati.poem.Column)
 568  
    * @see org.melati.poem.dbms.MySQL#getIndexLength
 569  
    */
 570  
   public String getIndexLength(Column<?> column) {
 571  546
     return "";
 572  
   }
 573  
 
 574  
   /**
 575  
    * MSSQL cannot index a TEXT column. But neither can it compare them so we
 576  
    * don't use it, we use VARCHAR(255).
 577  
    *  
 578  
    * {@inheritDoc}
 579  
    * @see org.melati.poem.dbms.Dbms#canBeIndexed(org.melati.poem.Column)
 580  
    */
 581  
   public boolean canBeIndexed(Column<?> column) {
 582  535
     return true;
 583  
   }
 584  
 
 585  
   /**
 586  
    * MySQL had no EXISTS keyword, from 4.1 onwards it does.
 587  
    * NOTE There is a bootstrap problem here, we need to use the 
 588  
    * unchecked troid, otherwise we get a stack overflow.
 589  
    * {@inheritDoc}
 590  
    * @see org.melati.poem.dbms.Dbms#givesCapabilitySQL
 591  
    * @see org.melati.poem.dbms.MySQL#givesCapabilitySQL
 592  
    */
 593  
   public String givesCapabilitySQL(Integer userTroid, String capabilityExpr) {
 594  23
     return "SELECT * FROM " + getQuotedName("groupMembership") + " WHERE "
 595  23
         + getQuotedName("user") + " = " + userTroid + " AND "
 596  23
         + "EXISTS ( " + "SELECT " + getQuotedName("groupCapability") + "."
 597  23
         + getQuotedName("group") + " FROM "
 598  23
         + getQuotedName("groupCapability") + " WHERE "
 599  23
         + getQuotedName("groupCapability") + "." + getQuotedName("group")
 600  23
         + " = " + getQuotedName("groupMembership") + "."
 601  23
         + getQuotedName("group") + " AND " + getQuotedName("capability")
 602  
         + " = " + capabilityExpr + ")";
 603  
   }
 604  
 
 605  
   /**
 606  
    * This is the Postgresql syntax.
 607  
    * {@inheritDoc}
 608  
    * @see org.melati.poem.dbms.Dbms#caseInsensitiveRegExpSQL(String, String)
 609  
    */
 610  
   public String caseInsensitiveRegExpSQL(String term1, String term2) {
 611  21
     if (StringUtils.isQuoted(term2)) {
 612  7
       term2 = term2.substring(1, term2.length() - 1);
 613  
     } 
 614  21
     term2 = StringUtils.quoted(StringUtils.quoted(term2, '%'), '\'');
 615  
     
 616  21
     return term1 + " ILIKE " + term2;
 617  
   }
 618  
 
 619  
   /**
 620  
    * {@inheritDoc}
 621  
    * @see java.lang.Object#toString()
 622  
    */
 623  
   public String toString() {
 624  16
     return this.getClass().getName();
 625  
   }
 626  
 
 627  
   /**
 628  
    * {@inheritDoc}
 629  
    * @see org.melati.poem.dbms.Dbms#getForeignKeyDefinition
 630  
    */
 631  
   public String getForeignKeyDefinition(String tableName, String fieldName, 
 632  
       String targetTableName, String targetTableFieldName, String fixName) {
 633  18
     StringBuffer sb = new StringBuffer();
 634  36
     sb.append(" ADD FOREIGN KEY (" + getQuotedName(fieldName) + ") REFERENCES " + 
 635  18
               getQuotedName(targetTableName) + 
 636  18
               "(" + getQuotedName(targetTableFieldName) + ")");
 637  18
     if (fixName.equals("prevent"))
 638  5
       sb.append(" ON DELETE RESTRICT");
 639  18
     if (fixName.equals("delete"))
 640  8
       sb.append(" ON DELETE CASCADE");      
 641  18
     if (fixName.equals("clear"))
 642  5
       sb.append(" ON DELETE SET NULL");      
 643  18
     return sb.toString();
 644  
   }
 645  
 
 646  
   /**
 647  
    * Return the PRIMARY KEY definition string for this dbms. 
 648  
    * 
 649  
    * @param fieldName the table Troid column, often id, unquoted
 650  
    * @return The definition string
 651  
    * @see org.melati.poem.dbms.AnsiStandard#getPrimaryKeyDefinition(java.lang.String)
 652  
    * {@inheritDoc}
 653  
    * @see org.melati.poem.dbms.Dbms#getPrimaryKeyDefinition(java.lang.String)
 654  
    */
 655  
   public String getPrimaryKeyDefinition(String fieldName) {
 656  164
     return " ADD PRIMARY KEY (" + getQuotedName(fieldName) + ")";
 657  
   }
 658  
   
 659  
   @Override
 660  
   public String alterColumnNotNullableSQL(String tableName, Column<?> column) {
 661  17
     return "ALTER TABLE " + getQuotedName(tableName) +
 662  17
     " ALTER COLUMN " + getQuotedName(column.getName()) +
 663  
     " SET NOT NULL";
 664  
   }
 665  
   
 666  
   /**
 667  
    * {@inheritDoc}
 668  
    * @see org.melati.poem.dbms.Dbms#selectLimit(java.lang.String, int)
 669  
    */
 670  
   public String selectLimit(String querySelection, int limit) {
 671  13
     return "SELECT " + querySelection + " LIMIT " + limit;
 672  
   }
 673  
   
 674  
   /**
 675  
    * @see org.melati.poem.dbms.Dbms#booleanTrueExpression(org.melati.poem.Column)
 676  
    */
 677  
   public String booleanTrueExpression(Column<Boolean> booleanColumn) {
 678  1
     return booleanColumn.fullQuotedName();
 679  
   }
 680  
 
 681  
   @Override
 682  
   public String getSqlDefaultValue(SQLType<?> sqlType) {
 683  139
     if (sqlType instanceof BooleanPoemType) {
 684  11
       return ("false");
 685  
     }
 686  128
     if (sqlType instanceof DoublePoemType) {
 687  9
       return ("0.0");
 688  
     }
 689  119
     if (sqlType instanceof LongPoemType) {
 690  9
       return ("0");
 691  
     }
 692  110
     if (sqlType instanceof BinaryPoemType) {
 693  3
       return "";
 694  
     }
 695  107
     if (sqlType instanceof BigDecimalPoemType) {
 696  17
       return new BigDecimal(0.0).toString();
 697  
     }
 698  90
     if (sqlType instanceof DatePoemType) {
 699  9
       return new Date(new java.util.Date().getTime()).toString();
 700  
     }
 701  81
     if (sqlType instanceof TimestampPoemType) {
 702  5
       return new Timestamp(System.currentTimeMillis()).toString();
 703  
     }
 704  76
     if (sqlType instanceof TimePoemType) {
 705  4
       return new Time(System.currentTimeMillis()).toString();
 706  
     }
 707  72
     if (sqlType instanceof PasswordPoemType) {
 708  5
       return "FIXME";
 709  
     }
 710  67
     if (sqlType instanceof StringPoemType) {
 711  26
       return "default";
 712  
     }
 713  
     //Set prevent as default fix
 714  41
     if (sqlType instanceof IntegrityFixPoemType) {
 715  5
       return StandardIntegrityFix.prevent.getIndex().toString();
 716  
     }
 717  
 
 718  
     // Defaults to User for ColumnPoemType
 719  
     // Primary for SearchabilityPoemType
 720  
     // This needs to be last, as types above extend IntegerPoemType
 721  36
     if (sqlType instanceof IntegerPoemType) {
 722  36
       return ("0");
 723  
     }
 724  0
     throw new PoemBugPoemException("Unrecognised sqlType: " + sqlType);
 725  
 
 726  
   }
 727  
 
 728  
   /** TODO test on something which actually uses this */
 729  
   @Override
 730  
   public String alterColumnAddCommentSQL(Column<?> column, String comment) {
 731  
     // FIREBIRD, ORACLE, postgresql
 732  0
     return "COMMENT ON COLUMN " 
 733  0
         + getQuotedName(unreservedName(column.getTable().getName())) 
 734  
         +"." 
 735  0
         + getQuotedName(unreservedName(column.getName()))
 736  
         + " IS '" 
 737  
         + comment 
 738  
         + "'";
 739  
   }
 740  
 
 741  
   /** TODO test on something which actually uses this */
 742  
   @Override
 743  
   public String alterTableAddCommentSQL(Table<?> table, String comment) {
 744  0
     return "COMMENT ON TABLE " 
 745  0
         + getQuotedName(table.getName()) 
 746  
         + " IS '" 
 747  
         + comment 
 748  
         + "'";
 749  
   }
 750  
 
 751  
 }
 752