Coverage Report - org.melati.poem.dbms.SQLServer
 
Classes in this File Line Coverage Branch Coverage Complexity
SQLServer
55%
52/93
38%
34/88
4.095
SQLServer$SQLServerBooleanPoemType
0%
0/9
0%
0/6
4.095
 
 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, West Sussex 
 46  
  *     RH10 1LE, UK
 47  
  *
 48  
  */
 49  
 package org.melati.poem.dbms;
 50  
 
 51  
 import java.sql.DatabaseMetaData;
 52  
 import java.sql.PreparedStatement;
 53  
 import java.sql.ResultSet;
 54  
 import java.sql.SQLException;
 55  
 
 56  
 import org.melati.poem.BinaryPoemType;
 57  
 import org.melati.poem.BooleanPoemType;
 58  
 import org.melati.poem.Column;
 59  
 import org.melati.poem.DatePoemType;
 60  
 import org.melati.poem.DoublePoemType;
 61  
 import org.melati.poem.PoemType;
 62  
 import org.melati.poem.SQLPoemType;
 63  
 import org.melati.poem.SQLType;
 64  
 import org.melati.poem.StringPoemType;
 65  
 import org.melati.poem.TimestampPoemType;
 66  
 import org.melati.poem.util.StringUtils;
 67  
 
 68  
 /**
 69  
  * A Driver for the Microsoft SQL server.
 70  
  */
 71  
 public class SQLServer extends AnsiStandard {
 72  
 
 73  
   /**
 74  
    * SQL Server does not have a pleasant <code>TEXT</code> datatype, so we use
 75  
    * an arbetary value in a <code>VARCHAR</code>.
 76  
    */
 77  
   public static final int sqlServerTextHack = 2333;
 78  
 
 79  
   /**
 80  
    * Maximum size of a binary field.
 81  
    */
 82  
   public static final int sqlServerMaxBinarySize = 2147483647;
 83  
 
 84  
   /**
 85  
    * Constructor.
 86  
    */
 87  1
   public SQLServer() {
 88  
     // buggy
 89  
     // setDriverClassName("com.merant.datadirect.jdbc.sqlserver.SQLServerDriver");
 90  
     // setDriverClassName("sun.jdbc.odbc.JdbcOdbcDriver"); //does not work
 91  
     // setDriverClassName("com.ashna.jturbo.driver.Driver"); //works
 92  
     // setDriverClassName("com.jnetdirect.jsql.JSQLDriver"); //works
 93  
 
 94  
     // 2003
 95  
     // does not return indices without schema name ?
 96  
     // setDriverClassName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
 97  
     // FreeTDS driver has many unimplemented features and => does not work.
 98  
 
 99  
     // Works with 2003
 100  
     // setDriverClassName("com.inet.tds.TdsDriver");
 101  
 
 102  
     // Not working
 103  
     //setDriverClassName("easysoft.sql.jobDriver"); 
 104  
     // 2005
 105  1
     setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
 106  1
   }
 107  
 
 108  
   /**
 109  
    * Get the user we are connected as and return that as the schema.
 110  
    *
 111  
    * @see org.melati.poem.dbms.Dbms#getSchema()
 112  
    * @see org.melati.poem.dbms.AnsiStandard#getSchema()
 113  
    */
 114  
   public String getSchema() {
 115  1
     return null;
 116  
   }
 117  
 
 118  
   // Commented out as PMD objects to over riding method which only call
 119  
   // super.
 120  
   // public String getQuotedName(String name) {
 121  
   // if you don't want to set 'use ANSI quoted identifiers' database property
 122  
   // to 'true' (on SQL Server)
 123  
 
 124  
   /*
 125  
    * if(name.equalsIgnoreCase("nullable")) return "\"" + name+"\"";
 126  
    * if(name.equalsIgnoreCase("unique")) return "\"" + name+"\"";
 127  
    * if(name.equalsIgnoreCase("user")) return "q" + name;
 128  
    * if(name.equalsIgnoreCase("group")) return "q" + name; return name;
 129  
    */
 130  
 
 131  
   // if you already set 'use ANSI quoted identifiers' property to 'true'
 132  
   // return super.getQuotedName(name);
 133  
   // }
 134  
   /**
 135  
    * {@inheritDoc}
 136  
    * 
 137  
    * @see org.melati.poem.dbms.AnsiStandard#getSqlDefinition(java.lang.String)
 138  
    */
 139  
   public String getSqlDefinition(String sqlTypeName) {
 140  6
     if (sqlTypeName.equals("BOOLEAN")) {
 141  1
       return ("BIT");
 142  
     }
 143  5
     if (sqlTypeName.equals("DATE")) {
 144  1
       return ("DATETIME");
 145  
     }
 146  4
     if (sqlTypeName.equals("TIMESTAMP")) {
 147  1
       return ("DATETIME");
 148  
     }
 149  3
     return super.getSqlDefinition(sqlTypeName);
 150  
   }
 151  
 
 152  
   /**
 153  
    * {@inheritDoc}
 154  
    * 
 155  
    * @see org.melati.poem.dbms.AnsiStandard#getStringSqlDefinition(int)
 156  
    */
 157  
   public String getStringSqlDefinition(int size) throws SQLException {
 158  2
     if (size < 0) { // Don't use TEXT as it doesn't support
 159  
       // indexing or comparison
 160  1
       return "VARCHAR(" + sqlServerTextHack + ")";
 161  
     }
 162  1
     return super.getStringSqlDefinition(size);
 163  
   }
 164  
 
 165  
   /**
 166  
    * Translates a MSSQL String into a Poem <code>StringPoemType</code>.
 167  
    */
 168  
   //public static class SQLServerStringPoemType extends StringPoemType {
 169  
 
 170  
     /**
 171  
      * Constructor.
 172  
      * 
 173  
      * @param nullable
 174  
      *          nullability
 175  
      * @param size
 176  
      *          length
 177  
      */
 178  
     //public SQLServerStringPoemType(boolean nullable, int size) {
 179  
     //  super(nullable, size);
 180  
     //}
 181  
 
 182  
     // MSSQL returns metadata info size 2147483647 for its TEXT type
 183  
     // We set size to sqlServerTextHack for our Text type
 184  
     //protected boolean _canRepresent(SQLPoemType other) {
 185  
     //  return (getSize() < 0 || getSize() == 2147483647
 186  
     //          || getSize() == sqlServerTextHack || getSize() >= ((StringPoemType)other)
 187  
     //          .getSize());
 188  
     //}
 189  
 
 190  
     /**
 191  
      * {@inheritDoc}
 192  
      * 
 193  
      * @see org.melati.poem.BasePoemType#canRepresent(PoemType)
 194  
      */
 195  
     /*
 196  
      * public PoemType canRepresent(PoemType other) { return other instanceof
 197  
      * StringPoemType && _canRepresent((StringPoemType) other) &&
 198  
      * !(!getNullable() && ((StringPoemType) other).getNullable()) ? other :
 199  
      * null; }
 200  
      */
 201  
   //}
 202  
 
 203  
   /**
 204  
    * Accomodate our String size hack. {@inheritDoc}
 205  
    * 
 206  
    * @see org.melati.poem.dbms.AnsiStandard#canRepresent
 207  
    */
 208  
   public <S,O>PoemType<O> canRepresent(PoemType<S> storage, PoemType<O> type) {
 209  5
     if (storage instanceof StringPoemType && type instanceof StringPoemType) {
 210  1
       if (((StringPoemType)storage).getSize() == sqlServerTextHack
 211  0
               && ((StringPoemType)type).getSize() == -1
 212  0
               && !(!storage.getNullable() && type.getNullable())) {
 213  0
         return type;
 214  
       } else {
 215  1
         return storage.canRepresent(type);
 216  
       }
 217  4
     } else if (storage instanceof BinaryPoemType
 218  
             && type instanceof BinaryPoemType) {
 219  0
       if (((BinaryPoemType)storage).getSize() == sqlServerMaxBinarySize
 220  0
               && ((BinaryPoemType)type).getSize() == -1
 221  0
               && !(!storage.getNullable() && type.getNullable())) {
 222  0
         return type;
 223  
       } else {
 224  0
         return storage.canRepresent(type);
 225  
       }
 226  4
     } else if (storage instanceof DatePoemType
 227  
             && type instanceof TimestampPoemType) {
 228  0
       if (!(!storage.getNullable() && type.getNullable())) {
 229  0
         return type;
 230  
       } else {
 231  0
         return storage.canRepresent(type);
 232  
       }
 233  
     } else {
 234  4
       return super.canRepresent(storage, type);
 235  
     }
 236  
   }
 237  
 
 238  
   /**
 239  
    * Translates a MSSQL Date into a Poem <code>DatePoemType</code>.
 240  
    */
 241  
  // public static class SQLServerDatePoemType extends DatePoemType {
 242  
 
 243  
     /**
 244  
      * Constructor.
 245  
      * 
 246  
      * @param nullable
 247  
      *          nullability
 248  
      */
 249  
    // public SQLServerDatePoemType(boolean nullable) {
 250  
    //   super(Types.DATE, "DATETIME", nullable);
 251  
    // }
 252  
 
 253  
    // protected boolean _canRepresent(SQLPoemType other) {
 254  
    //   return other instanceof DatePoemType
 255  
    //           || other instanceof TimestampPoemType;
 256  
    // }
 257  
 
 258  
  // }
 259  
 
 260  
   /**
 261  
    * Translates a MSSQL Date into a Poem <code>TimestampPoemType</code>.
 262  
    */
 263  
   //public static class SQLServerTimestampPoemType extends TimestampPoemType {
 264  
 
 265  
     /**
 266  
      * Constructor.
 267  
      * 
 268  
      * @param nullable
 269  
      *          nullability
 270  
      */
 271  
     //public SQLServerTimestampPoemType(boolean nullable) {
 272  
     //  super(Types.TIMESTAMP, "DATETIME", nullable);
 273  
     //}
 274  
   //}
 275  
 
 276  
   /**
 277  
    * {@inheritDoc}
 278  
    * 
 279  
    * @see org.melati.poem.dbms.AnsiStandard#sqlBooleanValueOfRaw(java.lang.Object)
 280  
    */
 281  
   public String sqlBooleanValueOfRaw(Object raw) {
 282  2
     if (((Boolean)raw).booleanValue())
 283  1
       return "1";
 284  
     else
 285  1
       return "0";
 286  
   }
 287  
 
 288  
   /**
 289  
    * Translates an SQLServer Boolean into a Poem <code>BooleanPoemType</code>.
 290  
    */
 291  0
   public static class SQLServerBooleanPoemType extends BooleanPoemType {
 292  
 
 293  
     /**
 294  
      * Constructor.
 295  
      * 
 296  
      * @param nullable
 297  
      *          nullability
 298  
      */
 299  
     public SQLServerBooleanPoemType(boolean nullable) {
 300  0
       super(nullable);
 301  0
     }
 302  
 
 303  
     protected Boolean _getRaw(ResultSet rs, int col) throws SQLException {
 304  0
       synchronized (rs) {
 305  0
         boolean v = rs.getBoolean(col);
 306  0
         return rs.wasNull() ? null : (v ? Boolean.TRUE : Boolean.FALSE);
 307  0
       }
 308  
     }
 309  
 
 310  
     protected void _setRaw(PreparedStatement ps, int col, Object bool)
 311  
             throws SQLException {
 312  0
       ps.setInt(col, ((Boolean)bool).booleanValue() ? 1 : 0);
 313  0
     }
 314  
 
 315  
   }
 316  
 
 317  
   /**
 318  
    * {@inheritDoc}
 319  
    * 
 320  
    * @see org.melati.poem.dbms.Dbms#getLongSqlDefinition()
 321  
    */
 322  
   public String getLongSqlDefinition() {
 323  1
     return "BIGINT";
 324  
   }
 325  
 
 326  
   /**
 327  
    * {@inheritDoc}
 328  
    * 
 329  
    * @see org.melati.poem.dbms.Dbms#getBinarySqlDefinition(int)
 330  
    */
 331  
   public String getBinarySqlDefinition(int size) throws SQLException {
 332  2
     if (size < 0)
 333  1
       return "VARBINARY(MAX)";
 334  
 
 335  1
     return "VARBINARY(" + size + ")";
 336  
   }
 337  
 
 338  
   /**
 339  
    * {@inheritDoc}
 340  
    * 
 341  
    * @see org.melati.poem.dbms.AnsiStandard#defaultPoemTypeOfColumnMetaData(
 342  
    *      java.sql.ResultSet)
 343  
    */
 344  
   public SQLPoemType<?> defaultPoemTypeOfColumnMetaData(ResultSet md)
 345  
           throws SQLException {
 346  
 
 347  
     /*
 348  
     ResultSetMetaData rsmd = md.getMetaData();
 349  
     int cols = rsmd.getColumnCount();
 350  
     for (int i = 1; i <= cols; i++) {
 351  
       String table = rsmd.getTableName(i);
 352  
       System.err.println("table name: " + table);
 353  
       String column = rsmd.getColumnName(i);
 354  
       System.err.println("column name: " + column);
 355  
       int type = rsmd.getColumnType(i);
 356  
       System.err.println("type: " + type);
 357  
       String typeName = rsmd.getColumnTypeName(i);
 358  
       System.err.println("type Name: " + typeName);
 359  
       String className = rsmd.getColumnClassName(i);
 360  
       System.err.println("class Name: " + className);
 361  
       System.err.println("String val: " + md.getString(i));
 362  
       System.err.println("");
 363  
     }
 364  
     */
 365  
     // Not used in Poem 
 366  
     //if (md.getString("TYPE_NAME").equals("text"))
 367  
     //  return new SQLServerStringPoemType(
 368  
     //          md.getInt("NULLABLE") == DatabaseMetaData.columnNullable, md
 369  
     //                  .getInt("COLUMN_SIZE"));
 370  
     // We use a magic number for text fields
 371  0
     if (md.getString("TYPE_NAME").equals("varchar")
 372  0
             && md.getInt("COLUMN_SIZE") == sqlServerTextHack)
 373  0
       return new StringPoemType(
 374  0
               md.getInt("NULLABLE") == DatabaseMetaData.columnNullable, -1);
 375  
 
 376  
     // Not used in Poem 
 377  0
     if (md.getString("TYPE_NAME").equals("char"))
 378  0
       return new StringPoemType(
 379  0
               md.getInt("NULLABLE") == DatabaseMetaData.columnNullable, md
 380  0
                       .getInt("COLUMN_SIZE"));
 381  0
     if (md.getString("TYPE_NAME").equals("float"))
 382  0
       return new DoublePoemType(
 383  0
               md.getInt("NULLABLE") == DatabaseMetaData.columnNullable);
 384  0
     if (md.getString("TYPE_NAME").equals("datetime"))
 385  0
       return new DatePoemType(
 386  0
               md.getInt("NULLABLE") == DatabaseMetaData.columnNullable);
 387  0
     if (md.getString("TYPE_NAME").equals("bit"))
 388  0
       return new SQLServerBooleanPoemType(
 389  0
               md.getInt("NULLABLE") == DatabaseMetaData.columnNullable);
 390  
     /*
 391  
      * // MSSQL returns type -2 (BINARY) not 93 (TIMESTAMP) 
 392  
      * // They don't mean what we mean by timestamp
 393  
      * // They mean a one-per-record record creation timestamp  
 394  
      * if( md.getString("TYPE_NAME").equals("timestamp"))
 395  
      *   return new TimestampPoemType(md.getInt("NULLABLE")== DatabaseMetaData.columnNullable);
 396  
      */
 397  0
     return super.defaultPoemTypeOfColumnMetaData(md);
 398  
   }
 399  
 
 400  
   /**
 401  
    * Ignore <TT>dtproperties</TT> as it is a 'System' table used to store
 402  
    * Entity Relationship diagrams which have a jdbc type of TABLE when it should
 403  
    * probably have a jdbc type of 'SYSTEM TABLE'. 
 404  
    * 
 405  
    * {@inheritDoc}
 406  
    * @see org.melati.poem.dbms.AnsiStandard#melatiName(java.lang.String)
 407  
    */
 408  
   public String melatiName(String name) {
 409  3
     if (name == null)
 410  1
       return null;
 411  2
     if (name.equalsIgnoreCase("dtproperties"))
 412  1
       return null;
 413  1
     return name;
 414  
   }
 415  
 
 416  
   /**
 417  
    * MSSQL cannot index TEXT fields. Probably means that if you are serious
 418  
    * about using MSSQL you should use a varchar.
 419  
    * 
 420  
    * If a field is defined as Text in the DSD we use a VARCHAR. Not sure what
 421  
    * happens if a legacy db really uses TEXT.
 422  
    * 
 423  
    * @return whether it is allowed.
 424  
    */
 425  
   public boolean canBeIndexed(Column<?> column) {
 426  2
     PoemType<?> t = column.getType();
 427  2
     if (t instanceof StringPoemType && ((StringPoemType)t).getSize() < 0)
 428  1
       return false;
 429  1
     return true;
 430  
   }
 431  
 
 432  
   /**
 433  
    * {@inheritDoc}
 434  
    * 
 435  
    * @see org.melati.poem.dbms.AnsiStandard#caseInsensitiveRegExpSQL
 436  
    */
 437  
   public String caseInsensitiveRegExpSQL(String term1, String term2) {
 438  3
     if (StringUtils.isQuoted(term2)) {
 439  1
       term2 = term2.substring(1, term2.length() - 1);
 440  
     }
 441  3
     term2 = StringUtils.quoted(StringUtils.quoted(term2, '%'), '\'');
 442  
 
 443  3
     return term1 + " LIKE " + term2;
 444  
   }
 445  
 
 446  
   /**
 447  
    * Slightly different - embrace and extend.
 448  
    */
 449  
   public String getForeignKeyDefinition(String tableName, String fieldName,
 450  
           String targetTableName, String targetTableFieldName, String fixName) {
 451  3
     StringBuffer sb = new StringBuffer();
 452  6
     sb.append(" ADD FOREIGN KEY (" + getQuotedName(fieldName) + ") REFERENCES "
 453  3
             + getQuotedName(targetTableName) + "("
 454  3
             + getQuotedName(targetTableFieldName) + ")");
 455  3
     if (fixName.equals("prevent"))
 456  1
       sb.append(" ON DELETE NO ACTION");
 457  3
     if (fixName.equals("delete"))
 458  1
       sb.append(" ON DELETE CASCADE");
 459  3
     if (fixName.equals("clear"))
 460  1
       sb.append(" ON DELETE SET NULL");
 461  3
     return sb.toString();
 462  
   }
 463  
 
 464  
   /**
 465  
    * Accommodate SQLServer syntax.
 466  
    */
 467  
   public String alterColumnNotNullableSQL(String tableName, Column<?> column) {
 468  0
     return "ALTER TABLE " + getQuotedName(tableName) + " ALTER COLUMN "
 469  0
             + getQuotedName(column.getName()) + " "
 470  0
             + column.getSQLType().sqlDefinition(this);
 471  
   }
 472  
 
 473  
   /**
 474  
    * {@inheritDoc}
 475  
    * 
 476  
    * @see org.melati.poem.dbms.Dbms#selectLimit(java.lang.String, int)
 477  
    */
 478  
   public String selectLimit(String querySelection, int limit) {
 479  1
     return "SELECT TOP " + limit + querySelection;
 480  
   }
 481  
 
 482  
   
 483  
   /**
 484  
    * {@inheritDoc}
 485  
    * @see org.melati.poem.dbms.Dbms#booleanTrueExpression(org.melati.poem.Column)
 486  
    */
 487  
   public String booleanTrueExpression(Column<Boolean> booleanColumn) {
 488  0
     return booleanColumn.fullQuotedName() + "=1";
 489  
   }
 490  
 
 491  
   /**
 492  
    * {@inheritDoc}
 493  
    * @see org.melati.poem.dbms.AnsiStandard#getSqlDefaultValue(org.melati.poem.SQLType)
 494  
    */
 495  
   public String getSqlDefaultValue(SQLType<?> sqlType) {
 496  0
     if (sqlType instanceof BooleanPoemType) {
 497  0
       return ("0");
 498  
     }
 499  0
     if (sqlType instanceof BinaryPoemType) {
 500  0
       return "convert(varbinary, '')";
 501  
     }
 502  0
     return super.getSqlDefaultValue(sqlType);
 503  
   }
 504  
   
 505  
   public String getQuotedValue(SQLType<?> sqlType, String value) {
 506  0
     if (sqlType instanceof BinaryPoemType) {
 507  0
       return value;
 508  
     }
 509  0
     return super.getQuotedValue(sqlType, value);
 510  
   }  
 511  
 }