KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hsqldb > Session


1 /* Copyright (c) 1995-2000, The Hypersonic SQL 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 Hypersonic SQL 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 THE HYPERSONIC SQL GROUP,
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  * This software consists of voluntary contributions made by many individuals
31  * on behalf of the Hypersonic SQL Group.
32  *
33  *
34  * For work added by the HSQL Development Group:
35  *
36  * Copyright (c) 2001-2005, The HSQL Development Group
37  * All rights reserved.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions are met:
41  *
42  * Redistributions of source code must retain the above copyright notice, this
43  * list of conditions and the following disclaimer.
44  *
45  * Redistributions in binary form must reproduce the above copyright notice,
46  * this list of conditions and the following disclaimer in the documentation
47  * and/or other materials provided with the distribution.
48  *
49  * Neither the name of the HSQL Development Group nor the names of its
50  * contributors may be used to endorse or promote products derived from this
51  * software without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
54  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
57  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
58  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
59  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
60  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
61  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
62  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
63  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64  */

65
66
67 package org.hsqldb;
68
69 import java.sql.Date JavaDoc;
70 import java.sql.Time JavaDoc;
71 import java.sql.Timestamp JavaDoc;
72
73 import org.hsqldb.HsqlNameManager.HsqlName;
74 import org.hsqldb.jdbc.jdbcConnection;
75 import org.hsqldb.lib.ArrayUtil;
76 import org.hsqldb.lib.HashMappedList;
77 import org.hsqldb.lib.HsqlArrayList;
78 import org.hsqldb.lib.IntKeyHashMap;
79 import org.hsqldb.lib.SimpleLog;
80 import org.hsqldb.lib.java.JavaSystem;
81 import org.hsqldb.store.ValuePool;
82
83 // fredt@users 20020320 - doc 1.7.0 - update
84
// fredt@users 20020315 - patch 1.7.0 - switch for scripting
85
// fredt@users 20020130 - patch 476694 by velichko - transaction savepoints
86
// additions in different parts to support savepoint transactions
87
// fredt@users 20020910 - patch 1.7.1 by fredt - database readonly enforcement
88
// fredt@users 20020912 - patch 1.7.1 by fredt - permanent internal connection
89
// boucherb@users 20030512 - patch 1.7.2 - compiled statements
90
// - session becomes execution hub
91
// boucherb@users 20050510 - patch 1.7.2 - generalized Result packet passing
92
// based command execution
93
// - batch execution handling
94
// fredt@users 20030628 - patch 1.7.2 - session proxy support
95
// fredt@users 20040509 - patch 1.7.2 - SQL conformance for CURRENT_TIMESTAMP and other datetime functions
96

97 /**
98  * Implementation of a user session with the database. In 1.7.2 Session
99  * becomes the public interface to an HSQLDB database, accessed locally or
100  * remotely via SessionInterface.
101  *
102  * When as Session is closed, all references to internal engine objects are
103  * set to null. But the session id and scripting mode may still be used for
104  * scripting
105  *
106  * New class based based on original Hypersonic code.
107  * Extensively rewritten and extended in successive versions of HSQLDB.
108  *
109  * @author Thomas Mueller (Hypersonic SQL Group)
110  * @version 1.8.0
111  * @since 1.7.0
112  */

