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