0001: /* Copyright (c) 1995-2000, The Hypersonic SQL Group.
0002: * All rights reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * Redistributions of source code must retain the above copyright notice, this
0008: * list of conditions and the following disclaimer.
0009: *
0010: * Redistributions in binary form must reproduce the above copyright notice,
0011: * this list of conditions and the following disclaimer in the documentation
0012: * and/or other materials provided with the distribution.
0013: *
0014: * Neither the name of the Hypersonic SQL Group nor the names of its
0015: * contributors may be used to endorse or promote products derived from this
0016: * software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0021: * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
0022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029: *
0030: * This software consists of voluntary contributions made by many individuals
0031: * on behalf of the Hypersonic SQL Group.
0032: *
0033: *
0034: * For work added by the HSQL Development Group:
0035: *
0036: * Copyright (c) 2001-2005, The HSQL Development Group
0037: * All rights reserved.
0038: *
0039: * Redistribution and use in source and binary forms, with or without
0040: * modification, are permitted provided that the following conditions are met:
0041: *
0042: * Redistributions of source code must retain the above copyright notice, this
0043: * list of conditions and the following disclaimer.
0044: *
0045: * Redistributions in binary form must reproduce the above copyright notice,
0046: * this list of conditions and the following disclaimer in the documentation
0047: * and/or other materials provided with the distribution.
0048: *
0049: * Neither the name of the HSQL Development Group nor the names of its
0050: * contributors may be used to endorse or promote products derived from this
0051: * software without specific prior written permission.
0052: *
0053: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0054: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0055: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0056: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
0057: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0058: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0059: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0060: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0061: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0062: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0063: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0064: */
0065:
0066: package org.hsqldb;
0067:
0068: import java.sql.Date;
0069: import java.sql.Time;
0070: import java.sql.Timestamp;
0071:
0072: import org.hsqldb.HsqlNameManager.HsqlName;
0073: import org.hsqldb.jdbc.jdbcConnection;
0074: import org.hsqldb.lib.ArrayUtil;
0075: import org.hsqldb.lib.HashMappedList;
0076: import org.hsqldb.lib.HsqlArrayList;
0077: import org.hsqldb.lib.IntKeyHashMap;
0078: import org.hsqldb.lib.SimpleLog;
0079: import org.hsqldb.lib.java.JavaSystem;
0080: import org.hsqldb.store.ValuePool;
0081:
0082: // fredt@users 20020320 - doc 1.7.0 - update
0083: // fredt@users 20020315 - patch 1.7.0 - switch for scripting
0084: // fredt@users 20020130 - patch 476694 by velichko - transaction savepoints
0085: // additions in different parts to support savepoint transactions
0086: // fredt@users 20020910 - patch 1.7.1 by fredt - database readonly enforcement
0087: // fredt@users 20020912 - patch 1.7.1 by fredt - permanent internal connection
0088: // boucherb@users 20030512 - patch 1.7.2 - compiled statements
0089: // - session becomes execution hub
0090: // boucherb@users 20050510 - patch 1.7.2 - generalized Result packet passing
0091: // based command execution
0092: // - batch execution handling
0093: // fredt@users 20030628 - patch 1.7.2 - session proxy support
0094: // fredt@users 20040509 - patch 1.7.2 - SQL conformance for CURRENT_TIMESTAMP and other datetime functions
0095:
0096: /**
0097: * Implementation of a user session with the database. In 1.7.2 Session
0098: * becomes the public interface to an HSQLDB database, accessed locally or
0099: * remotely via SessionInterface.
0100: *
0101: * When as Session is closed, all references to internal engine objects are
0102: * set to null. But the session id and scripting mode may still be used for
0103: * scripting
0104: *
0105: * New class based based on original Hypersonic code.
0106: * Extensively rewritten and extended in successive versions of HSQLDB.
0107: *
0108: * @author Thomas Mueller (Hypersonic SQL Group)
0109: * @version 1.8.0
0110: * @since 1.7.0
0111: */
0112: public class Session implements SessionInterface {
0113:
0114: //
0115: private volatile boolean isAutoCommit;
0116: private volatile boolean isReadOnly;
0117: private volatile boolean isClosed;
0118:
0119: //
0120: Database database;
0121: private User user;
0122: HsqlArrayList rowActionList;
0123: private boolean isNestedTransaction;
0124: private int nestedOldTransIndex;
0125: int isolationMode = SessionInterface.TX_READ_COMMITTED;
0126: long actionTimestamp;
0127: long transactionTimestamp;
0128: private int currentMaxRows;
0129: private int sessionMaxRows;
0130: private Number lastIdentity = ValuePool.getInt(0);
0131: private final int sessionId;
0132: HashMappedList savepoints;
0133: private boolean script;
0134: private Tokenizer tokenizer;
0135: private Parser parser;
0136: static final Result emptyUpdateCount = new Result(
0137: ResultConstants.UPDATECOUNT);
0138:
0139: //
0140: private jdbcConnection intConnection;
0141:
0142: // schema
0143: public HsqlName currentSchema;
0144: public HsqlName loggedSchema;
0145: private HsqlName oldSchema;
0146:
0147: // query processing
0148: boolean isProcessingScript;
0149: boolean isProcessingLog;
0150:
0151: // two types of temp tables
0152: private IntKeyHashMap indexArrayMap;
0153: private IntKeyHashMap indexArrayKeepMap;
0154:
0155: /** @todo fredt - clarify in which circumstances Session has to disconnect */
0156: Session getSession() {
0157: return this ;
0158: }
0159:
0160: /**
0161: * Constructs a new Session object.
0162: *
0163: * @param db the database to which this represents a connection
0164: * @param user the initial user
0165: * @param autocommit the initial autocommit value
0166: * @param readonly the initial readonly value
0167: * @param id the session identifier, as known to the database
0168: */
0169: Session(Database db, User user, boolean autocommit,
0170: boolean readonly, int id) {
0171:
0172: sessionId = id;
0173: database = db;
0174: this .user = user;
0175: rowActionList = new HsqlArrayList(true);
0176: savepoints = new HashMappedList(4);
0177: isAutoCommit = autocommit;
0178: isReadOnly = readonly;
0179: dbCommandInterpreter = new DatabaseCommandInterpreter(this );
0180: compiledStatementExecutor = new CompiledStatementExecutor(this );
0181: compiledStatementManager = db.compiledStatementManager;
0182: tokenizer = new Tokenizer();
0183: parser = new Parser(this , database, tokenizer);
0184:
0185: resetSchema();
0186: }
0187:
0188: void resetSchema() {
0189:
0190: HsqlName initialSchema = user.getInitialSchema();
0191:
0192: currentSchema = ((initialSchema == null) ? database.schemaManager
0193: .getDefaultSchemaHsqlName()
0194: : initialSchema);
0195: }
0196:
0197: /**
0198: * Retrieves the session identifier for this Session.
0199: *
0200: * @return the session identifier for this Session
0201: */
0202: public int getId() {
0203: return sessionId;
0204: }
0205:
0206: /**
0207: * Closes this Session.
0208: */
0209: public void close() {
0210:
0211: if (isClosed) {
0212: return;
0213: }
0214:
0215: synchronized (database) {
0216:
0217: // test again inside block
0218: if (isClosed) {
0219: return;
0220: }
0221:
0222: database.sessionManager.removeSession(this );
0223: rollback();
0224:
0225: try {
0226: database.logger.writeToLog(this , Token.T_DISCONNECT);
0227: } catch (HsqlException e) {
0228: }
0229:
0230: clearIndexRoots();
0231: clearIndexRootsKeep();
0232: compiledStatementManager.removeSession(sessionId);
0233: database.closeIfLast();
0234:
0235: database = null;
0236: user = null;
0237: rowActionList = null;
0238: savepoints = null;
0239: intConnection = null;
0240: compiledStatementExecutor = null;
0241: compiledStatementManager = null;
0242: dbCommandInterpreter = null;
0243: lastIdentity = null;
0244: isClosed = true;
0245: }
0246: }
0247:
0248: /**
0249: * Retrieves whether this Session is closed.
0250: *
0251: * @return true if this Session is closed
0252: */
0253: public boolean isClosed() {
0254: return isClosed;
0255: }
0256:
0257: public void setIsolation(int level) throws HsqlException {
0258: isolationMode = level;
0259: }
0260:
0261: public int getIsolation() throws HsqlException {
0262: return isolationMode;
0263: }
0264:
0265: /**
0266: * Setter for iLastIdentity attribute.
0267: *
0268: * @param i the new value
0269: */
0270: void setLastIdentity(Number i) {
0271: lastIdentity = i;
0272: }
0273:
0274: /**
0275: * Getter for iLastIdentity attribute.
0276: *
0277: * @return the current value
0278: */
0279: Number getLastIdentity() {
0280: return lastIdentity;
0281: }
0282:
0283: /**
0284: * Retrieves the Database instance to which this
0285: * Session represents a connection.
0286: *
0287: * @return the Database object to which this Session is connected
0288: */
0289: Database getDatabase() {
0290: return database;
0291: }
0292:
0293: /**
0294: * Retrieves the name, as known to the database, of the
0295: * user currently controlling this Session.
0296: *
0297: * @return the name of the user currently connected within this Session
0298: */
0299: String getUsername() {
0300: return user.getName();
0301: }
0302:
0303: /**
0304: * Retrieves the User object representing the user currently controlling
0305: * this Session.
0306: *
0307: * @return this Session's User object
0308: */
0309: public User getUser() {
0310: return user;
0311: }
0312:
0313: /**
0314: * Sets this Session's User object to the one specified by the
0315: * user argument.
0316: *
0317: * @param user the new User object for this session
0318: */
0319: void setUser(User user) {
0320: this .user = user;
0321: }
0322:
0323: int getMaxRows() {
0324: return currentMaxRows;
0325: }
0326:
0327: int getSQLMaxRows() {
0328: return sessionMaxRows;
0329: }
0330:
0331: /**
0332: * The SQL command SET MAXROWS n will override the Statement.setMaxRows(n)
0333: * until SET MAXROWS 0 is issued.
0334: *
0335: * NB this is dedicated to the SET MAXROWS sql statement and should not
0336: * otherwise be called. (fredt@users)
0337: */
0338: void setSQLMaxRows(int rows) {
0339: currentMaxRows = sessionMaxRows = rows;
0340: }
0341:
0342: /**
0343: * Checks whether this Session's current User has the privileges of
0344: * the ADMIN role.
0345: *
0346: * @throws HsqlException if this Session's User does not have the
0347: * privileges of the ADMIN role.
0348: */
0349: void checkAdmin() throws HsqlException {
0350: user.checkAdmin();
0351: }
0352:
0353: /**
0354: * Checks whether this Session's current User has the set of rights
0355: * specified by the right argument, in relation to the database
0356: * object identifier specified by the object argument.
0357: *
0358: * @param object the database object to check
0359: * @param right the rights to check for
0360: * @throws HsqlException if the Session User does not have such rights
0361: */
0362: void check(HsqlName object, int right) throws HsqlException {
0363: user.check(object, right);
0364: }
0365:
0366: /**
0367: * Checks the rights on a function
0368: *
0369: * @param object the function
0370: * @throws HsqlException if the Session User does not have such rights
0371: */
0372: void check(String object) throws HsqlException {
0373: user.check(object);
0374: }
0375:
0376: /**
0377: * This is used for reading - writing to existing tables.
0378: * @throws HsqlException
0379: */
0380: void checkReadWrite() throws HsqlException {
0381:
0382: if (isReadOnly) {
0383: throw Trace.error(Trace.DATABASE_IS_READONLY);
0384: }
0385: }
0386:
0387: /**
0388: * This is used for creating new database objects such as tables.
0389: * @throws HsqlException
0390: */
0391: void checkDDLWrite() throws HsqlException {
0392:
0393: if (database.isFilesReadOnly() && !user.isSys()) {
0394: throw Trace.error(Trace.DATABASE_IS_READONLY);
0395: }
0396:
0397: checkReadWrite();
0398: }
0399:
0400: /**
0401: * Adds a single-row deletion step to the transaction UNDO buffer.
0402: *
0403: * @param table the table from which the row was deleted
0404: * @param row the deleted row
0405: * @throws HsqlException
0406: */
0407: boolean addDeleteAction(Table table, Row row) throws HsqlException {
0408:
0409: if (!isAutoCommit || isNestedTransaction) {
0410: Transaction t = new Transaction(true, table, row,
0411: actionTimestamp);
0412:
0413: rowActionList.add(t);
0414: database.txManager.addTransaction(this , t);
0415:
0416: return true;
0417: } else {
0418: table.removeRowFromStore(row);
0419: }
0420:
0421: return false;
0422: }
0423:
0424: /**
0425: * Adds a single-row insertion step to the transaction UNDO buffer.
0426: *
0427: * @param table the table into which the row was inserted
0428: * @param row the inserted row
0429: * @throws HsqlException
0430: */
0431: boolean addInsertAction(Table table, Row row) throws HsqlException {
0432:
0433: if (!isAutoCommit || isNestedTransaction) {
0434: Transaction t = new Transaction(false, table, row,
0435: actionTimestamp);
0436:
0437: rowActionList.add(t);
0438: database.txManager.addTransaction(this , t);
0439:
0440: return true;
0441: } else {
0442: table.commitRowToStore(row);
0443: }
0444:
0445: return false;
0446: }
0447:
0448: /**
0449: * Setter for the autocommit attribute.
0450: *
0451: * @param autocommit the new value
0452: * @throws HsqlException
0453: */
0454: public void setAutoCommit(boolean autocommit) {
0455:
0456: if (isClosed) {
0457: return;
0458: }
0459:
0460: synchronized (database) {
0461: if (autocommit != isAutoCommit) {
0462: commit();
0463:
0464: isAutoCommit = autocommit;
0465:
0466: try {
0467: database.logger.writeToLog(this ,
0468: getAutoCommitStatement());
0469: } catch (HsqlException e) {
0470: }
0471: }
0472: }
0473: }
0474:
0475: public void startPhasedTransaction() throws HsqlException {
0476: }
0477:
0478: public void prepareCommit() throws HsqlException {
0479: }
0480:
0481: /**
0482: * Commits any uncommited transaction this Session may have open
0483: *
0484: * @throws HsqlException
0485: */
0486: public void commit() {
0487:
0488: if (isClosed) {
0489: return;
0490: }
0491:
0492: synchronized (database) {
0493: if (!rowActionList.isEmpty()) {
0494: try {
0495: database.logger.writeCommitStatement(this );
0496: } catch (HsqlException e) {
0497: }
0498: }
0499:
0500: database.txManager.commit(this );
0501: clearIndexRoots();
0502: }
0503: }
0504:
0505: /**
0506: * Rolls back any uncommited transaction this Session may have open.
0507: *
0508: * @throws HsqlException
0509: */
0510: public void rollback() {
0511:
0512: if (isClosed) {
0513: return;
0514: }
0515:
0516: synchronized (database) {
0517: if (rowActionList.size() != 0) {
0518: try {
0519: database.logger.writeToLog(this , Token.T_ROLLBACK);
0520: } catch (HsqlException e) {
0521: }
0522: }
0523:
0524: database.txManager.rollback(this );
0525: clearIndexRoots();
0526: }
0527: }
0528:
0529: /**
0530: * No-op in this implementation
0531: */
0532: public void resetSession() throws HsqlException {
0533: throw new HsqlException("", "", 0);
0534: }
0535:
0536: /**
0537: * Implements a transaction SAVEPOINT. A new SAVEPOINT with the
0538: * name of an existing one replaces the old SAVEPOINT.
0539: *
0540: * @param name of the savepoint
0541: * @throws HsqlException if there is no current transaction
0542: */
0543: void savepoint(String name) throws HsqlException {
0544:
0545: savepoints.remove(name);
0546: savepoints.add(name, ValuePool.getInt(rowActionList.size()));
0547:
0548: try {
0549: database.logger.writeToLog(this , Token.T_SAVEPOINT + " "
0550: + name);
0551: } catch (HsqlException e) {
0552: }
0553: }
0554:
0555: /**
0556: * Implements a partial transaction ROLLBACK.
0557: *
0558: * @param name Name of savepoint that was marked before by savepoint()
0559: * call
0560: * @throws HsqlException
0561: */
0562: void rollbackToSavepoint(String name) throws HsqlException {
0563:
0564: if (isClosed) {
0565: return;
0566: }
0567:
0568: try {
0569: database.logger
0570: .writeToLog(this , Token.T_ROLLBACK + " "
0571: + Token.T_TO + " " + Token.T_SAVEPOINT
0572: + " " + name);
0573: } catch (HsqlException e) {
0574: }
0575:
0576: database.txManager.rollbackSavepoint(this , name);
0577: }
0578:
0579: /**
0580: * Implements release of named SAVEPOINT.
0581: *
0582: * @param name Name of savepoint that was marked before by savepoint()
0583: * call
0584: * @throws HsqlException if name does not correspond to a savepoint
0585: */
0586: void releaseSavepoint(String name) throws HsqlException {
0587:
0588: // remove this and all later savepoints
0589: int index = savepoints.getIndex(name);
0590:
0591: Trace.check(index >= 0, Trace.SAVEPOINT_NOT_FOUND, name);
0592:
0593: while (savepoints.size() > index) {
0594: savepoints.remove(savepoints.size() - 1);
0595: }
0596: }
0597:
0598: /**
0599: * Starts a nested transaction.
0600: *
0601: * @throws HsqlException
0602: */
0603: void beginNestedTransaction() throws HsqlException {
0604:
0605: if (isNestedTransaction) {
0606: Trace.doAssert(false, "beginNestedTransaction");
0607: }
0608:
0609: nestedOldTransIndex = rowActionList.size();
0610: isNestedTransaction = true;
0611:
0612: if (isAutoCommit) {
0613: try {
0614: database.logger
0615: .writeToLog(this , "SET AUTOCOMMIT FALSE");
0616: } catch (HsqlException e) {
0617: }
0618: }
0619: }
0620:
0621: /**
0622: * @todo -- fredt 20050604 - if this method is called after an out of memory
0623: * error during update, the next block might throw out of memory too and as
0624: * a result inNestedTransaction remains true and no further update
0625: * is possible. The session must be closed at that point by the user
0626: * application.
0627: */
0628:
0629: /**
0630: * Ends a nested transaction.
0631: *
0632: * @param rollback true to roll back or false to commit the nested transaction
0633: * @throws HsqlException
0634: */
0635: void endNestedTransaction(boolean rollback) throws HsqlException {
0636:
0637: if (!isNestedTransaction) {
0638: Trace.doAssert(false, "endNestedTransaction");
0639: }
0640:
0641: if (rollback) {
0642: database.txManager.rollbackTransactions(this ,
0643: nestedOldTransIndex, true);
0644: }
0645:
0646: // reset after the rollback
0647: isNestedTransaction = false;
0648:
0649: if (isAutoCommit) {
0650: database.txManager.commit(this );
0651:
0652: try {
0653: database.logger.writeToLog(this , "SET AUTOCOMMIT TRUE");
0654: } catch (HsqlException e) {
0655: }
0656: }
0657: }
0658:
0659: /**
0660: * Setter for readonly attribute.
0661: *
0662: * @param readonly the new value
0663: */
0664: public void setReadOnly(boolean readonly) throws HsqlException {
0665:
0666: if (!readonly && database.databaseReadOnly) {
0667: throw Trace.error(Trace.DATABASE_IS_READONLY);
0668: }
0669:
0670: isReadOnly = readonly;
0671: }
0672:
0673: /**
0674: * Getter for readonly attribute.
0675: *
0676: * @return the current value
0677: */
0678: public boolean isReadOnly() {
0679: return isReadOnly;
0680: }
0681:
0682: /**
0683: * Getter for nestedTransaction attribute.
0684: *
0685: * @return the current value
0686: */
0687: boolean isNestedTransaction() {
0688: return isNestedTransaction;
0689: }
0690:
0691: /**
0692: * Getter for autoCommit attribute.
0693: *
0694: * @return the current value
0695: */
0696: public boolean isAutoCommit() {
0697: return isAutoCommit;
0698: }
0699:
0700: /**
0701: * A switch to set scripting on the basis of type of statement executed.
0702: * A method in DatabaseCommandInterpreter.java sets this value to false
0703: * before other methods are called to act on an SQL statement, which may
0704: * set this to true. Afterwards the method reponsible for logging uses
0705: * getScripting() to determine if logging is required for the executed
0706: * statement. (fredt@users)
0707: *
0708: * @param script The new scripting value
0709: */
0710: void setScripting(boolean script) {
0711: this .script = script;
0712: }
0713:
0714: /**
0715: * Getter for scripting attribute.
0716: *
0717: * @return scripting for the last statement.
0718: */
0719: boolean getScripting() {
0720: return script;
0721: }
0722:
0723: public String getAutoCommitStatement() {
0724: return isAutoCommit ? "SET AUTOCOMMIT TRUE"
0725: : "SET AUTOCOMMIT FALSE";
0726: }
0727:
0728: /**
0729: * Retrieves an internal Connection object equivalent to the one
0730: * that created this Session.
0731: *
0732: * @return internal connection.
0733: */
0734: jdbcConnection getInternalConnection() throws HsqlException {
0735:
0736: if (intConnection == null) {
0737: intConnection = new jdbcConnection(this );
0738: }
0739:
0740: return intConnection;
0741: }
0742:
0743: // boucherb@users 20020810 metadata 1.7.2
0744: //----------------------------------------------------------------
0745: private final long connectTime = System.currentTimeMillis();
0746:
0747: // more effecient for MetaData concerns than checkAdmin
0748:
0749: /**
0750: * Getter for admin attribute.
0751: *
0752: * @ return the current value
0753: */
0754: boolean isAdmin() {
0755: return user.isAdmin();
0756: }
0757:
0758: /**
0759: * Getter for connectTime attribute.
0760: *
0761: * @return the value
0762: */
0763: long getConnectTime() {
0764: return connectTime;
0765: }
0766:
0767: /**
0768: * Getter for transactionSise attribute.
0769: *
0770: * @return the current value
0771: */
0772: int getTransactionSize() {
0773: return rowActionList.size();
0774: }
0775:
0776: /**
0777: * Retrieves whether the database object identifier by the dbobject
0778: * argument is accessible by the current Session User.
0779: *
0780: * @return true if so, else false
0781: */
0782: boolean isAccessible(String dbobject) throws HsqlException {
0783: return user.isAccessible(dbobject);
0784: }
0785:
0786: boolean isAccessible(HsqlName dbobject) throws HsqlException {
0787: return user.isAccessible(dbobject);
0788: }
0789:
0790: // boucherb@users 20030417 - patch 1.7.2 - compiled statement support
0791: //-------------------------------------------------------------------
0792: DatabaseCommandInterpreter dbCommandInterpreter;
0793: CompiledStatementExecutor compiledStatementExecutor;
0794: CompiledStatementManager compiledStatementManager;
0795:
0796: CompiledStatement sqlCompileStatement(String sql)
0797: throws HsqlException {
0798:
0799: parser.reset(sql);
0800:
0801: CompiledStatement cs;
0802: int brackets = 0;
0803: String token = tokenizer.getString();
0804: int cmd = Token.get(token);
0805:
0806: switch (cmd) {
0807:
0808: case Token.OPENBRACKET: {
0809: brackets = parser.parseOpenBracketsSelect() + 1;
0810: }
0811: case Token.SELECT: {
0812: cs = parser.compileSelectStatement(brackets);
0813:
0814: break;
0815: }
0816: case Token.INSERT: {
0817: cs = parser.compileInsertStatement();
0818:
0819: break;
0820: }
0821: case Token.UPDATE: {
0822: cs = parser.compileUpdateStatement();
0823:
0824: break;
0825: }
0826: case Token.DELETE: {
0827: cs = parser.compileDeleteStatement();
0828:
0829: break;
0830: }
0831: case Token.CALL: {
0832: cs = parser.compileCallStatement();
0833:
0834: break;
0835: }
0836: default: {
0837:
0838: // DDL statements
0839: cs = new CompiledStatement(currentSchema);
0840:
0841: break;
0842: }
0843: }
0844:
0845: // In addition to requiring that the compilation was successful,
0846: // we also require that the submitted sql represents a _single_
0847: // valid DML or DDL statement. We do not check the DDL yet.
0848: // fredt - now accepts semicolon and whitespace at the end of statement
0849: // fredt - investigate if it should or not for prepared statements
0850: if (cs.type != CompiledStatement.DDL) {
0851: while (tokenizer.getPosition() < tokenizer.getLength()) {
0852: token = tokenizer.getString();
0853:
0854: if (token.length() != 0
0855: && !token.equals(Token.T_SEMICOLON)) {
0856: throw Trace.error(Trace.UNEXPECTED_TOKEN, token);
0857: }
0858: }
0859: }
0860:
0861: // - need to be able to key cs against its sql in statement pool
0862: // - also need to be able to revalidate its sql occasionally
0863: cs.sql = sql;
0864:
0865: return cs;
0866: }
0867:
0868: /**
0869: * Executes the command encapsulated by the cmd argument.
0870: *
0871: * @param cmd the command to execute
0872: * @return the result of executing the command
0873: */
0874: public Result execute(Result cmd) {
0875:
0876: try {
0877: if (isClosed) {
0878: Trace.check(false, Trace.ACCESS_IS_DENIED, Trace
0879: .getMessage(Trace.Session_execute));
0880: }
0881: } catch (Throwable t) {
0882: return new Result(t, null);
0883: }
0884:
0885: synchronized (database) {
0886: int type = cmd.mode;
0887:
0888: if (sessionMaxRows == 0) {
0889: currentMaxRows = cmd.updateCount;
0890: }
0891:
0892: // we simply get the next system change number - no matter what type of query
0893: actionTimestamp = database.txManager.nextActionTimestamp();
0894:
0895: JavaSystem.gc();
0896:
0897: switch (type) {
0898:
0899: case ResultConstants.SQLEXECUTE: {
0900: Result resultout = sqlExecute(cmd);
0901:
0902: resultout = performPostExecute(resultout);
0903:
0904: return resultout;
0905: }
0906: case ResultConstants.BATCHEXECUTE: {
0907: Result resultout = sqlExecuteBatch(cmd);
0908:
0909: resultout = performPostExecute(resultout);
0910:
0911: return resultout;
0912: }
0913: case ResultConstants.SQLEXECDIRECT: {
0914: Result resultout = sqlExecuteDirectNoPreChecks(cmd
0915: .getMainString());
0916:
0917: resultout = performPostExecute(resultout);
0918:
0919: return resultout;
0920: }
0921: case ResultConstants.BATCHEXECDIRECT: {
0922: Result resultout = sqlExecuteBatchDirect(cmd);
0923:
0924: resultout = performPostExecute(resultout);
0925:
0926: return resultout;
0927: }
0928: case ResultConstants.SQLPREPARE: {
0929: CompiledStatement cs;
0930:
0931: try {
0932: cs = compiledStatementManager.compile(this , cmd
0933: .getMainString());
0934: } catch (Throwable t) {
0935: return new Result(t, cmd.getMainString());
0936: }
0937:
0938: Result rmd = cs.describeResult();
0939: Result pmd = cs.describeParameters();
0940:
0941: return Result.newPrepareResponse(cs.id, rmd, pmd);
0942: }
0943: case ResultConstants.SQLFREESTMT: {
0944: compiledStatementManager.freeStatement(cmd
0945: .getStatementID(), sessionId, false);
0946:
0947: return emptyUpdateCount;
0948: }
0949: case ResultConstants.GETSESSIONATTR: {
0950: return getAttributes();
0951: }
0952: case ResultConstants.SETSESSIONATTR: {
0953: return setAttributes(cmd);
0954: }
0955: case ResultConstants.SQLENDTRAN: {
0956: switch (cmd.getEndTranType()) {
0957:
0958: case ResultConstants.COMMIT:
0959: commit();
0960: break;
0961:
0962: case ResultConstants.ROLLBACK:
0963: rollback();
0964: break;
0965:
0966: case ResultConstants.SAVEPOINT_NAME_RELEASE:
0967: try {
0968: String name = cmd.getMainString();
0969:
0970: releaseSavepoint(name);
0971: } catch (Throwable t) {
0972: return new Result(t, null);
0973: }
0974: break;
0975:
0976: case ResultConstants.SAVEPOINT_NAME_ROLLBACK:
0977: try {
0978: rollbackToSavepoint(cmd.getMainString());
0979: } catch (Throwable t) {
0980: return new Result(t, null);
0981: }
0982: break;
0983:
0984: // not yet
0985: // case ResultConstants.COMMIT_AND_CHAIN :
0986: // case ResultConstants.ROLLBACK_AND_CHAIN :
0987: }
0988:
0989: return emptyUpdateCount;
0990: }
0991: case ResultConstants.SQLSETCONNECTATTR: {
0992: switch (cmd.getConnectionAttrType()) {
0993:
0994: case ResultConstants.SQL_ATTR_SAVEPOINT_NAME:
0995: try {
0996: savepoint(cmd.getMainString());
0997: } catch (Throwable t) {
0998: return new Result(t, null);
0999: }
1000:
1001: // case ResultConstants.SQL_ATTR_AUTO_IPD
1002: // - always true
1003: // default: throw - case never happens
1004: }
1005:
1006: return emptyUpdateCount;
1007: }
1008: case ResultConstants.SQLDISCONNECT: {
1009: close();
1010:
1011: return emptyUpdateCount;
1012: }
1013: default: {
1014: return new Result(Trace.runtimeError(
1015: Trace.UNSUPPORTED_INTERNAL_OPERATION,
1016: "Session.execute()"), null);
1017: }
1018: }
1019: }
1020: }
1021:
1022: private Result performPostExecute(Result r) {
1023:
1024: try {
1025: if (database != null) {
1026: database.schemaManager.logSequences(this ,
1027: database.logger);
1028:
1029: if (isAutoCommit) {
1030: clearIndexRoots();
1031: database.logger.synchLog();
1032: }
1033: }
1034:
1035: return r;
1036: } catch (Exception e) {
1037: return new Result(e, null);
1038: } finally {
1039: if (database != null && database.logger.needsCheckpoint()) {
1040: try {
1041: database.logger.checkpoint(false);
1042: } catch (HsqlException e) {
1043: database.logger.appLog.logContext(
1044: SimpleLog.LOG_ERROR,
1045: "checkpoint did not complete");
1046: }
1047: }
1048: }
1049: }
1050:
1051: public Result sqlExecuteDirectNoPreChecks(String sql) {
1052:
1053: synchronized (database) {
1054: return dbCommandInterpreter.execute(sql);
1055: }
1056: }
1057:
1058: Result sqlExecuteCompiledNoPreChecks(CompiledStatement cs,
1059: Object[] pvals) {
1060: return compiledStatementExecutor.execute(cs, pvals);
1061: }
1062:
1063: private Result sqlExecuteBatch(Result cmd) {
1064:
1065: int csid;
1066: Record record;
1067: Result out;
1068: CompiledStatement cs;
1069: Expression[] parameters;
1070: int[] updateCounts;
1071: int count;
1072:
1073: csid = cmd.getStatementID();
1074: cs = database.compiledStatementManager.getStatement(this , csid);
1075:
1076: if (cs == null) {
1077:
1078: // invalid sql has been removed already
1079: return new Result(Trace.runtimeError(
1080: Trace.INVALID_PREPARED_STATEMENT, null), null);
1081: }
1082:
1083: parameters = cs.parameters;
1084: count = 0;
1085: updateCounts = new int[cmd.getSize()];
1086: record = cmd.rRoot;
1087:
1088: while (record != null) {
1089: Result in;
1090: Object[] pvals = record.data;
1091:
1092: in = sqlExecuteCompiledNoPreChecks(cs, pvals);
1093:
1094: // On the client side, iterate over the vals and throw
1095: // a BatchUpdateException if a batch status value of
1096: // esultConstants.EXECUTE_FAILED is encountered in the result
1097: if (in.mode == ResultConstants.UPDATECOUNT) {
1098: updateCounts[count++] = in.updateCount;
1099: } else if (in.isData()) {
1100:
1101: // FIXME: we don't have what it takes yet
1102: // to differentiate between things like
1103: // stored procedure calls to methods with
1104: // void return type and select statements with
1105: // a single row/column containg null
1106: updateCounts[count++] = ResultConstants.SUCCESS_NO_INFO;
1107: } else {
1108: updateCounts = ArrayUtil.arraySlice(updateCounts, 0,
1109: count);
1110:
1111: break;
1112: }
1113:
1114: record = record.next;
1115: }
1116:
1117: out = new Result(ResultConstants.SQLEXECUTE, updateCounts, 0);
1118:
1119: return out;
1120: }
1121:
1122: private Result sqlExecuteBatchDirect(Result cmd) {
1123:
1124: Record record;
1125: Result out;
1126: int[] updateCounts;
1127: int count;
1128:
1129: count = 0;
1130: updateCounts = new int[cmd.getSize()];
1131: record = cmd.rRoot;
1132:
1133: while (record != null) {
1134: Result in;
1135: String sql = (String) record.data[0];
1136:
1137: try {
1138: in = dbCommandInterpreter.execute(sql);
1139: } catch (Throwable t) {
1140: in = new Result(ResultConstants.ERROR);
1141:
1142: // if (t instanceof OutOfMemoryError) {
1143: // System.gc();
1144: // }
1145: // "in" alread equals "err"
1146: // maybe test for OOME and do a gc() ?
1147: // t.printStackTrace();
1148: }
1149:
1150: // On the client side, iterate over the colType vals and throw
1151: // a BatchUpdateException if a batch status value of
1152: // ResultConstants.EXECUTE_FAILED is encountered
1153: if (in.mode == ResultConstants.UPDATECOUNT) {
1154: updateCounts[count++] = in.updateCount;
1155: } else if (in.isData()) {
1156:
1157: // FIXME: we don't have what it takes yet
1158: // to differentiate between things like
1159: // stored procedure calls to methods with
1160: // void return type and select statements with
1161: // a single row/column containg null
1162: updateCounts[count++] = ResultConstants.SUCCESS_NO_INFO;
1163: } else {
1164: updateCounts = ArrayUtil.arraySlice(updateCounts, 0,
1165: count);
1166:
1167: break;
1168: }
1169:
1170: record = record.next;
1171: }
1172:
1173: out = new Result(ResultConstants.SQLEXECUTE, updateCounts, 0);
1174:
1175: return out;
1176: }
1177:
1178: /**
1179: * Retrieves the result of executing the prepared statement whose csid
1180: * and parameter values/types are encapsulated by the cmd argument.
1181: *
1182: * @return the result of executing the statement
1183: */
1184: private Result sqlExecute(Result cmd) {
1185:
1186: int csid = cmd.getStatementID();
1187: CompiledStatement cs = compiledStatementManager.getStatement(
1188: this , csid);
1189:
1190: if (cs == null) {
1191:
1192: // invalid sql has been removed already
1193: return new Result(Trace.runtimeError(
1194: Trace.INVALID_PREPARED_STATEMENT, null), null);
1195: }
1196:
1197: Object[] pvals = cmd.getParameterData();
1198:
1199: return sqlExecute(cs, pvals);
1200: }
1201:
1202: private Result sqlExecute(CompiledStatement cs, Object[] pvals) {
1203: return sqlExecuteCompiledNoPreChecks(cs, pvals);
1204: }
1205:
1206: // session DATETIME functions
1207: long currentDateTimeSCN;
1208: long currentMillis;
1209: Date currentDate;
1210: Time currentTime;
1211: Timestamp currentTimestamp;
1212:
1213: /**
1214: * Returns the current date, unchanged for the duration of the current
1215: * execution unit (statement).<p>
1216: *
1217: * SQL standards require that CURRENT_DATE, CURRENT_TIME and
1218: * CURRENT_TIMESTAMP are all evaluated at the same point of
1219: * time in the duration of each SQL statement, no matter how long the
1220: * SQL statement takes to complete.<p>
1221: *
1222: * When this method or a corresponding method for CURRENT_TIME or
1223: * CURRENT_TIMESTAMP is first called in the scope of a system change
1224: * number, currentMillis is set to the current system time. All further
1225: * CURRENT_XXXX calls in this scope will use this millisecond value.
1226: * (fredt@users)
1227: */
1228: Date getCurrentDate() {
1229:
1230: if (currentDateTimeSCN != actionTimestamp) {
1231: currentDateTimeSCN = actionTimestamp;
1232: currentMillis = System.currentTimeMillis();
1233: currentDate = HsqlDateTime.getCurrentDate(currentMillis);
1234: currentTime = null;
1235: currentTimestamp = null;
1236: } else if (currentDate == null) {
1237: currentDate = HsqlDateTime.getCurrentDate(currentMillis);
1238: }
1239:
1240: return currentDate;
1241: }
1242:
1243: /**
1244: * Returns the current time, unchanged for the duration of the current
1245: * execution unit (statement)
1246: */
1247: Time getCurrentTime() {
1248:
1249: if (currentDateTimeSCN != actionTimestamp) {
1250: currentDateTimeSCN = actionTimestamp;
1251: currentMillis = System.currentTimeMillis();
1252: currentDate = null;
1253: currentTime = new Time(HsqlDateTime
1254: .getNormalisedTime(currentMillis));
1255: currentTimestamp = null;
1256: } else if (currentTime == null) {
1257: currentTime = new Time(HsqlDateTime
1258: .getNormalisedTime(currentMillis));
1259: }
1260:
1261: return currentTime;
1262: }
1263:
1264: /**
1265: * Returns the current timestamp, unchanged for the duration of the current
1266: * execution unit (statement)
1267: */
1268: Timestamp getCurrentTimestamp() {
1269:
1270: if (currentDateTimeSCN != actionTimestamp) {
1271: currentDateTimeSCN = actionTimestamp;
1272: currentMillis = System.currentTimeMillis();
1273: currentDate = null;
1274: currentTime = null;
1275: currentTimestamp = HsqlDateTime.getTimestamp(currentMillis);
1276: } else if (currentTimestamp == null) {
1277: currentTimestamp = HsqlDateTime.getTimestamp(currentMillis);
1278: }
1279:
1280: return currentTimestamp;
1281: }
1282:
1283: Result getAttributes() {
1284:
1285: Result r = Result.newSessionAttributesResult();
1286: Object[] row = new Object[] { database.getURI(), getUsername(),
1287: ValuePool.getInt(sessionId),
1288: ValuePool.getInt(isolationMode),
1289: ValuePool.getBoolean(isAutoCommit),
1290: ValuePool.getBoolean(database.databaseReadOnly),
1291: ValuePool.getBoolean(isReadOnly) };
1292:
1293: r.add(row);
1294:
1295: return r;
1296: }
1297:
1298: Result setAttributes(Result r) {
1299:
1300: Object[] row = r.rRoot.data;
1301:
1302: for (int i = 0; i < row.length; i++) {
1303: Object value = row[i];
1304:
1305: if (value == null) {
1306: continue;
1307: }
1308:
1309: try {
1310: switch (i) {
1311:
1312: case SessionInterface.INFO_AUTOCOMMIT: {
1313: this
1314: .setAutoCommit(((Boolean) value)
1315: .booleanValue());
1316:
1317: break;
1318: }
1319: case SessionInterface.INFO_CONNECTION_READONLY:
1320: this .setReadOnly(((Boolean) value).booleanValue());
1321: break;
1322: }
1323: } catch (HsqlException e) {
1324: return new Result(e, null);
1325: }
1326: }
1327:
1328: return emptyUpdateCount;
1329: }
1330:
1331: // DatabaseMetaData.getURL should work as specified for
1332: // internal connections too.
1333: public String getInternalConnectionURL() {
1334: return DatabaseURL.S_URL_PREFIX + database.getURI();
1335: }
1336:
1337: boolean isProcessingScript() {
1338: return isProcessingScript;
1339: }
1340:
1341: boolean isProcessingLog() {
1342: return isProcessingLog;
1343: }
1344:
1345: boolean isSchemaDefintion() {
1346: return oldSchema != null;
1347: }
1348:
1349: void startSchemaDefinition(String schema) throws HsqlException {
1350:
1351: if (isProcessingScript) {
1352: setSchema(schema);
1353:
1354: return;
1355: }
1356:
1357: oldSchema = currentSchema;
1358:
1359: setSchema(schema);
1360: }
1361:
1362: void endSchemaDefinition() throws HsqlException {
1363:
1364: if (oldSchema == null) {
1365: return;
1366: }
1367:
1368: currentSchema = oldSchema;
1369: oldSchema = null;
1370:
1371: database.logger.writeToLog(this , "SET SCHEMA "
1372: + currentSchema.statementName);
1373: }
1374:
1375: // schema object methods
1376: public void setSchema(String schema) throws HsqlException {
1377: currentSchema = database.schemaManager
1378: .getSchemaHsqlName(schema);
1379: }
1380:
1381: /**
1382: * If schemaName is null, return the current schema name, else return
1383: * the HsqlName object for the schema. If schemaName does not exist,
1384: * throw.
1385: */
1386: HsqlName getSchemaHsqlName(String name) throws HsqlException {
1387: return name == null ? currentSchema : database.schemaManager
1388: .getSchemaHsqlName(name);
1389: }
1390:
1391: /**
1392: * Same as above, but return string
1393: */
1394: public String getSchemaName(String name) throws HsqlException {
1395: return name == null ? currentSchema.name
1396: : database.schemaManager.getSchemaName(name);
1397: }
1398:
1399: /**
1400: * If schemaName is null, return the current schema name, else return
1401: * the HsqlName object for the schema. If schemaName does not exist, or
1402: * schema readonly, throw.
1403: */
1404: HsqlName getSchemaHsqlNameForWrite(String name)
1405: throws HsqlException {
1406:
1407: HsqlName schema = getSchemaHsqlName(name);
1408:
1409: if (database.schemaManager.isSystemSchema(schema)) {
1410: throw Trace.error(Trace.INVALID_SCHEMA_NAME_NO_SUBCLASS);
1411: }
1412:
1413: return schema;
1414: }
1415:
1416: /**
1417: * Same as above, but return string
1418: */
1419: public String getSchemaNameForWrite(String name)
1420: throws HsqlException {
1421:
1422: HsqlName schema = getSchemaHsqlNameForWrite(name);
1423:
1424: return schema.name;
1425: }
1426:
1427: /**
1428: * get the root for a temp table index
1429: */
1430: Node getIndexRoot(HsqlName index, boolean preserve) {
1431:
1432: if (preserve) {
1433: if (indexArrayKeepMap == null) {
1434: return null;
1435: }
1436:
1437: return (Node) indexArrayKeepMap.get(index.hashCode());
1438: } else {
1439: if (indexArrayMap == null) {
1440: return null;
1441: }
1442:
1443: return (Node) indexArrayMap.get(index.hashCode());
1444: }
1445: }
1446:
1447: /**
1448: * set the root for a temp table index
1449: */
1450: void setIndexRoot(HsqlName index, boolean preserve, Node root) {
1451:
1452: if (preserve) {
1453: if (indexArrayKeepMap == null) {
1454: if (root == null) {
1455: return;
1456: }
1457:
1458: indexArrayKeepMap = new IntKeyHashMap();
1459: }
1460:
1461: indexArrayKeepMap.put(index.hashCode(), root);
1462: } else {
1463: if (indexArrayMap == null) {
1464: if (root == null) {
1465: return;
1466: }
1467:
1468: indexArrayMap = new IntKeyHashMap();
1469: }
1470:
1471: indexArrayMap.put(index.hashCode(), root);
1472: }
1473: }
1474:
1475: void dropIndex(HsqlName index, boolean preserve) {
1476:
1477: if (preserve) {
1478: if (indexArrayKeepMap != null) {
1479: indexArrayKeepMap.remove(index.hashCode());
1480: }
1481: } else {
1482: if (indexArrayMap != null) {
1483: indexArrayMap.remove(index.hashCode());
1484: }
1485: }
1486: }
1487:
1488: /**
1489: * clear default temp table contents for this session
1490: */
1491: void clearIndexRoots() {
1492:
1493: if (indexArrayMap != null) {
1494: indexArrayMap.clear();
1495: }
1496: }
1497:
1498: /**
1499: * clear ON COMMIT PRESERVE temp table contents for this session
1500: */
1501: void clearIndexRootsKeep() {
1502:
1503: if (indexArrayKeepMap != null) {
1504: indexArrayKeepMap.clear();
1505: }
1506: }
1507:
1508: // warnings
1509: HsqlArrayList sqlWarnings;
1510:
1511: public void addWarning(HsqlException warning) {
1512:
1513: if (sqlWarnings == null) {
1514: sqlWarnings = new HsqlArrayList(true);
1515: }
1516:
1517: sqlWarnings.add(warning);
1518: }
1519:
1520: public HsqlException[] getAndClearWarnings() {
1521:
1522: if (sqlWarnings == null) {
1523: return new HsqlException[0];
1524: }
1525:
1526: HsqlException[] array = new HsqlException[sqlWarnings.size()];
1527:
1528: sqlWarnings.toArray(array);
1529: sqlWarnings.clear();
1530:
1531: return array;
1532: }
1533: }
|