1 /* 2 * Hibernate, Relational Persistence for Idiomatic Java 3 * 4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as 5 * indicated by the @author tags or express copyright attribution 6 * statements applied by the authors. All third-party contributions are 7 * distributed under license by Red Hat Middleware LLC. 8 * 9 * This copyrighted material is made available to anyone wishing to use, modify, 10 * copy, or redistribute it subject to the terms and conditions of the GNU 11 * Lesser General Public License, as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 16 * for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public License 19 * along with this distribution; if not, write to: 20 * Free Software Foundation, Inc. 21 * 51 Franklin Street, Fifth Floor 22 * Boston, MA 02110-1301 USA 23 * 24 */ 25 package org.hibernate.dialect; 26 27 import java.sql.SQLException; 28 import java.sql.Types; 29 import java.io.Serializable; 30 31 import org.hibernate.Hibernate; 32 import org.hibernate.LockMode; 33 import org.hibernate.StaleObjectStateException; 34 import org.hibernate.JDBCException; 35 import org.hibernate.engine.SessionImplementor; 36 import org.hibernate.persister.entity.Lockable; 37 import org.hibernate.cfg.Environment; 38 import org.hibernate.dialect.function.NoArgSQLFunction; 39 import org.hibernate.dialect.function.StandardSQLFunction; 40 import org.hibernate.dialect.function.VarArgsSQLFunction; 41 import org.hibernate.dialect.lock.LockingStrategy; 42 import org.hibernate.dialect.lock.SelectLockingStrategy; 43 import org.hibernate.exception.JDBCExceptionHelper; 44 import org.hibernate.exception.TemplatedViolatedConstraintNameExtracter; 45 import org.hibernate.exception.ViolatedConstraintNameExtracter; 46 import org.hibernate.util.ReflectHelper; 47 import org.slf4j.Logger; 48 import org.slf4j.LoggerFactory; 49 50 /** 51 * An SQL dialect compatible with HSQLDB (HyperSQL). 52 * <p/> 53 * Note this version supports HSQLDB version 1.8 and higher, only. 54 * <p/> 55 * Enhancements to version 3.3.1 GA dialect to provide basic support for both HSQLDB 1.8.x and 2.0. 56 * For version 3.6.0 GA and later use the dialect supplied with Hibernate. 57 * 58 * @author Christoph Sturm 59 * @author Phillip Baird 60 * @author Fred Toussi 61 */ 62 public class HSQLDialect extends Dialect { 63 64 private static final Logger log = LoggerFactory.getLogger( HSQLDialect.class ); 65 66 /** 67 * version is 18 for 1.8 or 20 for 2.0 68 */ 69 private int hsqldbVersion = 18; 70 71 HSQLDialect()72 public HSQLDialect() { 73 super(); 74 75 try { 76 Class props = ReflectHelper.classForName( "org.hsqldb.persist.HsqlDatabaseProperties" ); 77 String versionString = (String) props.getDeclaredField( "THIS_VERSION" ).get( null ); 78 79 hsqldbVersion = Integer.parseInt( versionString.substring( 0, 1 ) ) * 10; 80 hsqldbVersion += Integer.parseInt( versionString.substring( 2, 3 ) ); 81 } 82 catch ( Throwable e ) { 83 // must be a very old version 84 } 85 86 registerColumnType( Types.BIGINT, "bigint" ); 87 registerColumnType( Types.BINARY, "binary($l)" ); 88 registerColumnType( Types.BIT, "bit" ); 89 registerColumnType( Types.BOOLEAN, "boolean" ); 90 registerColumnType( Types.CHAR, "char($l)" ); 91 registerColumnType( Types.DATE, "date" ); 92 93 registerColumnType( Types.DECIMAL, "decimal($p,$s)" ); 94 registerColumnType( Types.DOUBLE, "double" ); 95 registerColumnType( Types.FLOAT, "float" ); 96 registerColumnType( Types.INTEGER, "integer" ); 97 registerColumnType( Types.LONGVARBINARY, "longvarbinary" ); 98 registerColumnType( Types.LONGVARCHAR, "longvarchar" ); 99 registerColumnType( Types.SMALLINT, "smallint" ); 100 registerColumnType( Types.TINYINT, "tinyint" ); 101 registerColumnType( Types.TIME, "time" ); 102 registerColumnType( Types.TIMESTAMP, "timestamp" ); 103 registerColumnType( Types.VARCHAR, "varchar($l)" ); 104 registerColumnType( Types.VARBINARY, "varbinary($l)" ); 105 106 if ( hsqldbVersion < 20 ) { 107 registerColumnType( Types.NUMERIC, "numeric" ); 108 } 109 else { 110 registerColumnType( Types.NUMERIC, "numeric($p,$s)" ); 111 } 112 113 //HSQL has no Blob/Clob support .... but just put these here for now! 114 if ( hsqldbVersion < 20 ) { 115 registerColumnType( Types.BLOB, "longvarbinary" ); 116 registerColumnType( Types.CLOB, "longvarchar" ); 117 } 118 else { 119 registerColumnType( Types.BLOB, "blob" ); 120 registerColumnType( Types.CLOB, "clob" ); 121 } 122 123 registerFunction( "ascii", new StandardSQLFunction( "ascii", Hibernate.INTEGER ) ); 124 registerFunction( "char", new StandardSQLFunction( "char", Hibernate.CHARACTER ) ); 125 registerFunction( "length", new StandardSQLFunction( "length", Hibernate.LONG ) ); 126 registerFunction( "lower", new StandardSQLFunction( "lower" ) ); 127 registerFunction( "upper", new StandardSQLFunction( "upper" ) ); 128 registerFunction( "lcase", new StandardSQLFunction( "lcase" ) ); 129 registerFunction( "ucase", new StandardSQLFunction( "ucase" ) ); 130 registerFunction( "soundex", new StandardSQLFunction( "soundex", Hibernate.STRING ) ); 131 registerFunction( "ltrim", new StandardSQLFunction( "ltrim" ) ); 132 registerFunction( "rtrim", new StandardSQLFunction( "rtrim" ) ); 133 registerFunction( "reverse", new StandardSQLFunction( "reverse" ) ); 134 registerFunction( "space", new StandardSQLFunction( "space", Hibernate.STRING ) ); 135 registerFunction( "rawtohex", new StandardSQLFunction( "rawtohex" ) ); 136 registerFunction( "hextoraw", new StandardSQLFunction( "hextoraw" ) ); 137 138 registerFunction( "user", new NoArgSQLFunction( "user", Hibernate.STRING ) ); 139 registerFunction( "database", new NoArgSQLFunction( "database", Hibernate.STRING ) ); 140 141 registerFunction( "current_date", new NoArgSQLFunction( "current_date", Hibernate.DATE, false ) ); 142 registerFunction( "curdate", new NoArgSQLFunction( "curdate", Hibernate.DATE ) ); 143 registerFunction( "current_timestamp", new NoArgSQLFunction( "current_timestamp", Hibernate.TIMESTAMP, false ) ); 144 registerFunction( "now", new NoArgSQLFunction( "now", Hibernate.TIMESTAMP ) ); 145 registerFunction( "current_time", new NoArgSQLFunction( "current_time", Hibernate.TIME, false ) ); 146 registerFunction( "curtime", new NoArgSQLFunction( "curtime", Hibernate.TIME ) ); 147 registerFunction( "day", new StandardSQLFunction( "day", Hibernate.INTEGER ) ); 148 registerFunction( "dayofweek", new StandardSQLFunction( "dayofweek", Hibernate.INTEGER ) ); 149 registerFunction( "dayofyear", new StandardSQLFunction( "dayofyear", Hibernate.INTEGER ) ); 150 registerFunction( "dayofmonth", new StandardSQLFunction( "dayofmonth", Hibernate.INTEGER ) ); 151 registerFunction( "month", new StandardSQLFunction( "month", Hibernate.INTEGER ) ); 152 registerFunction( "year", new StandardSQLFunction( "year", Hibernate.INTEGER ) ); 153 registerFunction( "week", new StandardSQLFunction( "week", Hibernate.INTEGER ) ); 154 registerFunction( "quater", new StandardSQLFunction( "quater", Hibernate.INTEGER ) ); 155 registerFunction( "hour", new StandardSQLFunction( "hour", Hibernate.INTEGER ) ); 156 registerFunction( "minute", new StandardSQLFunction( "minute", Hibernate.INTEGER ) ); 157 registerFunction( "second", new StandardSQLFunction( "second", Hibernate.INTEGER ) ); 158 registerFunction( "dayname", new StandardSQLFunction( "dayname", Hibernate.STRING ) ); 159 registerFunction( "monthname", new StandardSQLFunction( "monthname", Hibernate.STRING ) ); 160 161 registerFunction( "abs", new StandardSQLFunction( "abs" ) ); 162 registerFunction( "sign", new StandardSQLFunction( "sign", Hibernate.INTEGER ) ); 163 164 registerFunction( "acos", new StandardSQLFunction( "acos", Hibernate.DOUBLE ) ); 165 registerFunction( "asin", new StandardSQLFunction( "asin", Hibernate.DOUBLE ) ); 166 registerFunction( "atan", new StandardSQLFunction( "atan", Hibernate.DOUBLE ) ); 167 registerFunction( "cos", new StandardSQLFunction( "cos", Hibernate.DOUBLE ) ); 168 registerFunction( "cot", new StandardSQLFunction( "cot", Hibernate.DOUBLE ) ); 169 registerFunction( "exp", new StandardSQLFunction( "exp", Hibernate.DOUBLE ) ); 170 registerFunction( "log", new StandardSQLFunction( "log", Hibernate.DOUBLE ) ); 171 registerFunction( "log10", new StandardSQLFunction( "log10", Hibernate.DOUBLE ) ); 172 registerFunction( "sin", new StandardSQLFunction( "sin", Hibernate.DOUBLE ) ); 173 registerFunction( "sqrt", new StandardSQLFunction( "sqrt", Hibernate.DOUBLE ) ); 174 registerFunction( "tan", new StandardSQLFunction( "tan", Hibernate.DOUBLE ) ); 175 registerFunction( "pi", new NoArgSQLFunction( "pi", Hibernate.DOUBLE ) ); 176 registerFunction( "rand", new StandardSQLFunction( "rand", Hibernate.FLOAT ) ); 177 178 registerFunction( "radians", new StandardSQLFunction( "radians", Hibernate.DOUBLE ) ); 179 registerFunction( "degrees", new StandardSQLFunction( "degrees", Hibernate.DOUBLE ) ); 180 registerFunction( "roundmagic", new StandardSQLFunction( "roundmagic" ) ); 181 182 registerFunction( "ceiling", new StandardSQLFunction( "ceiling" ) ); 183 registerFunction( "floor", new StandardSQLFunction( "floor" ) ); 184 185 // Multi-param dialect functions... 186 registerFunction( "mod", new StandardSQLFunction( "mod", Hibernate.INTEGER ) ); 187 188 // function templates 189 registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "(", "||", ")" ) ); 190 191 getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE ); 192 } 193 getAddColumnString()194 public String getAddColumnString() { 195 return "add column"; 196 } 197 supportsIdentityColumns()198 public boolean supportsIdentityColumns() { 199 return true; 200 } 201 getIdentityColumnString()202 public String getIdentityColumnString() { 203 return "generated by default as identity (start with 1)"; //not null is implicit 204 } 205 getIdentitySelectString()206 public String getIdentitySelectString() { 207 return "call identity()"; 208 } 209 getIdentityInsertString()210 public String getIdentityInsertString() { 211 return hsqldbVersion < 20 ? "null" : "default"; 212 } 213 supportsLockTimeouts()214 public boolean supportsLockTimeouts() { 215 return false; 216 } 217 getForUpdateString()218 public String getForUpdateString() { 219 return ""; 220 } 221 supportsUnique()222 public boolean supportsUnique() { 223 return false; 224 } 225 supportsLimit()226 public boolean supportsLimit() { 227 return true; 228 } 229 getLimitString(String sql, boolean hasOffset)230 public String getLimitString(String sql, boolean hasOffset) { 231 if ( hsqldbVersion < 20 ) { 232 return new StringBuffer( sql.length() + 10 ) 233 .append( sql ) 234 .insert( 235 sql.toLowerCase().indexOf( "select" ) + 6, 236 hasOffset ? " limit ? ?" : " top ?" 237 ) 238 .toString(); 239 } 240 else { 241 return new StringBuffer( sql.length() + 20 ) 242 .append( sql ) 243 .append( hasOffset ? " offset ? limit ?" : " limit ?" ) 244 .toString(); 245 } 246 } 247 bindLimitParametersFirst()248 public boolean bindLimitParametersFirst() { 249 return hsqldbVersion < 20; 250 } 251 supportsIfExistsAfterTableName()252 public boolean supportsIfExistsAfterTableName() { 253 return true; 254 } 255 supportsColumnCheck()256 public boolean supportsColumnCheck() { 257 return hsqldbVersion >= 20; 258 } 259 supportsSequences()260 public boolean supportsSequences() { 261 return true; 262 } 263 supportsPooledSequences()264 public boolean supportsPooledSequences() { 265 return true; 266 } 267 getCreateSequenceString(String sequenceName)268 protected String getCreateSequenceString(String sequenceName) { 269 return "create sequence " + sequenceName; 270 } 271 getDropSequenceString(String sequenceName)272 protected String getDropSequenceString(String sequenceName) { 273 return "drop sequence " + sequenceName; 274 } 275 getSelectSequenceNextValString(String sequenceName)276 public String getSelectSequenceNextValString(String sequenceName) { 277 return "next value for " + sequenceName; 278 } 279 getSequenceNextValString(String sequenceName)280 public String getSequenceNextValString(String sequenceName) { 281 return "call next value for " + sequenceName; 282 } 283 getQuerySequencesString()284 public String getQuerySequencesString() { 285 // this assumes schema support, which is present in 1.8.0 and later... 286 return "select sequence_name from information_schema.system_sequences"; 287 } 288 getViolatedConstraintNameExtracter()289 public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() { 290 return hsqldbVersion < 20 ? EXTRACTER_18 : EXTRACTER_20; 291 } 292 293 private static ViolatedConstraintNameExtracter EXTRACTER_18 = new TemplatedViolatedConstraintNameExtracter() { 294 295 /** 296 * Extract the name of the violated constraint from the given SQLException. 297 * 298 * @param sqle The exception that was the result of the constraint violation. 299 * @return The extracted constraint name. 300 */ 301 public String extractConstraintName(SQLException sqle) { 302 String constraintName = null; 303 304 int errorCode = JDBCExceptionHelper.extractErrorCode( sqle ); 305 306 if ( errorCode == -8 ) { 307 constraintName = extractUsingTemplate( 308 "Integrity constraint violation ", " table:", sqle.getMessage() 309 ); 310 } 311 else if ( errorCode == -9 ) { 312 constraintName = extractUsingTemplate( 313 "Violation of unique index: ", " in statement [", sqle.getMessage() 314 ); 315 } 316 else if ( errorCode == -104 ) { 317 constraintName = extractUsingTemplate( 318 "Unique constraint violation: ", " in statement [", sqle.getMessage() 319 ); 320 } 321 else if ( errorCode == -177 ) { 322 constraintName = extractUsingTemplate( 323 "Integrity constraint violation - no parent ", " table:", 324 sqle.getMessage() 325 ); 326 } 327 return constraintName; 328 } 329 330 }; 331 332 /** 333 * HSQLDB 2.0 messages have changed 334 * messages may be localized - therefore use the common, non-locale element " table: " 335 */ 336 private static ViolatedConstraintNameExtracter EXTRACTER_20 = new TemplatedViolatedConstraintNameExtracter() { 337 338 public String extractConstraintName(SQLException sqle) { 339 String constraintName = null; 340 341 int errorCode = JDBCExceptionHelper.extractErrorCode( sqle ); 342 343 if ( errorCode == -8 ) { 344 constraintName = extractUsingTemplate( 345 "; ", " table: ", sqle.getMessage() 346 ); 347 } 348 else if ( errorCode == -9 ) { 349 constraintName = extractUsingTemplate( 350 "; ", " table: ", sqle.getMessage() 351 ); 352 } 353 else if ( errorCode == -104 ) { 354 constraintName = extractUsingTemplate( 355 "; ", " table: ", sqle.getMessage() 356 ); 357 } 358 else if ( errorCode == -177 ) { 359 constraintName = extractUsingTemplate( 360 "; ", " table: ", sqle.getMessage() 361 ); 362 } 363 return constraintName; 364 } 365 }; 366 getSelectClauseNullString(int sqlType)367 public String getSelectClauseNullString(int sqlType) { 368 String literal; 369 switch ( sqlType ) { 370 case Types.VARCHAR: 371 case Types.CHAR: 372 literal = "cast(null as varchar(100))"; 373 break; 374 case Types.DATE: 375 literal = "cast(null as date)"; 376 break; 377 case Types.TIMESTAMP: 378 literal = "cast(null as timestamp)"; 379 break; 380 case Types.TIME: 381 literal = "cast(null as time)"; 382 break; 383 default: 384 literal = "cast(null as int)"; 385 } 386 return literal; 387 } 388 supportsUnionAll()389 public boolean supportsUnionAll() { 390 return true; 391 } 392 393 // temporary table support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 394 // Hibernate uses this information for temporary tables that it uses for its own operations 395 // therefore the appropriate strategy is taken with different versions of HSQLDB 396 397 // All versions of HSQLDB support GLOBAL TEMPORARY tables where the table 398 // definition is shared by all users but data is private to the session 399 // HSQLDB 2.0 also supports session-based LOCAL TEMPORARY tables where 400 // the definition and data is private to the session and table declaration 401 // can happen in the middle of a transaction 402 403 /** 404 * Does this dialect support temporary tables? 405 * 406 * @return True if temp tables are supported; false otherwise. 407 */ supportsTemporaryTables()408 public boolean supportsTemporaryTables() { 409 return true; 410 } 411 412 /** 413 * With HSQLDB 2.0, the table name is qualified with MODULE to assist the drop 414 * statement (in-case there is a global name beginning with HT_) 415 * 416 * @param baseTableName The table name from which to base the temp table name. 417 * 418 * @return The generated temp table name. 419 */ generateTemporaryTableName(String baseTableName)420 public String generateTemporaryTableName(String baseTableName) { 421 if ( hsqldbVersion < 20 ) { 422 return "HT_" + baseTableName; 423 } 424 else { 425 return "MODULE.HT_" + baseTableName; 426 } 427 } 428 429 /** 430 * Command used to create a temporary table. 431 * 432 * @return The command used to create a temporary table. 433 */ getCreateTemporaryTableString()434 public String getCreateTemporaryTableString() { 435 if ( hsqldbVersion < 20 ) { 436 return "create global temporary table"; 437 } 438 else { 439 return "declare local temporary table"; 440 } 441 } 442 443 /** 444 * No fragment is needed if data is not needed beyond commit, otherwise 445 * should add "on commit preserve rows" 446 * 447 * @return Any required postfix. 448 */ getCreateTemporaryTablePostfix()449 public String getCreateTemporaryTablePostfix() { 450 return ""; 451 } 452 453 /** 454 * Command used to drop a temporary table. 455 * 456 * @return The command used to drop a temporary table. 457 */ getDropTemporaryTableString()458 public String getDropTemporaryTableString() { 459 return "drop table"; 460 } 461 462 /** 463 * Different behavior for GLOBAL TEMPORARY (1.8) and LOCAL TEMPORARY (2.0) 464 * <p/> 465 * Possible return values and their meanings:<ul> 466 * <li>{@link Boolean#TRUE} - Unequivocally, perform the temporary table DDL 467 * in isolation.</li> 468 * <li>{@link Boolean#FALSE} - Unequivocally, do <b>not</b> perform the 469 * temporary table DDL in isolation.</li> 470 * <li><i>null</i> - defer to the JDBC driver response in regards to 471 * {@link java.sql.DatabaseMetaData#dataDefinitionCausesTransactionCommit()}</li> 472 * </ul> 473 * 474 * @return see the result matrix above. 475 */ performTemporaryTableDDLInIsolation()476 public Boolean performTemporaryTableDDLInIsolation() { 477 if ( hsqldbVersion < 20 ) { 478 return Boolean.TRUE; 479 } 480 else { 481 return Boolean.FALSE; 482 } 483 } 484 485 /** 486 * Do we need to drop the temporary table after use? 487 * 488 * todo - clarify usage by Hibernate 489 * 490 * Version 1.8 GLOBAL TEMPORARY table definitions persist beyond the end 491 * of the session (by default, data is cleared at commit). If there are 492 * not too many such tables, perhaps we can avoid dropping them and reuse 493 * the table next time?<p> 494 * 495 * Version 2.x LOCAL TEMPORARY table definitions do not persist beyond 496 * the end of the session (by default, data is cleared at commit). 497 * 498 * @return True if the table should be dropped. 499 */ dropTemporaryTableAfterUse()500 public boolean dropTemporaryTableAfterUse() { 501 if ( hsqldbVersion < 20 ) { 502 return Boolean.TRUE; 503 } 504 else { 505 return Boolean.FALSE; 506 } 507 } 508 509 // current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 510 511 /** 512 * HSQLDB 1.8.x requires CALL CURRENT_TIMESTAMP but this should not 513 * be treated as a callable statement. It is equivalent to 514 * "select current_timestamp from dual" in some databases. 515 * HSQLDB 2.0 also supports VALUES CURRENT_TIMESTAMP 516 * 517 * @return True if the current timestamp can be retrieved; false otherwise. 518 */ supportsCurrentTimestampSelection()519 public boolean supportsCurrentTimestampSelection() { 520 return true; 521 } 522 523 /** 524 * Should the value returned by {@link #getCurrentTimestampSelectString} 525 * be treated as callable. Typically this indicates that JDBC escape 526 * syntax is being used...<p> 527 * 528 * CALL CURRENT_TIMESTAMP is used but this should not 529 * be treated as a callable statement. 530 * 531 * @return True if the {@link #getCurrentTimestampSelectString} return 532 * is callable; false otherwise. 533 */ isCurrentTimestampSelectStringCallable()534 public boolean isCurrentTimestampSelectStringCallable() { 535 return false; 536 } 537 538 /** 539 * Retrieve the command used to retrieve the current timestamp from the 540 * database. 541 * 542 * @return The command. 543 */ getCurrentTimestampSelectString()544 public String getCurrentTimestampSelectString() { 545 return "call current_timestamp"; 546 } 547 548 /** 549 * The name of the database-specific SQL function for retrieving the 550 * current timestamp. 551 * 552 * @return The function name. 553 */ getCurrentTimestampSQLFunctionName()554 public String getCurrentTimestampSQLFunctionName() { 555 // the standard SQL function name is current_timestamp... 556 return "current_timestamp"; 557 } 558 559 /** 560 * For HSQLDB 2.0, this is a copy of the base class implementation. 561 * For HSQLDB 1.8, only READ_UNCOMMITTED is supported. 562 * 563 * @param lockable The persister for the entity to be locked. 564 * @param lockMode The type of lock to be acquired. 565 * 566 * @return The appropriate locking strategy. 567 * 568 * @since 3.2 569 */ getLockingStrategy(Lockable lockable, LockMode lockMode)570 public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) { 571 if ( hsqldbVersion < 20 ) { 572 return new ReadUncommittedLockingStrategy( lockable, lockMode ); 573 } 574 else { 575 return new SelectLockingStrategy( lockable, lockMode ); 576 } 577 } 578 579 public static class ReadUncommittedLockingStrategy extends SelectLockingStrategy { ReadUncommittedLockingStrategy(Lockable lockable, LockMode lockMode)580 public ReadUncommittedLockingStrategy(Lockable lockable, LockMode lockMode) { 581 super( lockable, lockMode ); 582 } 583 lock(Serializable id, Object version, Object object, SessionImplementor session)584 public void lock(Serializable id, Object version, Object object, SessionImplementor session) 585 throws StaleObjectStateException, JDBCException { 586 if ( getLockMode().greaterThan( LockMode.READ ) ) { 587 log.warn( "HSQLDB supports only READ_UNCOMMITTED isolation" ); 588 } 589 super.lock( id, version, object, session ); 590 } 591 } 592 593 594 // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 595 supportsEmptyInList()596 public boolean supportsEmptyInList() { 597 return false; 598 } 599 600 /** 601 * todo - needs usage clarification 602 * 603 * If the SELECT statement is always part of a UNION, then the type of 604 * parameter is resolved by v. 2.0, but not v. 1.8 (assuming the other 605 * SELECT in the UNION has a column reference in the same position and 606 * can be type-resolved). 607 * 608 * On the other hand if the SELECT statement is isolated, all versions of 609 * HSQLDB require casting for "select ? from .." to work. 610 * 611 * @return True if select clause parameter must be cast()ed 612 * 613 * @since 3.2 614 */ requiresCastingOfParametersInSelectClause()615 public boolean requiresCastingOfParametersInSelectClause() { 616 return true; 617 } 618 619 /** 620 * For the underlying database, is READ_COMMITTED isolation implemented by 621 * forcing readers to wait for write locks to be released? 622 * 623 * @return True if writers block readers to achieve READ_COMMITTED; false otherwise. 624 */ doesReadCommittedCauseWritersToBlockReaders()625 public boolean doesReadCommittedCauseWritersToBlockReaders() { 626 return hsqldbVersion >= 20; 627 } 628 629 /** 630 * For the underlying database, is REPEATABLE_READ isolation implemented by 631 * forcing writers to wait for read locks to be released? 632 * 633 * @return True if readers block writers to achieve REPEATABLE_READ; false otherwise. 634 */ doesRepeatableReadCauseReadersToBlockWriters()635 public boolean doesRepeatableReadCauseReadersToBlockWriters() { 636 return hsqldbVersion >= 20; 637 } 638 639 supportsLobValueChangePropogation()640 public boolean supportsLobValueChangePropogation() { 641 return false; 642 } 643 toBooleanValueString(boolean bool)644 public String toBooleanValueString(boolean bool) { 645 return String.valueOf( bool ); 646 } 647 supportsTupleDistinctCounts()648 public boolean supportsTupleDistinctCounts() { 649 return false; 650 } 651 } 652