1 /* Copyright (c) 2001-2016, The HSQL Development Group 2 * All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * Redistributions of source code must retain the above copyright notice, this 8 * list of conditions and the following disclaimer. 9 * 10 * Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * Neither the name of the HSQL Development Group nor the names of its 15 * contributors may be used to endorse or promote products derived from this 16 * software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, 22 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 32 package org.hsqldb; 33 34 import java.io.InputStream; 35 import java.text.SimpleDateFormat; 36 import java.util.Calendar; 37 import java.util.GregorianCalendar; 38 import java.util.Locale; 39 import java.util.Random; 40 import java.util.TimeZone; 41 42 import org.hsqldb.HsqlNameManager.HsqlName; 43 import org.hsqldb.error.Error; 44 import org.hsqldb.error.ErrorCode; 45 import org.hsqldb.jdbc.JDBCConnection; 46 import org.hsqldb.jdbc.JDBCDriver; 47 import org.hsqldb.lib.ArrayUtil; 48 import org.hsqldb.lib.CountUpDownLatch; 49 import org.hsqldb.lib.HashMap; 50 import org.hsqldb.lib.HsqlArrayList; 51 import org.hsqldb.lib.HsqlDeque; 52 import org.hsqldb.lib.Iterator; 53 import org.hsqldb.lib.OrderedHashSet; 54 import org.hsqldb.lib.SimpleLog; 55 import org.hsqldb.lib.java.JavaSystem; 56 import org.hsqldb.map.ValuePool; 57 import org.hsqldb.navigator.RowSetNavigator; 58 import org.hsqldb.navigator.RowSetNavigatorClient; 59 import org.hsqldb.persist.HsqlDatabaseProperties; 60 import org.hsqldb.persist.HsqlProperties; 61 import org.hsqldb.persist.PersistentStore; 62 import org.hsqldb.result.Result; 63 import org.hsqldb.result.ResultConstants; 64 import org.hsqldb.result.ResultLob; 65 import org.hsqldb.result.ResultProperties; 66 import org.hsqldb.rights.Grantee; 67 import org.hsqldb.rights.User; 68 import org.hsqldb.types.BlobDataID; 69 import org.hsqldb.types.ClobDataID; 70 import org.hsqldb.types.TimeData; 71 import org.hsqldb.types.TimestampData; 72 import org.hsqldb.types.Type; 73 import org.hsqldb.types.Type.TypedComparator; 74 75 /** 76 * Implementation of SQL sessions. 77 * 78 * @author Fred Toussi (fredt@users dot sourceforge.net) 79 * @version 2.3.4 80 * @since 1.7.0 81 */ 82 public class Session implements SessionInterface { 83 84 // 85 private volatile boolean isClosed; 86 87 // 88 public Database database; 89 private final User sessionUser; 90 private User user; 91 private Grantee role; 92 93 // transaction support 94 public boolean isReadOnlyDefault; 95 int isolationLevelDefault = SessionInterface.TX_READ_COMMITTED; 96 int isolationLevel = SessionInterface.TX_READ_COMMITTED; 97 boolean isReadOnlyIsolation; 98 int actionIndex; 99 long actionStartTimestamp; 100 long actionTimestamp; 101 long transactionTimestamp; 102 long transactionEndTimestamp; 103 boolean txConflictRollback; 104 boolean isPreTransaction; 105 boolean isTransaction; 106 boolean isBatch; 107 volatile boolean abortAction; 108 volatile boolean abortTransaction; 109 volatile boolean redoAction; 110 HsqlArrayList rowActionList; 111 volatile boolean tempUnlocked; 112 public OrderedHashSet waitedSessions; 113 public OrderedHashSet waitingSessions; 114 OrderedHashSet tempSet; 115 OrderedHashSet actionSet; 116 public CountUpDownLatch latch = new CountUpDownLatch(); 117 TimeoutManager timeoutManager; 118 119 // current settings 120 final String zoneString; 121 final int sessionTimeZoneSeconds; 122 int timeZoneSeconds; 123 boolean isNetwork; 124 private int sessionMaxRows; 125 int sessionOptimization = 8; 126 private final long sessionId; 127 int sessionTxId = -1; 128 private boolean ignoreCase; 129 private long sessionStartTimestamp; 130 131 // internal connection 132 private JDBCConnection intConnection; 133 134 // external connection 135 private JDBCConnection extConnection; 136 137 // schema 138 public HsqlName currentSchema; 139 public HsqlName loggedSchema; 140 141 // query processing 142 ParserCommand parser; 143 boolean isProcessingScript; 144 boolean isProcessingLog; 145 public SessionContext sessionContext; 146 int resultMaxMemoryRows; 147 148 // 149 public SessionData sessionData; 150 151 // 152 public StatementManager statementManager; 153 154 // 155 public Object special; 156 157 /** 158 * Constructs a new Session object. 159 * 160 * @param db the database to which this represents a connection 161 * @param user the initial user 162 * @param autocommit the initial autocommit value 163 * @param readonly the initial readonly value 164 * @param id the session identifier, as known to the database 165 */ Session(Database db, User user, boolean autocommit, boolean readonly, long id, String zoneString, int timeZoneSeconds)166 Session(Database db, User user, boolean autocommit, boolean readonly, 167 long id, String zoneString, int timeZoneSeconds) { 168 169 sessionId = id; 170 database = db; 171 this.user = user; 172 this.sessionUser = user; 173 this.zoneString = zoneString; 174 this.sessionTimeZoneSeconds = timeZoneSeconds; 175 this.timeZoneSeconds = timeZoneSeconds; 176 rowActionList = new HsqlArrayList(32, true); 177 waitedSessions = new OrderedHashSet(); 178 waitingSessions = new OrderedHashSet(); 179 tempSet = new OrderedHashSet(); 180 actionSet = new OrderedHashSet(); 181 isolationLevelDefault = database.defaultIsolationLevel; 182 ignoreCase = database.sqlIgnoreCase; 183 isolationLevel = isolationLevelDefault; 184 txConflictRollback = database.txConflictRollback; 185 isReadOnlyDefault = readonly; 186 isReadOnlyIsolation = isolationLevel 187 == SessionInterface.TX_READ_UNCOMMITTED; 188 sessionContext = new SessionContext(this); 189 sessionContext.isAutoCommit = autocommit ? Boolean.TRUE 190 : Boolean.FALSE; 191 sessionContext.isReadOnly = isReadOnlyDefault ? Boolean.TRUE 192 : Boolean.FALSE; 193 parser = new ParserCommand(this, new Scanner()); 194 195 setResultMemoryRowCount(database.getResultMaxMemoryRows()); 196 resetSchema(); 197 198 sessionData = new SessionData(database, this); 199 statementManager = new StatementManager(database); 200 timeoutManager = new TimeoutManager(); 201 sessionStartTimestamp = System.currentTimeMillis(); 202 } 203 resetSchema()204 void resetSchema() { 205 loggedSchema = null; 206 currentSchema = user.getInitialOrDefaultSchema(); 207 } 208 209 /** 210 * Retrieves the session identifier for this Session. 211 * 212 * @return the session identifier for this Session 213 */ getId()214 public long getId() { 215 return sessionId; 216 } 217 getRandomId()218 public int getRandomId() { 219 return randomId; 220 } 221 222 /** 223 * Closes this Session. 224 */ close()225 public synchronized void close() { 226 227 if (isClosed) { 228 return; 229 } 230 231 rollback(false); 232 233 try { 234 database.logger.writeOtherStatement(this, Tokens.T_DISCONNECT); 235 } catch (HsqlException e) {} 236 237 sessionData.closeAllNavigators(); 238 sessionData.persistentStoreCollection.release(); 239 statementManager.reset(); 240 241 // keep sessionContext and sessionData 242 rowActionList.clear(); 243 244 isClosed = true; 245 user = null; 246 sessionContext.savepoints = null; 247 sessionContext.lastIdentity = null; 248 intConnection = null; 249 250 database.sessionManager.removeSession(this); 251 database.closeIfLast(); 252 253 database = null; 254 } 255 256 /** 257 * Retrieves whether this Session is closed. 258 * 259 * @return true if this Session is closed 260 */ isClosed()261 public boolean isClosed() { 262 return isClosed; 263 } 264 setIsolationDefault(int level)265 public synchronized void setIsolationDefault(int level) { 266 267 if (level == SessionInterface.TX_READ_UNCOMMITTED) { 268 level = SessionInterface.TX_READ_COMMITTED; 269 } 270 271 if (level == isolationLevelDefault) { 272 return; 273 } 274 275 isolationLevelDefault = level; 276 277 if (!isInMidTransaction()) { 278 isolationLevel = isolationLevelDefault; 279 isReadOnlyIsolation = level 280 == SessionInterface.TX_READ_UNCOMMITTED; 281 } 282 } 283 284 /** 285 * sets ISOLATION for the next transaction only 286 */ setIsolation(int level)287 public void setIsolation(int level) { 288 289 if (isInMidTransaction()) { 290 throw Error.error(ErrorCode.X_25001); 291 } 292 293 if (level == SessionInterface.TX_READ_UNCOMMITTED) { 294 level = SessionInterface.TX_READ_COMMITTED; 295 } 296 297 if (isolationLevel != level) { 298 isolationLevel = level; 299 isReadOnlyIsolation = level 300 == SessionInterface.TX_READ_UNCOMMITTED; 301 } 302 } 303 getIsolation()304 public synchronized int getIsolation() { 305 return isolationLevel; 306 } 307 308 /** 309 * Setter for iLastIdentity attribute. 310 * 311 * @param i the new value 312 */ setLastIdentity(Number i)313 void setLastIdentity(Number i) { 314 sessionContext.lastIdentity = i; 315 } 316 317 /** 318 * Getter for iLastIdentity attribute. 319 * 320 * @return the current value 321 */ getLastIdentity()322 public Number getLastIdentity() { 323 return sessionContext.lastIdentity; 324 } 325 326 /** 327 * Retrieves the Database instance to which this 328 * Session represents a connection. 329 * 330 * @return the Database object to which this Session is connected 331 */ getDatabase()332 public Database getDatabase() { 333 return database; 334 } 335 336 /** 337 * Retrieves the name, as known to the database, of the 338 * user currently controlling this Session. 339 * 340 * @return the name of the user currently connected within this Session 341 */ getUsername()342 public String getUsername() { 343 return user.getName().getNameString(); 344 } 345 346 /** 347 * Retrieves the User object representing the user currently controlling 348 * this Session. 349 * 350 * @return this Session's User object 351 */ getUser()352 public User getUser() { 353 return user; 354 } 355 getGrantee()356 public Grantee getGrantee() { 357 return user; 358 } 359 getRole()360 public Grantee getRole() { 361 return role; 362 } 363 364 /** 365 * Sets this Session's User object to the one specified by the 366 * user argument. 367 * 368 * @param user the new User object for this session 369 */ setUser(User user)370 public void setUser(User user) { 371 this.user = user; 372 } 373 setRole(Grantee role)374 public void setRole(Grantee role) { 375 this.role = role; 376 } 377 getMaxRows()378 int getMaxRows() { 379 return sessionContext.currentMaxRows; 380 } 381 382 /** 383 * The SQL command SET MAXROWS n will override the Statement.setMaxRows(n) 384 * for the next direct statement only 385 * 386 * NB this is dedicated to the SET MAXROWS sql statement and should not 387 * otherwise be called. (fredt@users) 388 */ setSQLMaxRows(int rows)389 void setSQLMaxRows(int rows) { 390 sessionMaxRows = rows; 391 } 392 setFeature(String feature, boolean value)393 void setFeature(String feature, boolean value) { 394 395 int number = 8; 396 397 if (value) { 398 sessionOptimization |= number; 399 } else { 400 sessionOptimization &= ~number; 401 } 402 } 403 404 /** 405 * Checks whether this Session's current User has the privileges of 406 * the ADMIN role. 407 */ checkAdmin()408 void checkAdmin() { 409 user.checkAdmin(); 410 } 411 412 /** 413 * This is used for reading - writing to existing tables. 414 * @throws HsqlException 415 */ checkReadWrite()416 void checkReadWrite() { 417 418 if (sessionContext.isReadOnly.booleanValue() || isReadOnlyIsolation) { 419 throw Error.error(ErrorCode.X_25006); 420 } 421 } 422 423 /** 424 * This is used for creating new database objects such as tables. 425 * @throws HsqlException 426 */ checkDDLWrite()427 void checkDDLWrite() { 428 429 if (isProcessingScript || isProcessingLog) { 430 return; 431 } 432 433 checkReadWrite(); 434 } 435 getActionTimestamp()436 public long getActionTimestamp() { 437 return actionTimestamp; 438 } 439 440 /** 441 * Adds a delete action to the row and the transaction manager. 442 * 443 * @param table the table of the row 444 * @param row the deleted row 445 * @throws HsqlException 446 */ addDeleteAction(Table table, PersistentStore store, Row row, int[] colMap)447 public void addDeleteAction(Table table, PersistentStore store, Row row, 448 int[] colMap) { 449 450 // tempActionHistory.add("add delete action " + actionTimestamp); 451 if (abortTransaction) { 452 throw Error.error(ErrorCode.X_40001); 453 } 454 455 if (abortAction) { 456 throw Error.error(ErrorCode.X_40502); 457 } 458 459 database.txManager.addDeleteAction(this, table, store, row, colMap); 460 } 461 addInsertAction(Table table, PersistentStore store, Row row, int[] changedColumns)462 void addInsertAction(Table table, PersistentStore store, Row row, 463 int[] changedColumns) { 464 465 // tempActionHistory.add("add insert to transaction " + actionTimestamp); 466 database.txManager.addInsertAction(this, table, store, row, 467 changedColumns); 468 469 // abort only after adding so that the new row gets removed from indexes 470 if (abortTransaction) { 471 throw Error.error(ErrorCode.X_40001); 472 } 473 474 if (abortAction) { 475 throw Error.error(ErrorCode.X_40502); 476 } 477 } 478 getRowActionList()479 public HsqlArrayList getRowActionList() { 480 return rowActionList; 481 } 482 483 /** 484 * Setter for the autocommit attribute. 485 * 486 * @param autocommit the new value 487 * @throws HsqlException 488 */ setAutoCommit(boolean autocommit)489 public synchronized void setAutoCommit(boolean autocommit) { 490 491 if (isClosed) { 492 return; 493 } 494 495 if (sessionContext.depth > 0) { 496 return; 497 } 498 499 if (sessionContext.isAutoCommit.booleanValue() != autocommit) { 500 commit(false); 501 502 sessionContext.isAutoCommit = autocommit ? Boolean.TRUE 503 : Boolean.FALSE; 504 } 505 } 506 beginAction(Statement cs)507 public void beginAction(Statement cs) { 508 509 actionIndex = rowActionList.size(); 510 511 database.txManager.beginAction(this, cs); 512 database.txManager.beginActionResume(this); 513 } 514 endAction(Result result)515 public void endAction(Result result) { 516 517 // tempActionHistory.add("endAction " + actionTimestamp); 518 abortAction = false; 519 520 sessionData.persistentStoreCollection.clearStatementTables(); 521 522 if (result.mode == ResultConstants.ERROR) { 523 sessionData.persistentStoreCollection.clearResultTables( 524 actionTimestamp); 525 database.txManager.rollbackAction(this); 526 } else { 527 sessionContext 528 .diagnosticsVariables[ExpressionColumn.idx_row_count] = 529 result.mode == ResultConstants.UPDATECOUNT 530 ? Integer.valueOf(result.getUpdateCount()) 531 : ValuePool.INTEGER_0; 532 533 database.txManager.completeActions(this); 534 } 535 536 // tempActionHistory.add("endAction ends " + actionTimestamp); 537 } 538 539 /** 540 * Explicit start of transaction by user 541 */ startTransaction()542 public void startTransaction() { 543 database.txManager.beginTransaction(this); 544 } 545 startPhasedTransaction()546 public synchronized void startPhasedTransaction() {} 547 548 /** 549 * @todo - fredt - for two phased pre-commit - after this call, further 550 * state changing calls should fail 551 */ prepareCommit()552 public synchronized void prepareCommit() { 553 554 if (isClosed) { 555 throw Error.error(ErrorCode.X_08003); 556 } 557 558 if (!database.txManager.prepareCommitActions(this)) { 559 560 // tempActionHistory.add("commit aborts " + actionTimestamp); 561 rollbackNoCheck(false); 562 563 throw Error.error(ErrorCode.X_40001); 564 } 565 } 566 567 /** 568 * Commits any uncommitted transaction this Session may have open 569 * 570 * @throws HsqlException 571 */ commit(boolean chain)572 public synchronized void commit(boolean chain) { 573 574 // tempActionHistory.add("commit " + actionTimestamp); 575 if (isClosed) { 576 return; 577 } 578 579 if (sessionContext.depth > 0) { 580 return; 581 } 582 583 if (isTransaction) { 584 if (!database.txManager.commitTransaction(this)) { 585 586 // tempActionHistory.add("commit aborts " + actionTimestamp); 587 rollbackNoCheck(chain); 588 589 throw Error.error(ErrorCode.X_40001); 590 } 591 } 592 593 endTransaction(true, chain); 594 595 if (database != null && !sessionUser.isSystem() 596 && database.logger.needsCheckpointReset()) { 597 database.checkpointRunner.start(); 598 } 599 } 600 601 /** 602 * Rolls back any uncommitted transaction this Session may have open. 603 * 604 * @throws HsqlException 605 */ rollback(boolean chain)606 public synchronized void rollback(boolean chain) { 607 608 // tempActionHistory.add("rollback " + actionTimestamp); 609 if (sessionContext.depth > 0) { 610 return; 611 } 612 613 rollbackNoCheck(chain); 614 } 615 rollbackNoCheck(boolean chain)616 synchronized void rollbackNoCheck(boolean chain) { 617 618 if (isClosed) { 619 return; 620 } 621 622 if (isTransaction) { 623 database.txManager.rollback(this); 624 } 625 626 endTransaction(false, chain); 627 } 628 endTransaction(boolean commit, boolean chain)629 private void endTransaction(boolean commit, boolean chain) { 630 631 abortTransaction = false; 632 633 sessionContext.resetStack(); 634 sessionContext.savepoints.clear(); 635 sessionContext.savepointTimestamps.clear(); 636 rowActionList.clear(); 637 sessionData.persistentStoreCollection.clearTransactionTables(); 638 sessionData.closeAllTransactionNavigators(); 639 sessionData.clearLobOps(); 640 641 if (!chain) { 642 sessionContext.isReadOnly = isReadOnlyDefault ? Boolean.TRUE 643 : Boolean.FALSE; 644 645 setIsolation(isolationLevelDefault); 646 } 647 648 if (database.logger.getSqlEventLogLevel() > 0) { 649 Statement endTX = commit ? StatementSession.commitNoChainStatement 650 : StatementSession 651 .rollbackNoChainStatement; 652 653 database.logger.logStatementEvent(this, endTX, null, 654 Result.updateZeroResult, 655 SimpleLog.LOG_ERROR); 656 } 657 /* debug 190 658 tempActionHistory.add("commit ends " + actionTimestamp); 659 tempActionHistory.clear(); 660 //*/ 661 } 662 663 /** 664 * Clear structures and reset variables to original. For JDBC use only. 665 * Note: sets autocommit true 666 */ resetSession()667 public synchronized void resetSession() { 668 669 if (isClosed) { 670 return; 671 } 672 673 rollbackNoCheck(false); 674 sessionData.closeAllNavigators(); 675 sessionData.persistentStoreCollection.clearAllTables(); 676 sessionData.clearLobOps(); 677 statementManager.reset(); 678 679 sessionContext.lastIdentity = ValuePool.INTEGER_0; 680 sessionContext.isAutoCommit = Boolean.TRUE; 681 682 setResultMemoryRowCount(database.getResultMaxMemoryRows()); 683 684 user = sessionUser; 685 686 resetSchema(); 687 setZoneSeconds(sessionTimeZoneSeconds); 688 689 sessionMaxRows = 0; 690 ignoreCase = database.sqlIgnoreCase; 691 692 setIsolation(isolationLevelDefault); 693 694 txConflictRollback = database.txConflictRollback; 695 } 696 697 /** 698 * Registers a transaction SAVEPOINT. A new SAVEPOINT with the 699 * name of an existing one replaces the old SAVEPOINT. 700 * 701 * @param name name of the savepoint 702 * @throws HsqlException if there is no current transaction 703 */ savepoint(String name)704 public synchronized void savepoint(String name) { 705 706 int index = sessionContext.savepoints.getIndex(name); 707 708 if (index != -1) { 709 sessionContext.savepoints.remove(name); 710 sessionContext.savepointTimestamps.remove(index); 711 } 712 713 sessionContext.savepoints.add(name, 714 ValuePool.getInt(rowActionList.size())); 715 sessionContext.savepointTimestamps.addLast(actionTimestamp); 716 } 717 718 /** 719 * Performs a partial transaction ROLLBACK to savepoint. 720 * 721 * @param name name of savepoint 722 * @throws HsqlException 723 */ rollbackToSavepoint(String name)724 public synchronized void rollbackToSavepoint(String name) { 725 726 if (isClosed) { 727 return; 728 } 729 730 int index = sessionContext.savepoints.getIndex(name); 731 732 if (index < 0) { 733 throw Error.error(ErrorCode.X_3B001, name); 734 } 735 736 database.txManager.rollbackSavepoint(this, index); 737 } 738 739 /** 740 * Performs a partial transaction ROLLBACK of current savepoint level. 741 * 742 * @throws HsqlException 743 */ rollbackToSavepoint()744 public synchronized void rollbackToSavepoint() { 745 746 if (isClosed) { 747 return; 748 } 749 750 database.txManager.rollbackSavepoint(this, 0); 751 } 752 rollbackAction(int start, long timestamp)753 public synchronized void rollbackAction(int start, long timestamp) { 754 755 if (isClosed) { 756 return; 757 } 758 759 database.txManager.rollbackPartial(this, start, timestamp); 760 } 761 762 /** 763 * Releases a savepoint 764 * 765 * @param name name of savepoint 766 * @throws HsqlException if name does not correspond to a savepoint 767 */ releaseSavepoint(String name)768 public synchronized void releaseSavepoint(String name) { 769 770 // remove this and all later savepoints 771 int index = sessionContext.savepoints.getIndex(name); 772 773 if (index < 0) { 774 throw Error.error(ErrorCode.X_3B001, name); 775 } 776 777 while (sessionContext.savepoints.size() > index) { 778 sessionContext.savepoints.remove(sessionContext.savepoints.size() 779 - 1); 780 sessionContext.savepointTimestamps.removeLast(); 781 } 782 } 783 isInMidTransaction()784 public boolean isInMidTransaction() { 785 return isTransaction; 786 } 787 setNoSQL()788 public void setNoSQL() { 789 sessionContext.noSQL = Boolean.TRUE; 790 } 791 setIgnoreCase(boolean mode)792 public void setIgnoreCase(boolean mode) { 793 ignoreCase = mode; 794 } 795 isIgnorecase()796 public boolean isIgnorecase() { 797 return ignoreCase; 798 } 799 800 /** 801 * sets READ ONLY for next transaction / subtransaction only 802 * 803 * @param readonly the new value 804 */ setReadOnly(boolean readonly)805 public void setReadOnly(boolean readonly) { 806 807 if (!readonly && database.databaseReadOnly) { 808 throw Error.error(ErrorCode.DATABASE_IS_READONLY); 809 } 810 811 if (isInMidTransaction()) { 812 throw Error.error(ErrorCode.X_25001); 813 } 814 815 sessionContext.isReadOnly = readonly ? Boolean.TRUE 816 : Boolean.FALSE; 817 } 818 setReadOnlyDefault(boolean readonly)819 public synchronized void setReadOnlyDefault(boolean readonly) { 820 821 if (!readonly && database.databaseReadOnly) { 822 throw Error.error(ErrorCode.DATABASE_IS_READONLY); 823 } 824 825 isReadOnlyDefault = readonly; 826 827 if (!isInMidTransaction()) { 828 sessionContext.isReadOnly = isReadOnlyDefault ? Boolean.TRUE 829 : Boolean.FALSE; 830 } 831 } 832 833 /** 834 * Getter for readonly attribute. 835 * 836 * @return the current value 837 */ isReadOnly()838 public boolean isReadOnly() { 839 return sessionContext.isReadOnly.booleanValue() || isReadOnlyIsolation; 840 } 841 isReadOnlyDefault()842 public synchronized boolean isReadOnlyDefault() { 843 return isReadOnlyDefault; 844 } 845 846 /** 847 * Getter for autoCommit attribute. 848 * 849 * @return the current value 850 */ isAutoCommit()851 public synchronized boolean isAutoCommit() { 852 return sessionContext.isAutoCommit.booleanValue(); 853 } 854 getStreamBlockSize()855 public synchronized int getStreamBlockSize() { 856 return lobStreamBlockSize; 857 } 858 859 /** 860 * Retrieves an internal Connection object equivalent to the one 861 * that created this Session. 862 * 863 * @return internal connection. 864 */ getInternalConnection()865 JDBCConnection getInternalConnection() { 866 867 if (intConnection == null) { 868 intConnection = new JDBCConnection(this); 869 } 870 871 JDBCDriver.driverInstance.threadConnection.set(intConnection); 872 873 return intConnection; 874 } 875 releaseInternalConnection()876 void releaseInternalConnection() { 877 878 if (sessionContext.depth == 0) { 879 JDBCDriver.driverInstance.threadConnection.set(null); 880 } 881 } 882 883 /** 884 * Retrieves the external JDBC connection 885 */ getJDBCConnection()886 public JDBCConnection getJDBCConnection() { 887 return extConnection; 888 } 889 setJDBCConnection(JDBCConnection connection)890 public void setJDBCConnection(JDBCConnection connection) { 891 extConnection = connection; 892 } 893 getDatabaseUniqueName()894 public String getDatabaseUniqueName() { 895 return database.getNameString(); 896 } 897 898 // boucherb@users 20020810 metadata 1.7.2 899 //---------------------------------------------------------------- 900 private final long connectTime = System.currentTimeMillis(); 901 902 // more efficient for MetaData concerns than checkAdmin 903 904 /** 905 * Getter for admin attribute. 906 * 907 * @return the current value 908 */ isAdmin()909 public boolean isAdmin() { 910 return user.isAdmin(); 911 } 912 913 /** 914 * Getter for connectTime attribute. 915 * 916 * @return the value 917 */ getConnectTime()918 public long getConnectTime() { 919 return connectTime; 920 } 921 922 /** 923 * Count of actions in current transaction. 924 * 925 * @return the current value 926 */ getTransactionSize()927 public int getTransactionSize() { 928 return rowActionList.size(); 929 } 930 getTransactionTimestamp()931 public long getTransactionTimestamp() { 932 return transactionTimestamp; 933 } 934 compileStatement(String sql, int props)935 public Statement compileStatement(String sql, int props) { 936 937 parser.reset(this, sql); 938 939 Statement cs = parser.compileStatement(props); 940 941 return cs; 942 } 943 compileStatement(String sql)944 public Statement compileStatement(String sql) { 945 946 parser.reset(this, sql); 947 948 Statement cs = 949 parser.compileStatement(ResultProperties.defaultPropsValue); 950 951 cs.setCompileTimestamp(Long.MAX_VALUE); 952 953 return cs; 954 } 955 956 /** 957 * Executes the command encapsulated by the cmd argument. 958 * 959 * @param cmd the command to execute 960 * @return the result of executing the command 961 */ execute(Result cmd)962 public synchronized Result execute(Result cmd) { 963 964 if (isClosed) { 965 return Result.newErrorResult(Error.error(ErrorCode.X_08503)); 966 } 967 968 sessionContext.currentMaxRows = 0; 969 isBatch = false; 970 971 JavaSystem.gc(); 972 973 switch (cmd.mode) { 974 975 case ResultConstants.LARGE_OBJECT_OP : { 976 return performLOBOperation((ResultLob) cmd); 977 } 978 case ResultConstants.EXECUTE : { 979 int maxRows = cmd.getUpdateCount(); 980 981 if (maxRows == -1) { 982 sessionContext.currentMaxRows = 0; 983 } else { 984 sessionContext.currentMaxRows = maxRows; 985 } 986 987 Statement cs = cmd.statement; 988 989 if (cs == null 990 || cs.compileTimestamp 991 < database.schemaManager.schemaChangeTimestamp) { 992 long csid = cmd.getStatementID(); 993 994 cs = statementManager.getStatement(this, csid); 995 996 cmd.setStatement(cs); 997 998 if (cs == null) { 999 1000 // invalid sql has been removed already 1001 return Result.newErrorResult( 1002 Error.error(ErrorCode.X_07502)); 1003 } 1004 } 1005 1006 Object[] pvals = (Object[]) cmd.valueData; 1007 Result result = executeCompiledStatement(cs, pvals, 1008 cmd.queryTimeout); 1009 1010 result = performPostExecute(cmd, result); 1011 1012 return result; 1013 } 1014 case ResultConstants.BATCHEXECUTE : { 1015 isBatch = true; 1016 1017 Result result = executeCompiledBatchStatement(cmd); 1018 1019 result = performPostExecute(cmd, result); 1020 1021 return result; 1022 } 1023 case ResultConstants.EXECDIRECT : { 1024 Result result = executeDirectStatement(cmd); 1025 1026 result = performPostExecute(cmd, result); 1027 1028 return result; 1029 } 1030 case ResultConstants.BATCHEXECDIRECT : { 1031 isBatch = true; 1032 1033 Result result = executeDirectBatchStatement(cmd); 1034 1035 result = performPostExecute(cmd, result); 1036 1037 return result; 1038 } 1039 case ResultConstants.PREPARE : { 1040 Statement cs; 1041 1042 try { 1043 cs = statementManager.compile(this, cmd); 1044 } catch (Throwable t) { 1045 String errorString = cmd.getMainString(); 1046 1047 return Result.newErrorResult(t, errorString); 1048 } 1049 1050 Result result = Result.newPrepareResponse(cs); 1051 1052 if (cs.getType() == StatementTypes.SELECT_CURSOR 1053 || cs.getType() == StatementTypes.CALL) { 1054 sessionData.setResultSetProperties(cmd, result); 1055 } 1056 1057 result = performPostExecute(cmd, result); 1058 1059 return result; 1060 } 1061 case ResultConstants.CLOSE_RESULT : { 1062 closeNavigator(cmd.getResultId()); 1063 1064 return Result.updateZeroResult; 1065 } 1066 case ResultConstants.UPDATE_RESULT : { 1067 Result result = this.executeResultUpdate(cmd); 1068 1069 result = performPostExecute(cmd, result); 1070 1071 return result; 1072 } 1073 case ResultConstants.FREESTMT : { 1074 statementManager.freeStatement(cmd.getStatementID()); 1075 1076 return Result.updateZeroResult; 1077 } 1078 case ResultConstants.GETSESSIONATTR : { 1079 int id = cmd.getStatementType(); 1080 1081 return getAttributesResult(id); 1082 } 1083 case ResultConstants.SETSESSIONATTR : { 1084 return setAttributes(cmd); 1085 } 1086 case ResultConstants.ENDTRAN : { 1087 switch (cmd.getActionType()) { 1088 1089 case ResultConstants.TX_COMMIT : 1090 try { 1091 commit(false); 1092 } catch (Throwable t) { 1093 return Result.newErrorResult(t); 1094 } 1095 break; 1096 1097 case ResultConstants.TX_COMMIT_AND_CHAIN : 1098 try { 1099 commit(true); 1100 } catch (Throwable t) { 1101 return Result.newErrorResult(t); 1102 } 1103 break; 1104 1105 case ResultConstants.TX_ROLLBACK : 1106 rollback(false); 1107 break; 1108 1109 case ResultConstants.TX_ROLLBACK_AND_CHAIN : 1110 rollback(true); 1111 break; 1112 1113 case ResultConstants.TX_SAVEPOINT_NAME_RELEASE : 1114 try { 1115 String name = cmd.getMainString(); 1116 1117 releaseSavepoint(name); 1118 } catch (Throwable t) { 1119 return Result.newErrorResult(t); 1120 } 1121 break; 1122 1123 case ResultConstants.TX_SAVEPOINT_NAME_ROLLBACK : 1124 try { 1125 rollbackToSavepoint(cmd.getMainString()); 1126 } catch (Throwable t) { 1127 return Result.newErrorResult(t); 1128 } 1129 break; 1130 1131 case ResultConstants.PREPARECOMMIT : 1132 try { 1133 prepareCommit(); 1134 } catch (Throwable t) { 1135 return Result.newErrorResult(t); 1136 } 1137 break; 1138 } 1139 1140 return Result.updateZeroResult; 1141 } 1142 case ResultConstants.SETCONNECTATTR : { 1143 switch (cmd.getConnectionAttrType()) { 1144 1145 case ResultConstants.SQL_ATTR_SAVEPOINT_NAME : 1146 try { 1147 savepoint(cmd.getMainString()); 1148 } catch (Throwable t) { 1149 return Result.newErrorResult(t); 1150 } 1151 1152 // case ResultConstants.SQL_ATTR_AUTO_IPD 1153 // - always true 1154 // default: throw - case never happens 1155 } 1156 1157 return Result.updateZeroResult; 1158 } 1159 case ResultConstants.REQUESTDATA : { 1160 return sessionData.getDataResultSlice(cmd.getResultId(), 1161 cmd.getUpdateCount(), 1162 cmd.getFetchSize()); 1163 } 1164 case ResultConstants.DISCONNECT : { 1165 close(); 1166 1167 return Result.updateZeroResult; 1168 } 1169 default : { 1170 return Result.newErrorResult( 1171 Error.runtimeError(ErrorCode.U_S0500, "Session")); 1172 } 1173 } 1174 } 1175 performPostExecute(Result command, Result result)1176 private Result performPostExecute(Result command, Result result) { 1177 1178 if (result.mode == ResultConstants.DATA) { 1179 result = sessionData.getDataResultHead(command, result, isNetwork); 1180 } 1181 1182 /* 1183 else if (result.mode == ResultConstants.ERROR) { 1184 while (sessionContext.depth > 0) { 1185 sessionContext.pop(); 1186 } 1187 } 1188 */ 1189 if (sqlWarnings != null && sqlWarnings.size() > 0) { 1190 if (result.mode == ResultConstants.UPDATECOUNT) { 1191 result = new Result(ResultConstants.UPDATECOUNT, 1192 result.getUpdateCount()); 1193 } 1194 1195 HsqlException[] warnings = getAndClearWarnings(); 1196 1197 result.addWarnings(warnings); 1198 } 1199 1200 return result; 1201 } 1202 getRows(long navigatorId, int offset, int blockSize)1203 public RowSetNavigatorClient getRows(long navigatorId, int offset, 1204 int blockSize) { 1205 return sessionData.getRowSetSlice(navigatorId, offset, blockSize); 1206 } 1207 closeNavigator(long id)1208 public synchronized void closeNavigator(long id) { 1209 sessionData.closeNavigator(id); 1210 } 1211 executeDirectStatement(Result cmd)1212 public Result executeDirectStatement(Result cmd) { 1213 1214 String sql = cmd.getMainString(); 1215 HsqlArrayList list; 1216 int maxRows = cmd.getUpdateCount(); 1217 1218 if (maxRows == -1) { 1219 sessionContext.currentMaxRows = 0; 1220 } else if (sessionMaxRows == 0) { 1221 sessionContext.currentMaxRows = maxRows; 1222 } else { 1223 sessionContext.currentMaxRows = sessionMaxRows; 1224 sessionMaxRows = 0; 1225 } 1226 1227 try { 1228 list = parser.compileStatements(sql, cmd); 1229 } catch (Throwable e) { 1230 return Result.newErrorResult(e); 1231 } 1232 1233 Result result = null; 1234 boolean recompile = false; 1235 HsqlName originalSchema = getCurrentSchemaHsqlName(); 1236 1237 for (int i = 0; i < list.size(); i++) { 1238 Statement cs = (Statement) list.get(i); 1239 1240 if (i > 0) { 1241 if (cs.getCompileTimestamp() 1242 > database.txManager.getGlobalChangeTimestamp()) { 1243 recompile = true; 1244 } 1245 1246 if (cs.getSchemaName() != null 1247 && cs.getSchemaName() != originalSchema) { 1248 recompile = true; 1249 } 1250 } 1251 1252 if (recompile) { 1253 cs = compileStatement(cs.getSQL(), cmd.getExecuteProperties()); 1254 } 1255 1256 cs.setGeneratedColumnInfo(cmd.getGeneratedResultType(), 1257 cmd.getGeneratedResultMetaData()); 1258 1259 result = executeCompiledStatement(cs, ValuePool.emptyObjectArray, 1260 cmd.queryTimeout); 1261 1262 if (result.mode == ResultConstants.ERROR) { 1263 break; 1264 } 1265 } 1266 1267 return result; 1268 } 1269 executeDirectStatement(String sql)1270 public Result executeDirectStatement(String sql) { 1271 1272 try { 1273 Statement cs = compileStatement(sql); 1274 Result result = executeCompiledStatement(cs, 1275 ValuePool.emptyObjectArray, 0); 1276 1277 return result; 1278 } catch (HsqlException e) { 1279 return Result.newErrorResult(e); 1280 } 1281 } 1282 executeCompiledStatement(Statement cs, Object[] pvals, int timeout)1283 public Result executeCompiledStatement(Statement cs, Object[] pvals, 1284 int timeout) { 1285 1286 Result r; 1287 1288 if (abortTransaction) { 1289 return handleAbortTransaction(); 1290 } 1291 1292 if (sessionContext.depth > 0) { 1293 if (sessionContext.noSQL.booleanValue() 1294 || cs.isAutoCommitStatement()) { 1295 return Result.newErrorResult(Error.error(ErrorCode.X_46000)); 1296 } 1297 } 1298 1299 if (cs.isAutoCommitStatement()) { 1300 if (isReadOnly()) { 1301 return Result.newErrorResult(Error.error(ErrorCode.X_25006)); 1302 } 1303 1304 try { 1305 1306 /** special autocommit for backward compatibility */ 1307 commit(false); 1308 } catch (HsqlException e) { 1309 database.logger.logInfoEvent("Exception at commit"); 1310 } 1311 } 1312 1313 sessionContext.currentStatement = cs; 1314 1315 boolean isTX = cs.isTransactionStatement(); 1316 1317 if (!isTX) { 1318 actionTimestamp = 1319 database.txManager.getNextGlobalChangeTimestamp(); 1320 1321 sessionContext.setDynamicArguments(pvals); 1322 1323 // statements such as DISCONNECT may close the session 1324 if (database.logger.getSqlEventLogLevel() 1325 >= SimpleLog.LOG_NORMAL) { 1326 database.logger.logStatementEvent(this, cs, pvals, 1327 Result.updateZeroResult, 1328 SimpleLog.LOG_NORMAL); 1329 } 1330 1331 r = cs.execute(this); 1332 sessionContext.currentStatement = null; 1333 1334 return r; 1335 } 1336 1337 repeatLoop: 1338 while (true) { 1339 actionIndex = rowActionList.size(); 1340 1341 database.txManager.beginAction(this, cs); 1342 1343 cs = sessionContext.currentStatement; 1344 1345 if (cs == null) { 1346 return Result.newErrorResult(Error.error(ErrorCode.X_07502)); 1347 } 1348 1349 if (abortTransaction) { 1350 return handleAbortTransaction(); 1351 } 1352 1353 timeoutManager.startTimeout(timeout); 1354 1355 while (true) { 1356 try { 1357 latch.await(); 1358 } catch (InterruptedException e) { 1359 Thread.interrupted(); 1360 1361 continue; 1362 } 1363 1364 break; 1365 } 1366 1367 if (abortAction) { 1368 r = Result.newErrorResult(Error.error(ErrorCode.X_40502)); 1369 1370 endAction(r); 1371 1372 break repeatLoop; 1373 } 1374 1375 if (abortTransaction) { 1376 return handleAbortTransaction(); 1377 } 1378 1379 database.txManager.beginActionResume(this); 1380 1381 // tempActionHistory.add("sql execute " + cs.sql + " " + actionTimestamp + " " + rowActionList.size()); 1382 sessionContext.setDynamicArguments(pvals); 1383 1384 r = cs.execute(this); 1385 1386 if (database.logger.getSqlEventLogLevel() 1387 >= SimpleLog.LOG_NORMAL) { 1388 database.logger.logStatementEvent(this, cs, pvals, r, 1389 SimpleLog.LOG_NORMAL); 1390 } 1391 1392 // tempActionHistory.add("sql execute end " + actionTimestamp + " " + rowActionList.size()); 1393 endAction(r); 1394 1395 if (abortTransaction) { 1396 break repeatLoop; 1397 } 1398 1399 if (redoAction) { 1400 redoAction = false; 1401 1402 while (true) { 1403 try { 1404 latch.await(); 1405 } catch (InterruptedException e) { 1406 Thread.interrupted(); 1407 1408 continue; 1409 } 1410 1411 break; 1412 } 1413 } else { 1414 break repeatLoop; 1415 } 1416 } 1417 1418 if (abortTransaction) { 1419 return handleAbortTransaction(); 1420 } 1421 1422 if (sessionContext.depth == 0 1423 && (sessionContext.isAutoCommit.booleanValue() 1424 || cs.isAutoCommitStatement())) { 1425 try { 1426 if (r.mode == ResultConstants.ERROR) { 1427 rollbackNoCheck(false); 1428 } else { 1429 commit(false); 1430 } 1431 } catch (Exception e) { 1432 sessionContext.currentStatement = null; 1433 1434 return Result.newErrorResult(Error.error(ErrorCode.X_40001, 1435 e)); 1436 } 1437 } 1438 1439 sessionContext.currentStatement = null; 1440 1441 return r; 1442 } 1443 handleAbortTransaction()1444 private Result handleAbortTransaction() { 1445 1446 rollbackNoCheck(false); 1447 1448 sessionContext.currentStatement = null; 1449 1450 return Result.newErrorResult(Error.error(ErrorCode.X_40001)); 1451 } 1452 executeCompiledBatchStatement(Result cmd)1453 private Result executeCompiledBatchStatement(Result cmd) { 1454 1455 long csid; 1456 Statement cs; 1457 int[] updateCounts; 1458 int count; 1459 1460 cs = cmd.statement; 1461 1462 if (cs == null 1463 || cs.compileTimestamp 1464 < database.schemaManager.schemaChangeTimestamp) { 1465 csid = cmd.getStatementID(); 1466 cs = statementManager.getStatement(this, csid); 1467 1468 if (cs == null) { 1469 1470 // invalid sql has been removed already 1471 return Result.newErrorResult(Error.error(ErrorCode.X_07502)); 1472 } 1473 } 1474 1475 count = 0; 1476 1477 RowSetNavigator nav = cmd.initialiseNavigator(); 1478 1479 updateCounts = new int[nav.getSize()]; 1480 1481 Result generatedResult = null; 1482 1483 if (cs.hasGeneratedColumns()) { 1484 generatedResult = 1485 Result.newGeneratedDataResult(cs.generatedResultMetaData()); 1486 } 1487 1488 Result error = null; 1489 1490 while (nav.hasNext()) { 1491 Object[] pvals = nav.getNext(); 1492 Result in = executeCompiledStatement(cs, pvals, cmd.queryTimeout); 1493 1494 // On the client side, iterate over the vals and throw 1495 // a BatchUpdateException if a batch status value of 1496 // esultConstants.EXECUTE_FAILED is encountered in the result 1497 if (in.isUpdateCount()) { 1498 if (cs.hasGeneratedColumns()) { 1499 RowSetNavigator navgen = 1500 in.getChainedResult().getNavigator(); 1501 1502 while (navgen.hasNext()) { 1503 Object[] generatedRow = navgen.getNext(); 1504 1505 generatedResult.getNavigator().add(generatedRow); 1506 } 1507 } 1508 1509 updateCounts[count++] = in.getUpdateCount(); 1510 } else if (in.isData()) { 1511 1512 // FIXME: we don't have what it takes yet 1513 // to differentiate between things like 1514 // stored procedure calls to methods with 1515 // void return type and select statements with 1516 // a single row/column containing null 1517 updateCounts[count++] = ResultConstants.SUCCESS_NO_INFO; 1518 } else if (in.mode == ResultConstants.CALL_RESPONSE) { 1519 updateCounts[count++] = ResultConstants.SUCCESS_NO_INFO; 1520 } else if (in.mode == ResultConstants.ERROR) { 1521 updateCounts = ArrayUtil.arraySlice(updateCounts, 0, count); 1522 error = in; 1523 1524 break; 1525 } else { 1526 throw Error.runtimeError(ErrorCode.U_S0500, "Session"); 1527 } 1528 } 1529 1530 return Result.newBatchedExecuteResponse(updateCounts, generatedResult, 1531 error); 1532 } 1533 executeDirectBatchStatement(Result cmd)1534 private Result executeDirectBatchStatement(Result cmd) { 1535 1536 int[] updateCounts; 1537 int count; 1538 1539 count = 0; 1540 1541 RowSetNavigator nav = cmd.initialiseNavigator(); 1542 1543 updateCounts = new int[nav.getSize()]; 1544 1545 Result error = null; 1546 1547 while (nav.hasNext()) { 1548 Result in; 1549 Object[] data = nav.getNext(); 1550 String sql = (String) data[0]; 1551 1552 try { 1553 Statement cs = compileStatement(sql); 1554 1555 in = executeCompiledStatement(cs, ValuePool.emptyObjectArray, 1556 cmd.queryTimeout); 1557 } catch (Throwable t) { 1558 in = Result.newErrorResult(t); 1559 1560 // if (t instanceof OutOfMemoryError) { 1561 // System.gc(); 1562 // } 1563 // "in" already equals "err" 1564 // maybe test for OOME and do a gc() ? 1565 // t.printStackTrace(); 1566 } 1567 1568 if (in.isUpdateCount()) { 1569 updateCounts[count++] = in.getUpdateCount(); 1570 } else if (in.isData()) { 1571 1572 // FIXME: we don't have what it takes yet 1573 // to differentiate between things like 1574 // stored procedure calls to methods with 1575 // void return type and select statements with 1576 // a single row/column containing null 1577 updateCounts[count++] = ResultConstants.SUCCESS_NO_INFO; 1578 } else if (in.mode == ResultConstants.CALL_RESPONSE) { 1579 updateCounts[count++] = ResultConstants.SUCCESS_NO_INFO; 1580 } else if (in.mode == ResultConstants.ERROR) { 1581 updateCounts = ArrayUtil.arraySlice(updateCounts, 0, count); 1582 error = in; 1583 1584 break; 1585 } else { 1586 throw Error.runtimeError(ErrorCode.U_S0500, "Session"); 1587 } 1588 } 1589 1590 return Result.newBatchedExecuteResponse(updateCounts, null, error); 1591 } 1592 1593 /** 1594 * Retrieves the result of inserting, updating or deleting a row 1595 * from an updatable result. 1596 * 1597 * @return the result of executing the statement 1598 */ executeResultUpdate(Result cmd)1599 private Result executeResultUpdate(Result cmd) { 1600 1601 long id = cmd.getResultId(); 1602 int actionType = cmd.getActionType(); 1603 Result result = sessionData.getDataResult(id); 1604 1605 if (result == null) { 1606 return Result.newErrorResult(Error.error(ErrorCode.X_24501)); 1607 } 1608 1609 Object[] pvals = (Object[]) cmd.valueData; 1610 Type[] types = cmd.metaData.columnTypes; 1611 StatementQuery statement = (StatementQuery) result.getStatement(); 1612 1613 sessionContext.rowUpdateStatement.setRowActionProperties(result, 1614 actionType, statement, types); 1615 1616 Result resultOut = 1617 executeCompiledStatement(sessionContext.rowUpdateStatement, pvals, 1618 cmd.queryTimeout); 1619 1620 return resultOut; 1621 } 1622 1623 // session DATETIME functions 1624 long currentDateSCN; 1625 long currentTimestampSCN; 1626 long currentMillis; 1627 private TimestampData currentDate; 1628 private TimestampData currentTimestamp; 1629 private TimestampData localTimestamp; 1630 private TimeData currentTime; 1631 private TimeData localTime; 1632 1633 /** 1634 * Returns the current date, unchanged for the duration of the current 1635 * execution unit (statement).<p> 1636 * 1637 * SQL standards require that CURRENT_DATE, CURRENT_TIME and 1638 * CURRENT_TIMESTAMP are all evaluated at the same point of 1639 * time in the duration of each SQL statement, no matter how long the 1640 * SQL statement takes to complete.<p> 1641 * 1642 * When this method or a corresponding method for CURRENT_TIME or 1643 * CURRENT_TIMESTAMP is first called in the scope of a system change 1644 * number, currentMillis is set to the current system time. All further 1645 * CURRENT_XXXX calls in this scope will use this millisecond value. 1646 * (fredt@users) 1647 */ getCurrentDate()1648 public synchronized TimestampData getCurrentDate() { 1649 1650 resetCurrentTimestamp(); 1651 1652 if (currentDate == null) { 1653 currentDate = (TimestampData) Type.SQL_DATE.getValue(currentMillis 1654 / 1000, 0, getZoneSeconds()); 1655 } 1656 1657 return currentDate; 1658 } 1659 1660 /** 1661 * Returns the current time, unchanged for the duration of the current 1662 * execution unit (statement) 1663 */ getCurrentTime(boolean withZone)1664 synchronized TimeData getCurrentTime(boolean withZone) { 1665 1666 resetCurrentTimestamp(); 1667 1668 if (withZone) { 1669 if (currentTime == null) { 1670 int seconds = 1671 (int) (HsqlDateTime.getNormalisedTime( 1672 getCalendarGMT(), currentMillis)) / 1000; 1673 int nanos = (int) (currentMillis % 1000) * 1000000; 1674 1675 currentTime = new TimeData(seconds, nanos, getZoneSeconds()); 1676 } 1677 1678 return currentTime; 1679 } else { 1680 if (localTime == null) { 1681 int seconds = 1682 (int) (HsqlDateTime.getNormalisedTime( 1683 getCalendarGMT(), 1684 currentMillis + getZoneSeconds() * 1000L)) / 1000; 1685 int nanos = (int) (currentMillis % 1000) * 1000000; 1686 1687 localTime = new TimeData(seconds, nanos, 0); 1688 } 1689 1690 return localTime; 1691 } 1692 } 1693 1694 /** 1695 * Returns the current timestamp, unchanged for the duration of the current 1696 * execution unit (statement) 1697 */ getCurrentTimestamp(boolean withZone)1698 synchronized TimestampData getCurrentTimestamp(boolean withZone) { 1699 1700 resetCurrentTimestamp(); 1701 1702 if (withZone) { 1703 if (currentTimestamp == null) { 1704 int nanos = (int) (currentMillis % 1000) * 1000000; 1705 1706 currentTimestamp = new TimestampData((currentMillis / 1000), 1707 nanos, getZoneSeconds()); 1708 } 1709 1710 return currentTimestamp; 1711 } else { 1712 if (localTimestamp == null) { 1713 int nanos = (int) (currentMillis % 1000) * 1000000; 1714 1715 localTimestamp = new TimestampData(currentMillis / 1000 1716 + getZoneSeconds(), nanos, 1717 0); 1718 } 1719 1720 return localTimestamp; 1721 } 1722 } 1723 getSystemTimestamp(boolean withZone)1724 synchronized TimestampData getSystemTimestamp(boolean withZone) { 1725 1726 long millis = System.currentTimeMillis(); 1727 long seconds = millis / 1000; 1728 int nanos = (int) (millis % 1000) * 1000000; 1729 TimeZone zone = TimeZone.getDefault(); 1730 int offset = zone.getOffset(millis) / 1000; 1731 1732 if (!withZone) { 1733 seconds += offset; 1734 offset = 0; 1735 } 1736 1737 return new TimestampData(seconds, nanos, offset); 1738 } 1739 resetCurrentTimestamp()1740 private void resetCurrentTimestamp() { 1741 1742 if (currentTimestampSCN != actionTimestamp) { 1743 currentTimestampSCN = actionTimestamp; 1744 currentMillis = System.currentTimeMillis(); 1745 currentDate = null; 1746 currentTimestamp = null; 1747 localTimestamp = null; 1748 currentTime = null; 1749 localTime = null; 1750 } 1751 } 1752 getAttributesResult(int id)1753 private Result getAttributesResult(int id) { 1754 1755 Result r = Result.newSessionAttributesResult(); 1756 Object[] data = r.getSingleRowData(); 1757 1758 data[SessionInterface.INFO_ID] = ValuePool.getInt(id); 1759 1760 switch (id) { 1761 1762 case SessionInterface.INFO_ISOLATION : 1763 data[SessionInterface.INFO_INTEGER] = 1764 ValuePool.getInt(isolationLevel); 1765 break; 1766 1767 case SessionInterface.INFO_AUTOCOMMIT : 1768 data[SessionInterface.INFO_BOOLEAN] = 1769 sessionContext.isAutoCommit; 1770 break; 1771 1772 case SessionInterface.INFO_CONNECTION_READONLY : 1773 data[SessionInterface.INFO_BOOLEAN] = 1774 sessionContext.isReadOnly; 1775 break; 1776 1777 case SessionInterface.INFO_CATALOG : 1778 data[SessionInterface.INFO_VARCHAR] = 1779 database.getCatalogName().name; 1780 break; 1781 } 1782 1783 return r; 1784 } 1785 setAttributes(Result r)1786 private Result setAttributes(Result r) { 1787 1788 Object[] row = r.getSessionAttributes(); 1789 int id = ((Integer) row[SessionInterface.INFO_ID]).intValue(); 1790 1791 try { 1792 switch (id) { 1793 1794 case SessionInterface.INFO_AUTOCOMMIT : { 1795 boolean value = 1796 ((Boolean) row[SessionInterface.INFO_BOOLEAN]) 1797 .booleanValue(); 1798 1799 this.setAutoCommit(value); 1800 1801 break; 1802 } 1803 case SessionInterface.INFO_CONNECTION_READONLY : { 1804 boolean value = 1805 ((Boolean) row[SessionInterface.INFO_BOOLEAN]) 1806 .booleanValue(); 1807 1808 this.setReadOnlyDefault(value); 1809 1810 break; 1811 } 1812 case SessionInterface.INFO_ISOLATION : { 1813 int value = 1814 ((Integer) row[SessionInterface.INFO_INTEGER]) 1815 .intValue(); 1816 1817 this.setIsolationDefault(value); 1818 1819 break; 1820 } 1821 case SessionInterface.INFO_CATALOG : { 1822 String value = 1823 ((String) row[SessionInterface.INFO_VARCHAR]); 1824 1825 this.setCatalog(value); 1826 } 1827 } 1828 } catch (HsqlException e) { 1829 return Result.newErrorResult(e); 1830 } 1831 1832 return Result.updateZeroResult; 1833 } 1834 getAttribute(int id)1835 public synchronized Object getAttribute(int id) { 1836 1837 switch (id) { 1838 1839 case SessionInterface.INFO_ISOLATION : 1840 return ValuePool.getInt(isolationLevel); 1841 1842 case SessionInterface.INFO_AUTOCOMMIT : 1843 return sessionContext.isAutoCommit; 1844 1845 case SessionInterface.INFO_CONNECTION_READONLY : 1846 return isReadOnlyDefault ? Boolean.TRUE 1847 : Boolean.FALSE; 1848 1849 case SessionInterface.INFO_CATALOG : 1850 return database.getCatalogName().name; 1851 } 1852 1853 return null; 1854 } 1855 setAttribute(int id, Object object)1856 public synchronized void setAttribute(int id, Object object) { 1857 1858 switch (id) { 1859 1860 case SessionInterface.INFO_AUTOCOMMIT : { 1861 boolean value = ((Boolean) object).booleanValue(); 1862 1863 this.setAutoCommit(value); 1864 1865 break; 1866 } 1867 case SessionInterface.INFO_CONNECTION_READONLY : { 1868 boolean value = ((Boolean) object).booleanValue(); 1869 1870 this.setReadOnlyDefault(value); 1871 1872 break; 1873 } 1874 case SessionInterface.INFO_ISOLATION : { 1875 int value = ((Integer) object).intValue(); 1876 1877 this.setIsolationDefault(value); 1878 1879 break; 1880 } 1881 case SessionInterface.INFO_CATALOG : { 1882 String value = ((String) object); 1883 1884 this.setCatalog(value); 1885 } 1886 } 1887 } 1888 1889 // lobs createBlob(long length)1890 public BlobDataID createBlob(long length) { 1891 1892 long lobID = database.lobManager.createBlob(this, length); 1893 1894 if (lobID == 0) { 1895 throw Error.error(ErrorCode.X_0F502); 1896 } 1897 1898 sessionData.registerNewLob(lobID); 1899 1900 return new BlobDataID(lobID); 1901 } 1902 createClob(long length)1903 public ClobDataID createClob(long length) { 1904 1905 long lobID = database.lobManager.createClob(this, length); 1906 1907 if (lobID == 0) { 1908 throw Error.error(ErrorCode.X_0F502); 1909 } 1910 1911 sessionData.registerNewLob(lobID); 1912 1913 return new ClobDataID(lobID); 1914 } 1915 registerResultLobs(Result result)1916 public void registerResultLobs(Result result) { 1917 sessionData.registerLobForResult(result); 1918 } 1919 allocateResultLob(ResultLob result, InputStream inputStream)1920 public void allocateResultLob(ResultLob result, InputStream inputStream) { 1921 sessionData.allocateLobForResult(result, inputStream); 1922 } 1923 performLOBOperation(ResultLob cmd)1924 Result performLOBOperation(ResultLob cmd) { 1925 1926 long id = cmd.getLobID(); 1927 int operation = cmd.getSubType(); 1928 1929 switch (operation) { 1930 1931 case ResultLob.LobResultTypes.REQUEST_GET_LOB : { 1932 return database.lobManager.getLob(id, cmd.getOffset(), 1933 cmd.getBlockLength()); 1934 } 1935 case ResultLob.LobResultTypes.REQUEST_GET_LENGTH : { 1936 return database.lobManager.getLength(id); 1937 } 1938 case ResultLob.LobResultTypes.REQUEST_GET_BYTES : { 1939 return database.lobManager.getBytes( 1940 id, cmd.getOffset(), (int) cmd.getBlockLength()); 1941 } 1942 case ResultLob.LobResultTypes.REQUEST_SET_BYTES : { 1943 return database.lobManager.setBytes( 1944 id, cmd.getOffset(), cmd.getByteArray(), 1945 (int) cmd.getBlockLength()); 1946 } 1947 case ResultLob.LobResultTypes.REQUEST_GET_CHARS : { 1948 return database.lobManager.getChars( 1949 id, cmd.getOffset(), (int) cmd.getBlockLength()); 1950 } 1951 case ResultLob.LobResultTypes.REQUEST_SET_CHARS : { 1952 return database.lobManager.setChars( 1953 id, cmd.getOffset(), cmd.getCharArray(), 1954 (int) cmd.getBlockLength()); 1955 } 1956 case ResultLob.LobResultTypes.REQUEST_TRUNCATE : { 1957 return database.lobManager.truncate(id, cmd.getOffset()); 1958 } 1959 case ResultLob.LobResultTypes.REQUEST_DUPLICATE_LOB : { 1960 return database.lobManager.createDuplicateLob(id); 1961 } 1962 case ResultLob.LobResultTypes.REQUEST_CREATE_BYTES : 1963 case ResultLob.LobResultTypes.REQUEST_CREATE_CHARS : 1964 case ResultLob.LobResultTypes.REQUEST_GET_BYTE_PATTERN_POSITION : 1965 case ResultLob.LobResultTypes.REQUEST_GET_CHAR_PATTERN_POSITION : { 1966 throw Error.error(ErrorCode.X_0A501); 1967 } 1968 default : { 1969 throw Error.runtimeError(ErrorCode.U_S0500, "Session"); 1970 } 1971 } 1972 } 1973 1974 // DatabaseMetaData.getURL should work as specified for 1975 // internal connections too. getInternalConnectionURL()1976 public String getInternalConnectionURL() { 1977 return DatabaseURL.S_URL_PREFIX + database.getURI(); 1978 } 1979 cancel(Result result)1980 public Result cancel(Result result) { 1981 1982 if (result.getType() == ResultConstants.SQLCANCEL) { 1983 if (result.getSessionRandomID() == randomId) { 1984 database.txManager.resetSession( 1985 null, this, TransactionManager.resetSessionAbort); 1986 } 1987 } 1988 1989 return Result.updateZeroResult; 1990 } 1991 isProcessingScript()1992 public boolean isProcessingScript() { 1993 return isProcessingScript; 1994 } 1995 isProcessingLog()1996 public boolean isProcessingLog() { 1997 return isProcessingLog; 1998 } 1999 2000 // schema object methods setSchema(String schema)2001 public void setSchema(String schema) { 2002 currentSchema = database.schemaManager.getSchemaHsqlName(schema); 2003 } 2004 setCatalog(String catalog)2005 public void setCatalog(String catalog) { 2006 2007 if (database.getCatalogName().name.equals(catalog)) { 2008 return; 2009 } 2010 2011 throw Error.error(ErrorCode.X_3D000); 2012 } 2013 2014 /** 2015 * If schemaName is null, return the current schema name, else return 2016 * the HsqlName object for the schema. If schemaName does not exist, 2017 * throw. 2018 */ getSchemaHsqlName(String name)2019 HsqlName getSchemaHsqlName(String name) { 2020 return name == null ? currentSchema 2021 : database.schemaManager.getSchemaHsqlName(name); 2022 } 2023 2024 /** 2025 * Same as above, but return string 2026 */ getSchemaName(String name)2027 public String getSchemaName(String name) { 2028 return name == null ? currentSchema.name 2029 : database.schemaManager.getSchemaName(name); 2030 } 2031 setCurrentSchemaHsqlName(HsqlName name)2032 public void setCurrentSchemaHsqlName(HsqlName name) { 2033 currentSchema = name; 2034 } 2035 getCurrentSchemaHsqlName()2036 public HsqlName getCurrentSchemaHsqlName() { 2037 return currentSchema; 2038 } 2039 getResultMemoryRowCount()2040 public int getResultMemoryRowCount() { 2041 return resultMaxMemoryRows; 2042 } 2043 setResultMemoryRowCount(int count)2044 public void setResultMemoryRowCount(int count) { 2045 2046 if (database.logger.getTempDirectoryPath() != null) { 2047 if (count < 0) { 2048 count = 0; 2049 } 2050 2051 resultMaxMemoryRows = count; 2052 } 2053 } 2054 2055 // warnings 2056 HsqlDeque sqlWarnings; 2057 addWarning(HsqlException warning)2058 public void addWarning(HsqlException warning) { 2059 2060 if (sqlWarnings == null) { 2061 sqlWarnings = new HsqlDeque(); 2062 } 2063 2064 if (sqlWarnings.size() > 9) { 2065 sqlWarnings.removeFirst(); 2066 } 2067 2068 int index = sqlWarnings.indexOf(warning); 2069 2070 if (index >= 0) { 2071 sqlWarnings.remove(index); 2072 } 2073 2074 sqlWarnings.add(warning); 2075 } 2076 getAndClearWarnings()2077 public HsqlException[] getAndClearWarnings() { 2078 2079 if (sqlWarnings == null) { 2080 return HsqlException.emptyArray; 2081 } 2082 2083 HsqlException[] array = new HsqlException[sqlWarnings.size()]; 2084 2085 sqlWarnings.toArray(array); 2086 sqlWarnings.clear(); 2087 2088 return array; 2089 } 2090 getLastWarning()2091 public HsqlException getLastWarning() { 2092 2093 if (sqlWarnings == null || sqlWarnings.size() == 0) { 2094 return null; 2095 } 2096 2097 return (HsqlException) sqlWarnings.getLast(); 2098 } 2099 clearWarnings()2100 public void clearWarnings() { 2101 2102 if (sqlWarnings != null) { 2103 sqlWarnings.clear(); 2104 } 2105 } 2106 2107 // session zone 2108 private Calendar calendar; 2109 private Calendar calendarGMT; 2110 getZoneSeconds()2111 public int getZoneSeconds() { 2112 return timeZoneSeconds; 2113 } 2114 setZoneSeconds(int seconds)2115 public void setZoneSeconds(int seconds) { 2116 timeZoneSeconds = seconds; 2117 } 2118 getCalendar()2119 public Calendar getCalendar() { 2120 2121 if (calendar == null) { 2122 if (zoneString == null) { 2123 calendar = new GregorianCalendar(); 2124 } else { 2125 TimeZone zone = TimeZone.getTimeZone(zoneString); 2126 2127 calendar = new GregorianCalendar(zone); 2128 } 2129 } 2130 2131 return calendar; 2132 } 2133 getCalendarGMT()2134 public Calendar getCalendarGMT() { 2135 2136 if (calendarGMT == null) { 2137 calendarGMT = new GregorianCalendar(TimeZone.getTimeZone("GMT")); 2138 } 2139 2140 return calendarGMT; 2141 } 2142 getSimpleDateFormatGMT()2143 public SimpleDateFormat getSimpleDateFormatGMT() { 2144 2145 if (simpleDateFormatGMT == null) { 2146 simpleDateFormatGMT = new SimpleDateFormat("MMMM", Locale.ENGLISH); 2147 2148 simpleDateFormatGMT.setCalendar(getCalendarGMT()); 2149 } 2150 2151 return simpleDateFormatGMT; 2152 } 2153 2154 // services 2155 TypedComparator typedComparator = Type.newComparator(this); 2156 Scanner secondaryScanner; 2157 SimpleDateFormat simpleDateFormat; 2158 SimpleDateFormat simpleDateFormatGMT; 2159 Random randomGenerator = new Random(); 2160 long seed = -1; 2161 public final int randomId = randomGenerator.nextInt(); 2162 2163 // getComparator()2164 public TypedComparator getComparator() { 2165 return typedComparator; 2166 } 2167 random(long seed)2168 public double random(long seed) { 2169 2170 if (this.seed != seed) { 2171 randomGenerator.setSeed(seed); 2172 2173 this.seed = seed; 2174 } 2175 2176 return randomGenerator.nextDouble(); 2177 } 2178 random()2179 public double random() { 2180 return randomGenerator.nextDouble(); 2181 } 2182 getScanner()2183 public Scanner getScanner() { 2184 2185 if (secondaryScanner == null) { 2186 secondaryScanner = new Scanner(); 2187 } 2188 2189 return secondaryScanner; 2190 } 2191 2192 // properties 2193 HsqlProperties clientProperties; 2194 getClientProperties()2195 public HsqlProperties getClientProperties() { 2196 2197 if (clientProperties == null) { 2198 clientProperties = new HsqlProperties(); 2199 2200 clientProperties.setProperty( 2201 HsqlDatabaseProperties.jdbc_translate_tti_types, 2202 database.sqlTranslateTTI); 2203 clientProperties.setProperty( 2204 HsqlDatabaseProperties.sql_live_object, 2205 database.sqlLiveObject); 2206 } 2207 2208 return clientProperties; 2209 } 2210 2211 // logging and SEQUENCE current values logSequences()2212 void logSequences() { 2213 2214 HashMap map = sessionData.sequenceUpdateMap; 2215 2216 if (map == null || map.isEmpty()) { 2217 return; 2218 } 2219 2220 Iterator it = map.keySet().iterator(); 2221 2222 for (int i = 0, size = map.size(); i < size; i++) { 2223 NumberSequence sequence = (NumberSequence) it.next(); 2224 2225 database.logger.writeSequenceStatement(this, sequence); 2226 } 2227 2228 sessionData.sequenceUpdateMap.clear(); 2229 } 2230 getStartTransactionSQL()2231 String getStartTransactionSQL() { 2232 2233 StringBuffer sb = new StringBuffer(); 2234 2235 sb.append(Tokens.T_START).append(' ').append(Tokens.T_TRANSACTION); 2236 2237 if (isolationLevel != isolationLevelDefault) { 2238 sb.append(' '); 2239 appendIsolationSQL(sb, isolationLevel); 2240 } 2241 2242 return sb.toString(); 2243 } 2244 getTransactionIsolationSQL()2245 String getTransactionIsolationSQL() { 2246 2247 StringBuffer sb = new StringBuffer(); 2248 2249 sb.append(Tokens.T_SET).append(' ').append(Tokens.T_TRANSACTION); 2250 sb.append(' '); 2251 appendIsolationSQL(sb, isolationLevel); 2252 2253 return sb.toString(); 2254 } 2255 getSessionIsolationSQL()2256 String getSessionIsolationSQL() { 2257 2258 StringBuffer sb = new StringBuffer(); 2259 2260 sb.append(Tokens.T_SET).append(' ').append(Tokens.T_SESSION); 2261 sb.append(' ').append(Tokens.T_CHARACTERISTICS).append(' '); 2262 sb.append(Tokens.T_AS).append(' ').append(Tokens.T_TRANSACTION).append( 2263 ' '); 2264 appendIsolationSQL(sb, isolationLevelDefault); 2265 2266 return sb.toString(); 2267 } 2268 appendIsolationSQL(StringBuffer sb, int isolationLevel)2269 static void appendIsolationSQL(StringBuffer sb, int isolationLevel) { 2270 2271 sb.append(Tokens.T_ISOLATION).append(' '); 2272 sb.append(Tokens.T_LEVEL).append(' '); 2273 sb.append(getIsolationString(isolationLevel)); 2274 } 2275 getIsolationString(int isolationLevel)2276 static String getIsolationString(int isolationLevel) { 2277 2278 switch (isolationLevel) { 2279 2280 case SessionInterface.TX_READ_UNCOMMITTED : 2281 case SessionInterface.TX_READ_COMMITTED : 2282 StringBuffer sb = new StringBuffer(); 2283 2284 sb.append(Tokens.T_READ).append(' '); 2285 sb.append(Tokens.T_COMMITTED); 2286 2287 return sb.toString(); 2288 2289 case SessionInterface.TX_REPEATABLE_READ : 2290 case SessionInterface.TX_SERIALIZABLE : 2291 default : 2292 return Tokens.T_SERIALIZABLE; 2293 } 2294 } 2295 getSetSchemaStatement()2296 String getSetSchemaStatement() { 2297 return "SET SCHEMA " + currentSchema.statementName; 2298 } 2299 2300 // timeouts 2301 class TimeoutManager { 2302 2303 volatile long actionTimestamp; 2304 volatile int currentTimeout; 2305 volatile boolean aborted; 2306 startTimeout(int timeout)2307 void startTimeout(int timeout) { 2308 2309 aborted = false; 2310 2311 if (timeout == 0) { 2312 return; 2313 } 2314 2315 currentTimeout = timeout; 2316 actionTimestamp = Session.this.actionTimestamp; 2317 2318 database.timeoutRunner.addSession(Session.this); 2319 } 2320 endTimeout()2321 boolean endTimeout() { 2322 2323 boolean aborted = this.aborted; 2324 2325 currentTimeout = 0; 2326 this.aborted = false; 2327 2328 return aborted; 2329 } 2330 checkTimeout()2331 public boolean checkTimeout() { 2332 2333 if (currentTimeout == 0) { 2334 return true; 2335 } 2336 2337 if (aborted || actionTimestamp != Session.this.actionTimestamp) { 2338 actionTimestamp = 0; 2339 currentTimeout = 0; 2340 aborted = false; 2341 2342 return true; 2343 } 2344 2345 --currentTimeout; 2346 2347 if (currentTimeout <= 0) { 2348 currentTimeout = 0; 2349 aborted = true; 2350 2351 database.txManager.resetSession( 2352 null, Session.this, TransactionManager.resetSessionAbort); 2353 2354 return true; 2355 } 2356 2357 return false; 2358 } 2359 } 2360 } 2361