113 public class Session implements SessionInterface {
114
115     //
116
private volatile boolean isAutoCommit;
117     private volatile boolean isReadOnly;
118     private volatile boolean isClosed;
119
120     //
121
Database database;
122     private User user;
123     HsqlArrayList rowActionList;
124     private boolean isNestedTransaction;
125     private int nestedOldTransIndex;
126     int isolationMode = SessionInterface.TX_READ_COMMITTED;
127     long actionTimestamp;
128     long transactionTimestamp;
129     private int currentMaxRows;
130     private int sessionMaxRows;
131     private Number JavaDoc lastIdentity = ValuePool.getInt(0);
132     private final int sessionId;
133     HashMappedList savepoints;
134     private boolean script;
135     private Tokenizer tokenizer;
136     private Parser parser;
137     static final Result emptyUpdateCount =
138         new Result(ResultConstants.UPDATECOUNT);
139
140     //
141
private jdbcConnection intConnection;
142
143     // schema
144
public HsqlName currentSchema;
145     public HsqlName loggedSchema;
146     private HsqlName oldSchema;
147
148     // query processing
149
boolean isProcessingScript;
150     boolean isProcessingLog;
151
152     // two types of temp tables
153
private IntKeyHashMap indexArrayMap;
154     private IntKeyHashMap indexArrayKeepMap;
155
156     /** @todo fredt - clarify in which circumstances Session has to disconnect */
157     Session getSession() {
158         return this;
159     }
160
161     /**
162      * Constructs a new Session object.
163      *
164      * @param db the database to which this represents a connection
165      * @param user the initial user
166      * @param autocommit the initial autocommit value
167      * @param readonly the initial readonly value
168      * @param id the session identifier, as known to the database
169      */

170     Session(Database db, User user, boolean autocommit, boolean readonly,
171             int id) {
172
173         sessionId = id;
174         database = db;
175         this.user = user;
176         rowActionList = new HsqlArrayList(true);
177         savepoints = new HashMappedList(4);
178         isAutoCommit = autocommit;
179         isReadOnly = readonly;
180         dbCommandInterpreter = new DatabaseCommandInterpreter(this);
181         compiledStatementExecutor = new CompiledStatementExecutor(this);
182         compiledStatementManager = db.compiledStatementManager;
183         tokenizer = new Tokenizer();
184         parser = new Parser(this, database, tokenizer);
185
186         resetSchema();
187     }
188
189     void resetSchema() {
190
191         HsqlName initialSchema = user.getInitialSchema();
192
193         currentSchema = ((initialSchema == null)
194                          ? database.schemaManager.getDefaultSchemaHsqlName()
195                          : initialSchema);
196     }
197
198     /**
199      * Retrieves the session identifier for this Session.
200      *
201      * @return the session identifier for this Session
202      */

203     public int getId() {
204         return sessionId;
205     }
206
207     /**
208      * Closes this Session.
209      */

210     public void close() {
211
212         if (isClosed) {
213             return;
214         }
215
216         synchronized (database) {
217
218             // test again inside block
219
if (isClosed) {
220                 return;
221             }
222
223             database.sessionManager.removeSession(this);
224             rollback();
225
226             try {
227                 database.logger.writeToLog(this, Token.T_DISCONNECT);
228             } catch (HsqlException e) {}
229
230             clearIndexRoots();
231             clearIndexRootsKeep();
232             compiledStatementManager.removeSession(sessionId);
233             database.closeIfLast();
234
235             database = null;
236             user = null;
237             rowActionList = null;
238             savepoints = null;
239             intConnection = null;
240             compiledStatementExecutor = null;
241             compiledStatementManager = null;
242             dbCommandInterpreter = null;
243             lastIdentity = null;
244             isClosed = true;
245         }
246     }
247
248     /**
249      * Retrieves whether this Session is closed.
250      *
251      * @return true if this Session is closed
252      */

253     public boolean isClosed() {
254         return isClosed;
255     }
256
257     public void setIsolation(int level) throws HsqlException {
258         isolationMode = level;
259     }
260
261     public int getIsolation() throws HsqlException {
262         return isolationMode;
263     }
264
265     /**
266      * Setter for iLastIdentity attribute.
267      *
268      * @param i the new value
269      */

270     void setLastIdentity(Number JavaDoc i) {
271         lastIdentity = i;
272     }
273
274     /**
275      * Getter for iLastIdentity attribute.
276      *
277      * @return the current value
278      */

279     Number JavaDoc getLastIdentity() {
280         return lastIdentity;
281     }
282
283     /**
284      * Retrieves the Database instance to which this
285      * Session represents a connection.
286      *
287      * @return the Database object to which this Session is connected
288      */

289     Database getDatabase() {
290         return database;
291     }
292
293     /**
294      * Retrieves the name, as known to the database, of the
295      * user currently controlling this Session.
296      *
297      * @return the name of the user currently connected within this Session
298      */

299     String JavaDoc getUsername() {
300         return user.getName();
301     }
302
303     /**
304      * Retrieves the User object representing the user currently controlling
305      * this Session.
306      *
307      * @return this Session's User object
308      */

309     public User getUser() {
310         return user;
311     }
312
313     /**
314      * Sets this Session's User object to the one specified by the
315      * user argument.
316      *
317      * @param user the new User object for this session
318      */

319     void setUser(User user) {
320         this.user = user;
321     }
322
323     int getMaxRows() {
324         return currentMaxRows;
325     }
326
327     int getSQLMaxRows() {
328         return sessionMaxRows;
329     }
330
331     /**
332      * The SQL command SET MAXROWS n will override the Statement.setMaxRows(n)
333      * until SET MAXROWS 0 is issued.
334      *
335      * NB this is dedicated to the SET MAXROWS sql statement and should not
336      * otherwise be called. (fredt@users)
337      */

338     void setSQLMaxRows(int rows) {
339         currentMaxRows = sessionMaxRows = rows;
340     }
341
342     /**
343      * Checks whether this Session's current User has the privileges of
344      * the ADMIN role.
345      *
346      * @throws HsqlException if this Session's User does not have the
347      * privileges of the ADMIN role.
348      */

349     void checkAdmin() throws HsqlException {
350         user.checkAdmin();
351     }
352
353     /**
354      * Checks whether this Session's current User has the set of rights
355      * specified by the right argument, in relation to the database
356      * object identifier specified by the object argument.
357      *
358      * @param object the database object to check
359      * @param right the rights to check for
360      * @throws HsqlException if the Session User does not have such rights
361      */

362     void check(HsqlName object, int right) throws HsqlException {
363         user.check(object, right);
364     }
365
366     /**
367      * Checks the rights on a function
368      *
369      * @param object the function
370      * @throws HsqlException if the Session User does not have such rights
371      */

372     void check(String JavaDoc object) throws HsqlException {
373         user.check(object);
374     }
375
376     /**
377      * This is used for reading - writing to existing tables.
378      * @throws HsqlException
379      */

380     void checkReadWrite() throws HsqlException {
381
382         if (isReadOnly) {
383             throw Trace.error(Trace.DATABASE_IS_READONLY);
384         }
385     }
386
387     /**
388      * This is used for creating new database objects such as tables.
389      * @throws HsqlException
390      */

391     void checkDDLWrite() throws HsqlException {
392
393         if (database.isFilesReadOnly() &&!user.isSys()) {
394             throw Trace.error(Trace.DATABASE_IS_READONLY);
395         }
396     }
397
398     /**
399      * Adds a single-row deletion step to the transaction UNDO buffer.
400      *
401      * @param table the table from which the row was deleted
402      * @param row the deleted row
403      * @throws HsqlException
404      */

405     boolean addDeleteAction(Table table, Row row) throws HsqlException {
406
407         if (!isAutoCommit || isNestedTransaction) {
408             Transaction t = new Transaction(true, table, row,
409                                             actionTimestamp);
410
411             rowActionList.add(t);
412             database.txManager.addTransaction(this, t);
413
414             return true;
415         } else {
416             table.removeRowFromStore(row);
417         }
418
419         return false;
420     }
421
422     /**
423      * Adds a single-row insertion step to the transaction UNDO buffer.
424      *
425      * @param table the table into which the row was inserted
426      * @param row the inserted row
427      * @throws HsqlException
428      */

429     boolean addInsertAction(Table table, Row row) throws HsqlException {
430
431         if (!isAutoCommit || isNestedTransaction) {
432             Transaction t = new Transaction(false, table, row,
433                                             actionTimestamp);
434
435             rowActionList.add(t);
436             database.txManager.addTransaction(this, t);
437
438             return true;
439         } else {
440             table.commitRowToStore(row);
441         }
442
443         return false;
444     }
445
446     /**
447      * Setter for the autocommit attribute.
448      *
449      * @param autocommit the new value
450      * @throws HsqlException
451      */

452     public void setAutoCommit(boolean autocommit) {
453
454         if (isClosed) {
455             return;
456         }
457
458         synchronized (database) {
459             if (autocommit != isAutoCommit) {
460                 commit();
461
462                 isAutoCommit = autocommit;
463
464                 try {
465                     database.logger.writeToLog(this,
466                                                getAutoCommitStatement());
467                 } catch (HsqlException e) {}
468             }
469         }
470     }
471
472     public void startPhasedTransaction() throws HsqlException {}
473
474     public void prepareCommit() throws HsqlException {}
475
476     /**
477      * Commits any uncommited transaction this Session may have open
478      *
479      * @throws HsqlException
480      */

481     public void commit() {
482
483         if (isClosed) {
484             return;
485         }
486
487         synchronized (database) {
488             if (!rowActionList.isEmpty()) {
489                 try {
490                     database.logger.writeCommitStatement(this);
491                 } catch (HsqlException e) {}
492             }
493
494             database.txManager.commit(this);
495             clearIndexRoots();
496         }
497     }
498
499     /**
500      * Rolls back any uncommited transaction this Session may have open.
501      *
502      * @throws HsqlException
503      */

504     public void rollback() {
505
506         if (isClosed) {
507             return;
508         }
509
510         synchronized (database) {
511             if (rowActionList.size() != 0) {
512                 try {
513                     database.logger.writeToLog(this, Token.T_ROLLBACK);
514                 } catch (HsqlException e) {}
515             }
516
517             database.txManager.rollback(this);
518             clearIndexRoots();
519         }
520     }
521
522     /**
523      * No-op in this implementation
524      */

525     public void resetSession() throws HsqlException {
526         throw new HsqlException("", "", 0);
527     }
528
529     /**
530      * Implements a transaction SAVEPOINT. A new SAVEPOINT with the
531      * name of an existing one replaces the old SAVEPOINT.
532      *
533      * @param name of the savepoint
534      * @throws HsqlException if there is no current transaction
535      */

536     void savepoint(String JavaDoc name) throws HsqlException {
537
538         savepoints.remove(name);
539         savepoints.add(name, ValuePool.getInt(rowActionList.size()));
540
541         try {
542             database.logger.writeToLog(this, Token.T_SAVEPOINT + " " + name);
543         } catch (HsqlException e) {}
544     }
545
546     /**
547      * Implements a partial transaction ROLLBACK.
548      *
549      * @param name Name of savepoint that was marked before by savepoint()
550      * call
551      * @throws HsqlException
552      */

553     void rollbackToSavepoint(String JavaDoc name) throws HsqlException {
554
555         if (isClosed) {
556             return;
557         }
558
559         try {
560             database.logger.writeToLog(this,
561                                        Token.T_ROLLBACK + " " + Token.T_TO
562                                        + " " + Token.T_SAVEPOINT + " "
563                                        + name);
564         } catch (HsqlException e) {}
565
566         database.txManager.rollbackSavepoint(this, name);
567     }
568
569     /**
570      * Implements release of named SAVEPOINT.
571      *
572      * @param name Name of savepoint that was marked before by savepoint()
573      * call
574      * @throws HsqlException if name does not correspond to a savepoint
575      */

576     void releaseSavepoint(String JavaDoc name) throws HsqlException {
577
578         // remove this and all later savepoints
579
int index = savepoints.getIndex(name);
580
581         Trace.check(index >= 0, Trace.SAVEPOINT_NOT_FOUND, name);
582
583         while (savepoints.size() > index) {
584             savepoints.remove(savepoints.size() - 1);
585         }
586     }
587
588     /**
589      * Starts a nested transaction.
590      *
591      * @throws HsqlException
592      */

593     void beginNestedTransaction() throws HsqlException {
594
595         if (isNestedTransaction) {
596             Trace.doAssert(false, "beginNestedTransaction");
597         }
598
599         nestedOldTransIndex = rowActionList.size();
600         isNestedTransaction = true;
601
602         if (isAutoCommit) {
603             try {
604                 database.logger.writeToLog(this, "SET AUTOCOMMIT FALSE");
605             } catch (HsqlException e) {}
606         }
607     }
608
609     /**
610      * @todo -- fredt 20050604 - if this method is called after an out of memory
611      * error during update, the next block might throw out of memory too and as
612      * a result inNestedTransaction remains true and no further update
613      * is possible. The session must be closed at that point by the user
614      * application.
615      */

616
617     /**
618      * Ends a nested transaction.
619      *
620      * @param rollback true to roll back or false to commit the nested transaction
621      * @throws HsqlException
622      */

623     void endNestedTransaction(boolean rollback) throws HsqlException {
624
625         if (!isNestedTransaction) {
626             Trace.doAssert(false, "endNestedTransaction");
627         }
628
629         if (rollback) {
630             database.txManager.rollbackTransactions(this,
631                     nestedOldTransIndex, true);
632         }
633
634         // reset after the rollback
635
isNestedTransaction = false;
636
637         if (isAutoCommit) {
638             database.txManager.commit(this);
639
640             try {
641                 database.logger.writeToLog(this, "SET AUTOCOMMIT TRUE");
642             } catch (HsqlException e) {}
643         }
644     }
645
646     /**
647      * Setter for readonly attribute.
648      *
649      * @param readonly the new value
650      */

651     public void setReadOnly(boolean readonly) throws HsqlException {
652
653         if (!readonly && database.databaseReadOnly) {
654             throw Trace.error(Trace.DATABASE_IS_READONLY);
655         }
656
657         isReadOnly = readonly;
658     }
659
660     /**
661      * Getter for readonly attribute.
662      *
663      * @return the current value
664      */

665     public boolean isReadOnly() {
666         return isReadOnly;
667     }
668
669     /**
670      * Getter for nestedTransaction attribute.
671      *
672      * @return the current value
673      */

674     boolean isNestedTransaction() {
675         return isNestedTransaction;
676     }
677
678     /**
679      * Getter for autoCommit attribute.
680      *
681      * @return the current value
682      */

683     public boolean isAutoCommit() {
684         return isAutoCommit;
685     }
686
687     /**
688      * A switch to set scripting on the basis of type of statement executed.
689      * A method in DatabaseCommandInterpreter.java sets this value to false
690      * before other methods are called to act on an SQL statement, which may
691      * set this to true. Afterwards the method reponsible for logging uses
692      * getScripting() to determine if logging is required for the executed
693      * statement. (fredt@users)
694      *
695      * @param script The new scripting value
696      */

697     void setScripting(boolean script) {
698         this.script = script;
699     }
700
701     /**
702      * Getter for scripting attribute.
703      *
704      * @return scripting for the last statement.
705      */

706     boolean getScripting() {
707         return script;
708     }
709
710     public String JavaDoc getAutoCommitStatement() {
711         return isAutoCommit ? "SET AUTOCOMMIT TRUE"
712                             : "SET AUTOCOMMIT FALSE";
713     }
714
715     /**
716      * Retrieves an internal Connection object equivalent to the one
717      * that created this Session.
718      *
719      * @return internal connection.
720      */

721     jdbcConnection getInternalConnection() throws HsqlException {
722
723         if (intConnection == null) {
724             intConnection = new jdbcConnection(this);
725         }
726
727         return intConnection;
728     }
729
730 // boucherb@users 20020810 metadata 1.7.2
731
//----------------------------------------------------------------
732
private final long connectTime = System.currentTimeMillis();
733
734 // more effecient for MetaData concerns than checkAdmin
735

736     /**
737      * Getter for admin attribute.
738      *
739      * @ return the current value
740      */

741     boolean isAdmin() {
742         return user.isAdmin();
743     }
744
745     /**
746      * Getter for connectTime attribute.
747      *
748      * @return the value
749      */

750     long getConnectTime() {
751         return connectTime;
752     }
753
754     /**
755      * Getter for transactionSise attribute.
756      *
757      * @return the current value
758      */

759     int getTransactionSize() {
760         return rowActionList.size();
761     }
762
763     /**
764      * Retrieves whether the database object identifier by the dbobject
765      * argument is accessible by the current Session User.
766      *
767      * @return true if so, else false
768      */

769     boolean isAccessible(String JavaDoc dbobject) throws HsqlException {
770         return user.isAccessible(dbobject);
771     }
772
773     boolean isAccessible(HsqlName dbobject) throws HsqlException {
774         return user.isAccessible(dbobject);
775     }
776
777 // boucherb@users 20030417 - patch 1.7.2 - compiled statement support
778
//-------------------------------------------------------------------
779
DatabaseCommandInterpreter dbCommandInterpreter;
780     CompiledStatementExecutor compiledStatementExecutor;
781     CompiledStatementManager compiledStatementManager;
782
783     CompiledStatement sqlCompileStatement(String JavaDoc sql) throws HsqlException {
784
785         parser.reset(sql);
786
787         CompiledStatement cs;
788         int brackets = 0;
789         String JavaDoc token = tokenizer.getString();
790         int cmd = Token.get(token);
791
792         switch (cmd) {
793
794             case Token.OPENBRACKET : {
795                 brackets = parser.parseOpenBracketsSelect() + 1;
796             }
797             case Token.SELECT : {
798                 cs = parser.compileSelectStatement(brackets);
799
800                 break;
801             }
802             case Token.INSERT : {
803                 cs = parser.compileInsertStatement();
804
805                 break;
806             }
807             case Token.UPDATE : {
808                 cs = parser.compileUpdateStatement();
809
810                 break;
811             }
812             case Token.DELETE : {
813                 cs = parser.compileDeleteStatement();
814
815                 break;
816             }
817             case Token.CALL : {
818                 cs = parser.compileCallStatement();
819
820                 break;
821             }
822             default : {
823
824                 // DDL statements
825
cs = new CompiledStatement(currentSchema);
826
827                 break;
828             }
829         }
830
831         // In addition to requiring that the compilation was successful,
832
// we also require that the submitted sql represents a _single_
833
// valid DML or DDL statement. We do not check the DDL yet.
834
// fredt - now accepts semicolon and whitespace at the end of statement
835
// fredt - investigate if it should or not for prepared statements
836
if (cs.type != CompiledStatement.DDL) {
837             while (tokenizer.getPosition() < tokenizer.getLength()) {
838                 token = tokenizer.getString();
839
840                 if (token.length() != 0 &&!token.equals(Token.T_SEMICOLON)) {
841                     throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
842                 }
843             }
844         }
845
846         // - need to be able to key cs against its sql in statement pool
847
// - also need to be able to revalidate its sql occasionally
848
cs.sql = sql;
849
850         return cs;
851     }
852
853     /**
854      * Executes the command encapsulated by the cmd argument.
855      *
856      * @param cmd the command to execute
857      * @return the result of executing the command
858      */

859     public Result execute(Result cmd) {
860
861         try {
862             if (isClosed) {
863                 Trace.check(false, Trace.ACCESS_IS_DENIED,
864                             Trace.getMessage(Trace.Session_execute));
865             }
866         } catch (Throwable JavaDoc t) {
867             return new Result(t, null);
868         }
869
870         synchronized (database) {
871             int type = cmd.mode;
872
873             if (sessionMaxRows == 0) {
874                 currentMaxRows = cmd.updateCount;
875             }
876
877             // we simply get the next system change number - no matter what type of query
878
actionTimestamp = database.txManager.nextActionTimestamp();
879
880             JavaSystem.gc();
881
882             switch (type) {
883
884                 case ResultConstants.SQLEXECUTE : {
885                     Result resultout = sqlExecute(cmd);
886
887                     resultout = performPostExecute(resultout);
888
889                     return resultout;
890                 }
891                 case ResultConstants.BATCHEXECUTE : {
892                     Result resultout = sqlExecuteBatch(cmd);
893
894                     resultout = performPostExecute(resultout);
895
896                     return resultout;
897                 }
898                 case ResultConstants.SQLEXECDIRECT : {
899                     Result resultout =
900                         sqlExecuteDirectNoPreChecks(cmd.getMainString());
901
902                     resultout = performPostExecute(resultout);
903
904                     return resultout;
905                 }
906                 case ResultConstants.BATCHEXECDIRECT : {
907                     Result resultout = sqlExecuteBatchDirect(cmd);
908
909                     resultout = performPostExecute(resultout);
910
911                     return resultout;
912                 }
913                 case ResultConstants.SQLPREPARE : {
914                     CompiledStatement cs;
915
916                     try {
917                         cs = compiledStatementManager.compile(
918                             this, cmd.getMainString());
919                     } catch (Throwable JavaDoc t) {
920                         return new Result(t, cmd.getMainString());
921                     }
922
923                     Result rmd = cs.describeResult();
924                     Result pmd = cs.describeParameters();
925
926                     return Result.newPrepareResponse(cs.id, rmd, pmd);
927                 }
928                 case ResultConstants.SQLFREESTMT : {
929                     compiledStatementManager.freeStatement(
930                         cmd.getStatementID(), sessionId, false);
931
932                     return emptyUpdateCount;
933                 }
934                 case ResultConstants.GETSESSIONATTR : {
935                     return getAttributes();
936                 }
937                 case ResultConstants.SETSESSIONATTR : {
938                     return setAttributes(cmd);
939                 }
940                 case ResultConstants.SQLENDTRAN : {
941                     switch (cmd.getEndTranType()) {
942
943                         case ResultConstants.COMMIT :
944                             commit();
945                             break;
946
947                         case ResultConstants.ROLLBACK :
948                             rollback();
949                             break;
950
951                         case ResultConstants.SAVEPOINT_NAME_RELEASE :
952                             try {
953                                 String JavaDoc name = cmd.getMainString();
954
955                                 releaseSavepoint(name);
956                             } catch (Throwable JavaDoc t) {
957                                 return new Result(t, null);
958                             }
959                             break;
960
961                         case ResultConstants.SAVEPOINT_NAME_ROLLBACK :
962                             try {
963                                 rollbackToSavepoint(cmd.getMainString());
964                             } catch (Throwable JavaDoc t) {
965                                 return new Result(t, null);
966                             }
967                             break;
968
969                         // not yet
970
// case ResultConstants.COMMIT_AND_CHAIN :
971
// case ResultConstants.ROLLBACK_AND_CHAIN :
972
}
973
974                     return emptyUpdateCount;
975                 }
976                 case ResultConstants.SQLSETCONNECTATTR : {
977                     switch (cmd.getConnectionAttrType()) {
978
979                         case ResultConstants.SQL_ATTR_SAVEPOINT_NAME :
980                             try {
981                                 savepoint(cmd.getMainString());
982                             } catch (Throwable JavaDoc t) {
983                                 return new Result(t, null);
984                             }
985
986                         // case ResultConstants.SQL_ATTR_AUTO_IPD
987
// - always true
988
// default: throw - case never happens
989
}
990
991                     return emptyUpdateCount;
992                 }
993                 case ResultConstants.SQLDISCONNECT : {
994                     close();
995
996                     return emptyUpdateCount;
997                 }
998                 default : {
999                     return new Result(
1000                        Trace.runtimeError(
1001                            Trace.UNSUPPORTED_INTERNAL_OPERATION,
1002                            "Session.execute()"), null);
1003                }
1004            }
1005        }
1006    }
1007
1008    private Result performPostExecute(Result r) {
1009
1010        try {
1011            if (database != null) {
1012                database.schemaManager.logSequences(this, database.logger);
1013
1014                if (isAutoCommit) {
1015                    clearIndexRoots();
1016                    database.logger.synchLog();
1017                }
1018            }
1019
1020            return r;
1021        } catch (Exception JavaDoc e) {
1022            return new Result(e, null);
1023        } finally {
1024            if (database != null && database.logger.needsCheckpoint()) {
1025                try {
1026                    database.logger.checkpoint(false);
1027                } catch (HsqlException e) {
1028                    database.logger.appLog.logContext(
1029                        SimpleLog.LOG_ERROR, "checkpoint did not complete");
1030                }
1031            }
1032        }
1033    }
1034
1035    public Result sqlExecuteDirectNoPreChecks(String JavaDoc sql) {
1036
1037        synchronized (database) {
1038            return dbCommandInterpreter.execute(sql);
1039        }
1040    }
1041
1042    Result sqlExecuteCompiledNoPreChecks(CompiledStatement cs,
1043                                         Object JavaDoc[] pvals) {
1044        return compiledStatementExecutor.execute(cs, pvals);
1045    }
1046
1047    private Result sqlExecuteBatch(Result cmd) {
1048
1049        int csid;
1050        Record record;
1051        Result out;
1052        CompiledStatement cs;
1053        Expression[] parameters;
1054        int[] updateCounts;
1055        int count;
1056
1057        csid = cmd.getStatementID();
1058        cs = database.compiledStatementManager.getStatement(this, csid);
1059
1060        if (cs == null) {
1061
1062            // invalid sql has been removed already
1063
return new Result(
1064                Trace.runtimeError(Trace.INVALID_PREPARED_STATEMENT, null),
1065                null);
1066        }
1067
1068        parameters = cs.parameters;
1069        count = 0;
1070        updateCounts = new int[cmd.getSize()];
1071        record = cmd.rRoot;
1072
1073        while (record != null) {
1074            Result in;
1075            Object JavaDoc[] pvals = record.data;
1076
1077            in = sqlExecuteCompiledNoPreChecks(cs, pvals);
1078
1079            // On the client side, iterate over the vals and throw
1080
// a BatchUpdateException if a batch status value of
1081
// esultConstants.EXECUTE_FAILED is encountered in the result
1082
if (in.mode == ResultConstants.UPDATECOUNT) {
1083                updateCounts[count++] = in.updateCount;
1084            } else if (in.isData()) {
1085
1086                // FIXME: we don't have what it takes yet
1087
// to differentiate between things like
1088
// stored procedure calls to methods with
1089
// void return type and select statements with
1090
// a single row/column containg null
1091
updateCounts[count++] = ResultConstants.SUCCESS_NO_INFO;
1092            } else {
1093                updateCounts = ArrayUtil.arraySlice(updateCounts, 0, count);
1094
1095                break;
1096            }
1097
1098            record = record.next;
1099        }
1100
1101        out = new Result(ResultConstants.SQLEXECUTE, updateCounts, 0);
1102
1103        return out;
1104    }
1105
1106    private Result sqlExecuteBatchDirect(Result cmd) {
1107
1108        Record record;
1109        Result out;
1110        int[] updateCounts;
1111        int count;
1112
1113        count = 0;
1114        updateCounts = new int[cmd.getSize()];
1115        record = cmd.rRoot;
1116
1117        while (record != null) {
1118            Result in;
1119            String JavaDoc sql = (String JavaDoc) record.data[0];
1120
1121            try {
1122                in = dbCommandInterpreter.execute(sql);
1123            } catch (Throwable JavaDoc t) {
1124                in = new Result(ResultConstants.ERROR);
1125
1126                // if (t instanceof OutOfMemoryError) {
1127
// System.gc();
1128
// }
1129
// "in" alread equals "err"
1130
// maybe test for OOME and do a gc() ?
1131
// t.printStackTrace();
1132
}
1133
1134            // On the client side, iterate over the colType vals and throw
1135
// a BatchUpdateException if a batch status value of
1136
// ResultConstants.EXECUTE_FAILED is encountered
1137
if (in.mode == ResultConstants.UPDATECOUNT) {
1138                updateCounts[count++] = in.updateCount;
1139            } else if (in.isData()) {
1140
1141                // FIXME: we don't have what it takes yet
1142
// to differentiate between things like
1143
// stored procedure calls to methods with
1144
// void return type and select statements with
1145
// a single row/column containg null
1146
updateCounts[count++] = ResultConstants.SUCCESS_NO_INFO;
1147            } else {
1148                updateCounts = ArrayUtil.arraySlice(updateCounts, 0, count);
1149
1150                break;
1151            }
1152
1153            record = record.next;
1154        }
1155
1156        out = new Result(ResultConstants.SQLEXECUTE, updateCounts, 0);
1157
1158        return out;
1159    }
1160
1161    /**
1162     * Retrieves the result of executing the prepared statement whose csid
1163     * and parameter values/types are encapsulated by the cmd argument.
1164     *
1165     * @return the result of executing the statement
1166     */

1167    private Result sqlExecute(Result cmd) {
1168
1169        int csid = cmd.getStatementID();
1170        CompiledStatement cs = compiledStatementManager.getStatement(this,
1171            csid);
1172
1173        if (cs == null) {
1174
1175            // invalid sql has been removed already
1176
return new Result(
1177                Trace.runtimeError(Trace.INVALID_PREPARED_STATEMENT, null),
1178                null);
1179        }
1180
1181        Object JavaDoc[] pvals = cmd.getParameterData();
1182
1183        return sqlExecute(cs, pvals);
1184    }
1185
1186    private Result sqlExecute(CompiledStatement cs, Object JavaDoc[] pvals) {
1187        return sqlExecuteCompiledNoPreChecks(cs, pvals);
1188    }
1189
1190// session DATETIME functions
1191
long currentDateTimeSCN;
1192    long currentMillis;
1193    Date JavaDoc currentDate;
1194    Time JavaDoc currentTime;
1195    Timestamp JavaDoc currentTimestamp;
1196
1197    /**
1198     * Returns the current date, unchanged for the duration of the current
1199     * execution unit (statement).<p>
1200     *
1201     * SQL standards require that CURRENT_DATE, CURRENT_TIME and
1202     * CURRENT_TIMESTAMP are all evaluated at the same point of
1203     * time in the duration of each SQL statement, no matter how long the
1204     * SQL statement takes to complete.<p>
1205     *
1206     * When this method or a corresponding method for CURRENT_TIME or
1207     * CURRENT_TIMESTAMP is first called in the scope of a system change
1208     * number, currentMillis is set to the current system time. All further
1209     * CURRENT_XXXX calls in this scope will use this millisecond value.
1210     * (fredt@users)
1211     */

1212    Date JavaDoc getCurrentDate() {
1213
1214        if (currentDateTimeSCN != actionTimestamp) {
1215            currentDateTimeSCN = actionTimestamp;
1216            currentMillis = System.currentTimeMillis();
1217            currentDate = HsqlDateTime.getCurrentDate(currentMillis);
1218            currentTime = null;
1219            currentTimestamp = null;
1220        } else if (currentDate == null) {
1221            currentDate = HsqlDateTime.getCurrentDate(currentMillis);
1222        }
1223
1224        return currentDate;
1225    }
1226
1227    /**
1228     * Returns the current time, unchanged for the duration of the current
1229     * execution unit (statement)
1230     */

1231    Time JavaDoc getCurrentTime() {
1232
1233        if (currentDateTimeSCN != actionTimestamp) {
1234            currentDateTimeSCN = actionTimestamp;
1235            currentMillis = System.currentTimeMillis();
1236            currentDate = null;
1237            currentTime =
1238                new Time JavaDoc(HsqlDateTime.getNormalisedTime(currentMillis));
1239            currentTimestamp = null;
1240        } else if (currentTime == null) {
1241            currentTime =
1242                new Time JavaDoc(HsqlDateTime.getNormalisedTime(currentMillis));
1243        }
1244
1245        return currentTime;
1246    }
1247
1248    /**
1249     * Returns the current timestamp, unchanged for the duration of the current
1250     * execution unit (statement)
1251     */

1252    Timestamp JavaDoc getCurrentTimestamp() {
1253
1254        if (currentDateTimeSCN != actionTimestamp) {
1255            currentDateTimeSCN = actionTimestamp;
1256            currentMillis = System.currentTimeMillis();
1257            currentDate = null;
1258            currentTime = null;
1259            currentTimestamp = HsqlDateTime.getTimestamp(currentMillis);
1260        } else if (currentTimestamp == null) {
1261            currentTimestamp = HsqlDateTime.getTimestamp(currentMillis);
1262        }
1263
1264        return currentTimestamp;
1265    }
1266
1267    Result getAttributes() {
1268
1269        Result r = Result.newSessionAttributesResult();
1270        Object JavaDoc[] row = new Object JavaDoc[] {
1271            database.getURI(), getUsername(), ValuePool.getInt(sessionId),
1272            ValuePool.getInt(isolationMode),
1273            ValuePool.getBoolean(isAutoCommit),
1274            ValuePool.getBoolean(database.databaseReadOnly),
1275            ValuePool.getBoolean(isReadOnly)
1276        };
1277
1278        r.add(row);
1279
1280        return r;
1281    }
1282
1283    Result setAttributes(Result r) {
1284
1285        Object JavaDoc[] row = r.rRoot.data;
1286
1287        for (int i = 0; i < row.length; i++) {
1288            Object JavaDoc value = row[i];
1289
1290            if (value == null) {
1291                continue;
1292            }
1293
1294            try {
1295                switch (i) {
1296
1297                    case SessionInterface.INFO_AUTOCOMMIT : {
1298                        this.setAutoCommit(((Boolean JavaDoc) value).booleanValue());
1299
1300                        break;
1301                    }
1302                    case SessionInterface.INFO_CONNECTION_READONLY :
1303                        this.setReadOnly(((Boolean JavaDoc) value).booleanValue());
1304                        break;
1305                }
1306            } catch (HsqlException e) {
1307                return new Result(e, null);
1308            }
1309        }
1310
1311        return emptyUpdateCount;
1312    }
1313
1314    // DatabaseMetaData.getURL should work as specified for
1315
// internal connections too.
1316
public String JavaDoc getInternalConnectionURL() {
1317        return DatabaseURL.S_URL_PREFIX + database.getURI();
1318    }
1319
1320    boolean isProcessingScript() {
1321        return isProcessingScript;
1322    }
1323
1324    boolean isProcessingLog() {
1325        return isProcessingLog;
1326    }
1327
1328    boolean isSchemaDefintion() {
1329        return oldSchema != null;
1330    }
1331
1332    void startSchemaDefinition(String JavaDoc schema) throws HsqlException {
1333
1334        if (isProcessingScript) {
1335            setSchema(schema);
1336
1337            return;
1338        }
1339
1340        oldSchema = currentSchema;
1341
1342        setSchema(schema);
1343    }
1344
1345    void endSchemaDefinition() throws HsqlException {
1346
1347        if (oldSchema == null) {
1348            return;
1349        }
1350
1351        currentSchema = oldSchema;
1352        oldSchema = null;
1353
1354        database.logger.writeToLog(this,
1355                                   "SET SCHEMA "
1356                                   + currentSchema.statementName);
1357    }
1358
1359    // schema object methods
1360
public void setSchema(String JavaDoc schema) throws HsqlException {
1361        currentSchema = database.schemaManager.getSchemaHsqlName(schema);
1362    }
1363
1364    /**
1365     * If schemaName is null, return the current schema name, else return
1366     * the HsqlName object for the schema. If schemaName does not exist,
1367     * throw.
1368     */

1369    HsqlName getSchemaHsqlName(String JavaDoc name) throws HsqlException {
1370        return name == null ? currentSchema
1371                            : database.schemaManager.getSchemaHsqlName(name);
1372    }
1373
1374    /**
1375     * Same as above, but return string
1376     */

1377    public String JavaDoc getSchemaName(String JavaDoc name) throws HsqlException {
1378        return name == null ? currentSchema.name
1379                            : database.schemaManager.getSchemaName(name);
1380    }
1381
1382    /**
1383     * If schemaName is null, return the current schema name, else return
1384     * the HsqlName object for the schema. If schemaName does not exist, or
1385     * schema readonly, throw.
1386     */

1387    HsqlName getSchemaHsqlNameForWrite(String JavaDoc name) throws HsqlException {
1388
1389        HsqlName schema = getSchemaHsqlName(name);
1390
1391        if (database.schemaManager.isSystemSchema(schema)) {
1392            throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
1393        }
1394
1395        return schema;
1396    }
1397
1398    /**
1399     * Same as above, but return string
1400     */

1401    public String JavaDoc getSchemaNameForWrite(String JavaDoc name) throws HsqlException {
1402
1403        HsqlName schema = getSchemaHsqlNameForWrite(name);
1404
1405        return schema.name;
1406    }
1407
1408    /**
1409     * get the root for a temp table index
1410     */

1411    Node getIndexRoot(HsqlName index, boolean preserve) {
1412
1413        if (preserve) {
1414            if (indexArrayKeepMap == null) {
1415                return null;
1416            }
1417
1418            return (Node) indexArrayKeepMap.get(index.hashCode());
1419        } else {
1420            if (indexArrayMap == null) {
1421                return null;
1422            }
1423
1424            return (Node) indexArrayMap.get(index.hashCode());
1425        }
1426    }
1427
1428    /**
1429     * set the root for a temp table index
1430     */

1431    void setIndexRoot(HsqlName index, boolean preserve, Node root) {
1432
1433        if (preserve) {
1434            if (indexArrayKeepMap == null) {
1435                if (root == null) {
1436                    return;
1437                }
1438
1439                indexArrayKeepMap = new IntKeyHashMap();
1440            }
1441
1442            indexArrayKeepMap.put(index.hashCode(), root);
1443        } else {
1444            if (indexArrayMap == null) {
1445                if (root == null) {
1446                    return;
1447                }
1448
1449                indexArrayMap = new IntKeyHashMap();
1450            }
1451
1452            indexArrayMap.put(index.hashCode(), root);
1453        }
1454    }
1455
1456    void dropIndex(HsqlName index, boolean preserve) {
1457
1458        if (preserve) {
1459            if (indexArrayKeepMap != null) {
1460                indexArrayKeepMap.remove(index.hashCode());
1461            }
1462        } else {
1463            if (indexArrayMap != null) {
1464                indexArrayMap.remove(index.hashCode());
1465            }
1466        }
1467    }
1468
1469    /**
1470     * clear default temp table contents for this session
1471     */

1472    void clearIndexRoots() {
1473
1474        if (indexArrayMap != null) {
1475            indexArrayMap.clear();
1476        }
1477    }
1478
1479    /**
1480     * clear ON COMMIT PRESERVE temp table contents for this session
1481     */

1482    void clearIndexRootsKeep() {
1483
1484        if (indexArrayKeepMap != null) {
1485            indexArrayKeepMap.clear();
1486        }
1487    }
1488}
1489
Popular Tags