EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.engine]

COVERAGE SUMMARY FOR SOURCE FILE [Session.java]

nameclass, %method, %block, %line, %
Session.java100% (2/2)97%  (111/114)86%  (1814/2112)87%  (497.8/575)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class Session100% (1/1)97%  (110/113)86%  (1811/2109)87%  (496.8/574)
reconnect (boolean): SessionInterface 0%   (0/1)0%   (0/24)0%   (0/8)
setConnectionInfo (ConnectionInfo): void 0%   (0/1)0%   (0/4)0%   (0/2)
setRedoLogBinary (boolean): void 0%   (0/1)0%   (0/4)0%   (0/2)
unlockReadLocks (): void 100% (1/1)11%  (5/46)18%  (2/11)
addTemporaryResult (LocalResult): void 100% (1/1)19%  (4/21)29%  (2/7)
closeTemporaryResults (): void 100% (1/1)19%  (4/21)33%  (2/6)
getTransactionId (): Value 100% (1/1)34%  (16/47)44%  (4/9)
checkCommitRollback (): void 100% (1/1)36%  (4/11)47%  (1.4/3)
log (Table, short, Row): void 100% (1/1)40%  (32/80)53%  (10/19)
waitIfExclusiveModeEnabled (): void 100% (1/1)65%  (17/26)50%  (6/12)
setQueryTimeout (int): void 100% (1/1)67%  (14/21)76%  (4.6/6)
hasPendingTransaction (): boolean 100% (1/1)75%  (6/8)75%  (0.8/1)
removeLocalTempTableConstraint (Constraint): void 100% (1/1)81%  (21/26)96%  (5.8/6)
removeLocalTempTableIndex (Index): void 100% (1/1)81%  (21/26)96%  (5.8/6)
addLocalTempTableIndex (Index): void 100% (1/1)81%  (22/27)83%  (5/6)
getProcedure (String): Procedure 100% (1/1)82%  (9/11)67%  (2/3)
addLocalTempTableConstraint (Constraint): void 100% (1/1)82%  (23/28)86%  (6/7)
removeLocalTempTable (Table): void 100% (1/1)83%  (24/29)97%  (5.8/6)
isReconnectNeeded (boolean): boolean 100% (1/1)84%  (16/19)75%  (6/8)
addLocalTempTable (Table): void 100% (1/1)85%  (28/33)86%  (6/7)
addLock (Table): void 100% (1/1)87%  (13/15)80%  (4/5)
setPreparedTransaction (String, boolean): void 100% (1/1)87%  (52/60)82%  (14/17)
rollbackTo (Session$Savepoint, boolean): void 100% (1/1)89%  (132/149)85%  (29/34)
rollback (): void 100% (1/1)89%  (51/57)89%  (17/19)
unlinkAtCommit (Value): void 100% (1/1)90%  (19/21)83%  (5/6)
cleanTempTables (boolean): void 100% (1/1)93%  (65/70)96%  (14.4/15)
getLocks (): Table [] 100% (1/1)94%  (29/31)78%  (7/9)
unlockAll (): void 100% (1/1)95%  (40/42)91%  (10/11)
commit (boolean): void 100% (1/1)97%  (149/154)98%  (40.4/41)
throttle (): void 100% (1/1)97%  (35/36)92%  (11/12)
Session (Database, User, int): void 100% (1/1)98%  (84/86)100% (23/23)
addLogPos (int, int): void 100% (1/1)100% (11/11)100% (4/4)
addProcedure (Procedure): void 100% (1/1)100% (16/16)100% (4/4)
addSavepoint (String): void 100% (1/1)100% (32/32)100% (8/8)
addTemporaryLob (Value): void 100% (1/1)100% (14/14)100% (4/4)
afterWriting (): void 100% (1/1)100% (4/4)100% (2/2)
begin (): void 100% (1/1)100% (7/7)100% (3/3)
cancel (): void 100% (1/1)100% (4/4)100% (2/2)
checkCanceled (): void 100% (1/1)100% (22/22)100% (8/8)
close (): void 100% (1/1)100% (27/27)100% (8/8)
containsUncommitted (): boolean 100% (1/1)100% (19/19)100% (3/3)
createConnection (boolean): JdbcConnection 100% (1/1)100% (16/16)100% (4/4)
endStatement (): void 100% (1/1)100% (6/6)100% (3/3)
endTransaction (): void 100% (1/1)100% (35/35)100% (9/9)
findLocalTempTable (String): Table 100% (1/1)100% (11/11)100% (3/3)
findLocalTempTableConstraint (String): Constraint 100% (1/1)100% (11/11)100% (3/3)
findLocalTempTableIndex (String): Index 100% (1/1)100% (11/11)100% (3/3)
getAllowLiterals (): boolean 100% (1/1)100% (3/3)100% (1/1)
getAutoCommit (): boolean 100% (1/1)100% (3/3)100% (1/1)
getCancel (): long 100% (1/1)100% (3/3)100% (1/1)
getClusterServers (): ArrayList 100% (1/1)100% (4/4)100% (1/1)
getCurrentCommand (): Command 100% (1/1)100% (3/3)100% (1/1)
getCurrentCommandStart (): long 100% (1/1)100% (3/3)100% (1/1)
getCurrentSchemaName (): String 100% (1/1)100% (3/3)100% (1/1)
getDataHandler (): DataHandler 100% (1/1)100% (3/3)100% (1/1)
getDatabase (): Database 100% (1/1)100% (3/3)100% (1/1)
getFirstUncommittedLog (): int 100% (1/1)100% (3/3)100% (1/1)
getId (): int 100% (1/1)100% (3/3)100% (1/1)
getLastIdentity (): Value 100% (1/1)100% (3/3)100% (1/1)
getLastScopeIdentity (): Value 100% (1/1)100% (3/3)100% (1/1)
getLocalTempTableConstraints (): HashMap 100% (1/1)100% (8/8)100% (3/3)
getLocalTempTableIndexes (): HashMap 100% (1/1)100% (8/8)100% (3/3)
getLocalTempTables (): ArrayList 100% (1/1)100% (10/10)100% (3/3)
getLockTimeout (): int 100% (1/1)100% (3/3)100% (1/1)
getModificationId (): int 100% (1/1)100% (3/3)100% (1/1)
getNextSystemIdentifier (String): String 100% (1/1)100% (21/21)100% (3/3)
getPowerOffCount (): int 100% (1/1)100% (4/4)100% (1/1)
getQueryTimeout (): int 100% (1/1)100% (3/3)100% (1/1)
getRandom (): Random 100% (1/1)100% (11/11)100% (3/3)
getSchemaSearchPath (): String [] 100% (1/1)100% (3/3)100% (1/1)
getSessionStart (): long 100% (1/1)100% (3/3)100% (1/1)
getStatementSavepoint (): long 100% (1/1)100% (13/13)100% (3/3)
getTrace (): Trace 100% (1/1)100% (40/40)100% (7/7)
getTransaction (): TransactionStore$Transaction 100% (1/1)100% (28/28)100% (7/7)
getTransactionStart (): long 100% (1/1)100% (11/11)100% (3/3)
getUser (): User 100% (1/1)100% (3/3)100% (1/1)
getVariable (String): Value 100% (1/1)100% (14/14)100% (3/3)
getVariableNames (): String [] 100% (1/1)100% (19/19)100% (5/5)
getWaitForLock (): Table 100% (1/1)100% (3/3)100% (1/1)
getWaitForLockThread (): Thread 100% (1/1)100% (3/3)100% (1/1)
hashCode (): int 100% (1/1)100% (3/3)100% (1/1)
initVariables (): void 100% (1/1)100% (9/9)100% (3/3)
isClosed (): boolean 100% (1/1)100% (3/3)100% (1/1)
isRedoLogBinaryEnabled (): boolean 100% (1/1)100% (3/3)100% (1/1)
isUndoLogEnabled (): boolean 100% (1/1)100% (3/3)100% (1/1)
nextObjectId (): int 100% (1/1)100% (8/8)100% (1/1)
prepare (String): Prepared 100% (1/1)100% (5/5)100% (1/1)
prepare (String, boolean): Prepared 100% (1/1)100% (12/12)100% (3/3)
prepareCommand (String, int): CommandInterface 100% (1/1)100% (4/4)100% (1/1)
prepareCommit (String): void 100% (1/1)100% (20/20)100% (6/6)
prepareLocal (String): Command 100% (1/1)100% (77/77)100% (20/20)
removeProcedure (String): void 100% (1/1)100% (9/9)100% (3/3)
rollbackToSavepoint (String): void 100% (1/1)100% (26/26)100% (8/8)
setAllCommitted (): void 100% (1/1)100% (7/7)100% (3/3)
setAllowLiterals (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setAutoCommit (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setCommitOrRollbackDisabled (boolean): boolean 100% (1/1)100% (8/8)100% (3/3)
setCurrentCommand (Command): void 100% (1/1)100% (21/21)100% (6/6)
setCurrentSchema (Schema): void 100% (1/1)100% (11/11)100% (3/3)
setLastIdentity (Value): void 100% (1/1)100% (7/7)100% (3/3)
setLastScopeIdentity (Value): void 100% (1/1)100% (4/4)100% (2/2)
setLockTimeout (int): void 100% (1/1)100% (4/4)100% (2/2)
setPowerOffCount (int): void 100% (1/1)100% (5/5)100% (2/2)
setSavepoint (): Session$Savepoint 100% (1/1)100% (19/19)100% (5/5)
setSchemaSearchPath (String []): void 100% (1/1)100% (10/10)100% (3/3)
setThrottle (int): void 100% (1/1)100% (4/4)100% (2/2)
setUndoLogEnabled (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setVariable (String, Value): void 100% (1/1)100% (40/40)100% (10/10)
setWaitForLock (Table, Thread): void 100% (1/1)100% (7/7)100% (3/3)
startStatementWithinTransaction (): void 100% (1/1)100% (4/4)100% (2/2)
toString (): String 100% (1/1)100% (18/18)100% (1/1)
unlinkAtCommitStop (Value): void 100% (1/1)100% (10/10)100% (3/3)
unlock (Table): void 100% (1/1)100% (6/6)100% (2/2)
     
class Session$Savepoint100% (1/1)100% (1/1)100% (3/3)100% (1/1)
Session$Savepoint (): void 100% (1/1)100% (3/3)100% (1/1)

1/*
2 * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
3 * and the EPL 1.0 (http://h2database.com/html/license.html).
4 * Initial Developer: H2 Group
5 */
6package org.h2.engine;
7 
8import java.util.ArrayList;
9import java.util.HashMap;
10import java.util.HashSet;
11import java.util.Iterator;
12import java.util.Random;
13 
14import org.h2.api.ErrorCode;
15import org.h2.command.Command;
16import org.h2.command.CommandInterface;
17import org.h2.command.Parser;
18import org.h2.command.Prepared;
19import org.h2.command.dml.SetTypes;
20import org.h2.constraint.Constraint;
21import org.h2.index.Index;
22import org.h2.jdbc.JdbcConnection;
23import org.h2.message.DbException;
24import org.h2.message.Trace;
25import org.h2.message.TraceSystem;
26import org.h2.mvstore.db.MVTable;
27import org.h2.mvstore.db.TransactionStore.Change;
28import org.h2.mvstore.db.TransactionStore.Transaction;
29import org.h2.result.LocalResult;
30import org.h2.result.Row;
31import org.h2.schema.Schema;
32import org.h2.store.DataHandler;
33import org.h2.store.InDoubtTransaction;
34import org.h2.store.LobStorageFrontend;
35import org.h2.table.Table;
36import org.h2.util.New;
37import org.h2.util.SmallLRUCache;
38import org.h2.value.Value;
39import org.h2.value.ValueArray;
40import org.h2.value.ValueLong;
41import org.h2.value.ValueNull;
42import org.h2.value.ValueString;
43 
44/**
45 * A session represents an embedded database connection. When using the server
46 * mode, this object resides on the server side and communicates with a
47 * SessionRemote object on the client side.
48 */
49public class Session extends SessionWithState {
50 
51    /**
52     * This special log position means that the log entry has been written.
53     */
54    public static final int LOG_WRITTEN = -1;
55 
56    /**
57     * The prefix of generated identifiers. It may not have letters, because
58     * they are case sensitive.
59     */
60    private static final String SYSTEM_IDENTIFIER_PREFIX = "_";
61    private static int nextSerialId;
62 
63    private final int serialId = nextSerialId++;
64    private final Database database;
65    private ConnectionInfo connectionInfo;
66    private final User user;
67    private final int id;
68    private final ArrayList<Table> locks = New.arrayList();
69    private final UndoLog undoLog;
70    private boolean autoCommit = true;
71    private Random random;
72    private int lockTimeout;
73    private Value lastIdentity = ValueLong.get(0);
74    private Value lastScopeIdentity = ValueLong.get(0);
75    private int firstUncommittedLog = Session.LOG_WRITTEN;
76    private int firstUncommittedPos = Session.LOG_WRITTEN;
77    private HashMap<String, Savepoint> savepoints;
78    private HashMap<String, Table> localTempTables;
79    private HashMap<String, Index> localTempTableIndexes;
80    private HashMap<String, Constraint> localTempTableConstraints;
81    private int throttle;
82    private long lastThrottle;
83    private Command currentCommand;
84    private boolean allowLiterals;
85    private String currentSchemaName;
86    private String[] schemaSearchPath;
87    private Trace trace;
88    private HashMap<String, Value> unlinkLobMap;
89    private int systemIdentifier;
90    private HashMap<String, Procedure> procedures;
91    private boolean undoLogEnabled = true;
92    private boolean redoLogBinary = true;
93    private boolean autoCommitAtTransactionEnd;
94    private String currentTransactionName;
95    private volatile long cancelAt;
96    private boolean closed;
97    private final long sessionStart = System.currentTimeMillis();
98    private long transactionStart;
99    private long currentCommandStart;
100    private HashMap<String, Value> variables;
101    private HashSet<LocalResult> temporaryResults;
102    private int queryTimeout;
103    private boolean commitOrRollbackDisabled;
104    private Table waitForLock;
105    private Thread waitForLockThread;
106    private int modificationId;
107    private int objectId;
108    private final int queryCacheSize;
109    private SmallLRUCache<String, Command> queryCache;
110    private long modificationMetaID = -1;
111    private ArrayList<Value> temporaryLobs;
112 
113    private Transaction transaction;
114    private long startStatement = -1;
115 
116    public Session(Database database, User user, int id) {
117        this.database = database;
118        this.queryTimeout = database.getSettings().maxQueryTimeout;
119        this.queryCacheSize = database.getSettings().queryCacheSize;
120        this.undoLog = new UndoLog(this);
121        this.user = user;
122        this.id = id;
123        Setting setting = database.findSetting(
124                SetTypes.getTypeName(SetTypes.DEFAULT_LOCK_TIMEOUT));
125        this.lockTimeout = setting == null ?
126                Constants.INITIAL_LOCK_TIMEOUT : setting.getIntValue();
127        this.currentSchemaName = Constants.SCHEMA_MAIN;
128    }
129 
130    @Override
131    public ArrayList<String> getClusterServers() {
132        return new ArrayList<String>();
133    }
134 
135    public boolean setCommitOrRollbackDisabled(boolean x) {
136        boolean old = commitOrRollbackDisabled;
137        commitOrRollbackDisabled = x;
138        return old;
139    }
140 
141    private void initVariables() {
142        if (variables == null) {
143            variables = database.newStringMap();
144        }
145    }
146 
147    /**
148     * Set the value of the given variable for this session.
149     *
150     * @param name the name of the variable (may not be null)
151     * @param value the new value (may not be null)
152     */
153    public void setVariable(String name, Value value) {
154        initVariables();
155        modificationId++;
156        Value old;
157        if (value == ValueNull.INSTANCE) {
158            old = variables.remove(name);
159        } else {
160            // link LOB values, to make sure we have our own object
161            value = value.link(database,
162                    LobStorageFrontend.TABLE_ID_SESSION_VARIABLE);
163            old = variables.put(name, value);
164        }
165        if (old != null) {
166            // close the old value (in case it is a lob)
167            old.unlink(database);
168            old.close();
169        }
170    }
171 
172    /**
173     * Get the value of the specified user defined variable. This method always
174     * returns a value; it returns ValueNull.INSTANCE if the variable doesn't
175     * exist.
176     *
177     * @param name the variable name
178     * @return the value, or NULL
179     */
180    public Value getVariable(String name) {
181        initVariables();
182        Value v = variables.get(name);
183        return v == null ? ValueNull.INSTANCE : v;
184    }
185 
186    /**
187     * Get the list of variable names that are set for this session.
188     *
189     * @return the list of names
190     */
191    public String[] getVariableNames() {
192        if (variables == null) {
193            return new String[0];
194        }
195        String[] list = new String[variables.size()];
196        variables.keySet().toArray(list);
197        return list;
198    }
199 
200    /**
201     * Get the local temporary table if one exists with that name, or null if
202     * not.
203     *
204     * @param name the table name
205     * @return the table, or null
206     */
207    public Table findLocalTempTable(String name) {
208        if (localTempTables == null) {
209            return null;
210        }
211        return localTempTables.get(name);
212    }
213 
214    public ArrayList<Table> getLocalTempTables() {
215        if (localTempTables == null) {
216            return New.arrayList();
217        }
218        return New.arrayList(localTempTables.values());
219    }
220 
221    /**
222     * Add a local temporary table to this session.
223     *
224     * @param table the table to add
225     * @throws DbException if a table with this name already exists
226     */
227    public void addLocalTempTable(Table table) {
228        if (localTempTables == null) {
229            localTempTables = database.newStringMap();
230        }
231        if (localTempTables.get(table.getName()) != null) {
232            throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1,
233                    table.getSQL());
234        }
235        modificationId++;
236        localTempTables.put(table.getName(), table);
237    }
238 
239    /**
240     * Drop and remove the given local temporary table from this session.
241     *
242     * @param table the table
243     */
244    public void removeLocalTempTable(Table table) {
245        modificationId++;
246        localTempTables.remove(table.getName());
247        synchronized (database) {
248            table.removeChildrenAndResources(this);
249        }
250    }
251 
252    /**
253     * Get the local temporary index if one exists with that name, or null if
254     * not.
255     *
256     * @param name the table name
257     * @return the table, or null
258     */
259    public Index findLocalTempTableIndex(String name) {
260        if (localTempTableIndexes == null) {
261            return null;
262        }
263        return localTempTableIndexes.get(name);
264    }
265 
266    public HashMap<String, Index> getLocalTempTableIndexes() {
267        if (localTempTableIndexes == null) {
268            return New.hashMap();
269        }
270        return localTempTableIndexes;
271    }
272 
273    /**
274     * Add a local temporary index to this session.
275     *
276     * @param index the index to add
277     * @throws DbException if a index with this name already exists
278     */
279    public void addLocalTempTableIndex(Index index) {
280        if (localTempTableIndexes == null) {
281            localTempTableIndexes = database.newStringMap();
282        }
283        if (localTempTableIndexes.get(index.getName()) != null) {
284            throw DbException.get(ErrorCode.INDEX_ALREADY_EXISTS_1,
285                    index.getSQL());
286        }
287        localTempTableIndexes.put(index.getName(), index);
288    }
289 
290    /**
291     * Drop and remove the given local temporary index from this session.
292     *
293     * @param index the index
294     */
295    public void removeLocalTempTableIndex(Index index) {
296        if (localTempTableIndexes != null) {
297            localTempTableIndexes.remove(index.getName());
298            synchronized (database) {
299                index.removeChildrenAndResources(this);
300            }
301        }
302    }
303 
304    /**
305     * Get the local temporary constraint if one exists with that name, or
306     * null if not.
307     *
308     * @param name the constraint name
309     * @return the constraint, or null
310     */
311    public Constraint findLocalTempTableConstraint(String name) {
312        if (localTempTableConstraints == null) {
313            return null;
314        }
315        return localTempTableConstraints.get(name);
316    }
317 
318    /**
319     * Get the map of constraints for all constraints on local, temporary
320     * tables, if any. The map's keys are the constraints' names.
321     *
322     * @return the map of constraints, or null
323     */
324    public HashMap<String, Constraint> getLocalTempTableConstraints() {
325        if (localTempTableConstraints == null) {
326            return New.hashMap();
327        }
328        return localTempTableConstraints;
329    }
330 
331    /**
332     * Add a local temporary constraint to this session.
333     *
334     * @param constraint the constraint to add
335     * @throws DbException if a constraint with the same name already exists
336     */
337    public void addLocalTempTableConstraint(Constraint constraint) {
338        if (localTempTableConstraints == null) {
339            localTempTableConstraints = database.newStringMap();
340        }
341        String name = constraint.getName();
342        if (localTempTableConstraints.get(name) != null) {
343            throw DbException.get(ErrorCode.CONSTRAINT_ALREADY_EXISTS_1,
344                    constraint.getSQL());
345        }
346        localTempTableConstraints.put(name, constraint);
347    }
348 
349    /**
350     * Drop and remove the given local temporary constraint from this session.
351     *
352     * @param constraint the constraint
353     */
354    void removeLocalTempTableConstraint(Constraint constraint) {
355        if (localTempTableConstraints != null) {
356            localTempTableConstraints.remove(constraint.getName());
357            synchronized (database) {
358                constraint.removeChildrenAndResources(this);
359            }
360        }
361    }
362 
363    @Override
364    public boolean getAutoCommit() {
365        return autoCommit;
366    }
367 
368    public User getUser() {
369        return user;
370    }
371 
372    @Override
373    public void setAutoCommit(boolean b) {
374        autoCommit = b;
375    }
376 
377    public int getLockTimeout() {
378        return lockTimeout;
379    }
380 
381    public void setLockTimeout(int lockTimeout) {
382        this.lockTimeout = lockTimeout;
383    }
384 
385    @Override
386    public synchronized CommandInterface prepareCommand(String sql,
387            int fetchSize) {
388        return prepareLocal(sql);
389    }
390 
391    /**
392     * Parse and prepare the given SQL statement. This method also checks the
393     * rights.
394     *
395     * @param sql the SQL statement
396     * @return the prepared statement
397     */
398    public Prepared prepare(String sql) {
399        return prepare(sql, false);
400    }
401 
402    /**
403     * Parse and prepare the given SQL statement.
404     *
405     * @param sql the SQL statement
406     * @param rightsChecked true if the rights have already been checked
407     * @return the prepared statement
408     */
409    public Prepared prepare(String sql, boolean rightsChecked) {
410        Parser parser = new Parser(this);
411        parser.setRightsChecked(rightsChecked);
412        return parser.prepare(sql);
413    }
414 
415    /**
416     * Parse and prepare the given SQL statement.
417     * This method also checks if the connection has been closed.
418     *
419     * @param sql the SQL statement
420     * @return the prepared statement
421     */
422    public Command prepareLocal(String sql) {
423        if (closed) {
424            throw DbException.get(ErrorCode.CONNECTION_BROKEN_1,
425                    "session closed");
426        }
427        Command command;
428        if (queryCacheSize > 0) {
429            if (queryCache == null) {
430                queryCache = SmallLRUCache.newInstance(queryCacheSize);
431                modificationMetaID = database.getModificationMetaId();
432            } else {
433                long newModificationMetaID = database.getModificationMetaId();
434                if (newModificationMetaID != modificationMetaID) {
435                    queryCache.clear();
436                    modificationMetaID = newModificationMetaID;
437                }
438                command = queryCache.get(sql);
439                if (command != null && command.canReuse()) {
440                    command.reuse();
441                    return command;
442                }
443            }
444        }
445        Parser parser = new Parser(this);
446        command = parser.prepareCommand(sql);
447        if (queryCache != null) {
448            if (command.isCacheable()) {
449                queryCache.put(sql, command);
450            }
451        }
452        return command;
453    }
454 
455    public Database getDatabase() {
456        return database;
457    }
458 
459    @Override
460    public int getPowerOffCount() {
461        return database.getPowerOffCount();
462    }
463 
464    @Override
465    public void setPowerOffCount(int count) {
466        database.setPowerOffCount(count);
467    }
468 
469    /**
470     * Commit the current transaction. If the statement was not a data
471     * definition statement, and if there are temporary tables that should be
472     * dropped or truncated at commit, this is done as well.
473     *
474     * @param ddl if the statement was a data definition statement
475     */
476    public void commit(boolean ddl) {
477        checkCommitRollback();
478        currentTransactionName = null;
479        transactionStart = 0;
480        if (transaction != null) {
481            // increment the data mod count, so that other sessions
482            // see the changes
483            // TODO should not rely on locking
484            if (locks.size() > 0) {
485                for (int i = 0, size = locks.size(); i < size; i++) {
486                    Table t = locks.get(i);
487                    if (t instanceof MVTable) {
488                        ((MVTable) t).commit();
489                    }
490                }
491            }
492            transaction.commit();
493            transaction = null;
494        }
495        if (containsUncommitted()) {
496            // need to commit even if rollback is not possible
497            // (create/drop table and so on)
498            database.commit(this);
499        }
500        if (temporaryLobs != null) {
501            for (Value v : temporaryLobs) {
502                if (!v.isLinked()) {
503                    v.close();
504                }
505            }
506            temporaryLobs.clear();
507        }
508        if (undoLog.size() > 0) {
509            // commit the rows when using MVCC
510            if (database.isMultiVersion()) {
511                ArrayList<Row> rows = New.arrayList();
512                synchronized (database) {
513                    while (undoLog.size() > 0) {
514                        UndoLogRecord entry = undoLog.getLast();
515                        entry.commit();
516                        rows.add(entry.getRow());
517                        undoLog.removeLast(false);
518                    }
519                    for (int i = 0, size = rows.size(); i < size; i++) {
520                        Row r = rows.get(i);
521                        r.commit();
522                    }
523                }
524            }
525            undoLog.clear();
526        }
527        if (!ddl) {
528            // do not clean the temp tables if the last command was a
529            // create/drop
530            cleanTempTables(false);
531            if (autoCommitAtTransactionEnd) {
532                autoCommit = true;
533                autoCommitAtTransactionEnd = false;
534            }
535        }
536        endTransaction();
537    }
538 
539    private void checkCommitRollback() {
540        if (commitOrRollbackDisabled && locks.size() > 0) {
541            throw DbException.get(ErrorCode.COMMIT_ROLLBACK_NOT_ALLOWED);
542        }
543    }
544 
545    private void endTransaction() {
546        if (unlinkLobMap != null && unlinkLobMap.size() > 0) {
547            // need to flush the transaction log, because we can't unlink lobs
548            // if the commit record is not written
549            database.flush();
550            for (Value v : unlinkLobMap.values()) {
551                v.unlink(database);
552                v.close();
553            }
554            unlinkLobMap = null;
555        }
556        unlockAll();
557    }
558 
559    /**
560     * Fully roll back the current transaction.
561     */
562    public void rollback() {
563        checkCommitRollback();
564        currentTransactionName = null;
565        boolean needCommit = false;
566        if (undoLog.size() > 0) {
567            rollbackTo(null, false);
568            needCommit = true;
569        }
570        if (transaction != null) {
571            rollbackTo(null, false);
572            needCommit = true;
573            // rollback stored the undo operations in the transaction
574            // committing will end the transaction
575            transaction.commit();
576            transaction = null;
577        }
578        if (locks.size() > 0 || needCommit) {
579            database.commit(this);
580        }
581        cleanTempTables(false);
582        if (autoCommitAtTransactionEnd) {
583            autoCommit = true;
584            autoCommitAtTransactionEnd = false;
585        }
586        endTransaction();
587    }
588 
589    /**
590     * Partially roll back the current transaction.
591     *
592     * @param savepoint the savepoint to which should be rolled back
593     * @param trimToSize if the list should be trimmed
594     */
595    public void rollbackTo(Savepoint savepoint, boolean trimToSize) {
596        int index = savepoint == null ? 0 : savepoint.logIndex;
597        while (undoLog.size() > index) {
598            UndoLogRecord entry = undoLog.getLast();
599            entry.undo(this);
600            undoLog.removeLast(trimToSize);
601        }
602        if (transaction != null) {
603            long savepointId = savepoint == null ? 0 : savepoint.transactionSavepoint;
604            HashMap<String, MVTable> tableMap =
605                    database.getMvStore().getTables();
606            Iterator<Change> it = transaction.getChanges(savepointId);
607            while (it.hasNext()) {
608                Change c = it.next();
609                MVTable t = tableMap.get(c.mapName);
610                if (t != null) {
611                    long key = ((ValueLong) c.key).getLong();
612                    ValueArray value = (ValueArray) c.value;
613                    short op;
614                    Row row;
615                    if (value == null) {
616                        op = UndoLogRecord.INSERT;
617                        row = t.getRow(this, key);
618                    } else {
619                        op = UndoLogRecord.DELETE;
620                        row = new Row(value.getList(), Row.MEMORY_CALCULATE);
621                    }
622                    row.setKey(key);
623                    UndoLogRecord log = new UndoLogRecord(t, op, row);
624                    log.undo(this);
625                }
626            }
627        }
628        if (savepoints != null) {
629            String[] names = new String[savepoints.size()];
630            savepoints.keySet().toArray(names);
631            for (String name : names) {
632                Savepoint sp = savepoints.get(name);
633                int savepointIndex = sp.logIndex;
634                if (savepointIndex > index) {
635                    savepoints.remove(name);
636                }
637            }
638        }
639    }
640 
641    @Override
642    public boolean hasPendingTransaction() {
643        return undoLog.size() > 0;
644    }
645 
646    /**
647     * Create a savepoint to allow rolling back to this state.
648     *
649     * @return the savepoint
650     */
651    public Savepoint setSavepoint() {
652        Savepoint sp = new Savepoint();
653        sp.logIndex = undoLog.size();
654        if (database.getMvStore() != null) {
655            sp.transactionSavepoint = getStatementSavepoint();
656        }
657        return sp;
658    }
659 
660    public int getId() {
661        return id;
662    }
663 
664    @Override
665    public void cancel() {
666        cancelAt = System.currentTimeMillis();
667    }
668 
669    @Override
670    public void close() {
671        if (!closed) {
672            try {
673                database.checkPowerOff();
674                cleanTempTables(true);
675                undoLog.clear();
676                database.removeSession(this);
677            } finally {
678                closed = true;
679            }
680        }
681    }
682 
683    /**
684     * Add a lock for the given table. The object is unlocked on commit or
685     * rollback.
686     *
687     * @param table the table that is locked
688     */
689    public void addLock(Table table) {
690        if (SysProperties.CHECK) {
691            if (locks.contains(table)) {
692                DbException.throwInternalError();
693            }
694        }
695        locks.add(table);
696    }
697 
698    /**
699     * Add an undo log entry to this session.
700     *
701     * @param table the table
702     * @param operation the operation type (see {@link UndoLogRecord})
703     * @param row the row
704     */
705    public void log(Table table, short operation, Row row) {
706        if (table.isMVStore()) {
707            return;
708        }
709        if (undoLogEnabled) {
710            UndoLogRecord log = new UndoLogRecord(table, operation, row);
711            // called _after_ the row was inserted successfully into the table,
712            // otherwise rollback will try to rollback a not-inserted row
713            if (SysProperties.CHECK) {
714                int lockMode = database.getLockMode();
715                if (lockMode != Constants.LOCK_MODE_OFF &&
716                        !database.isMultiVersion()) {
717                    String tableType = log.getTable().getTableType();
718                    if (locks.indexOf(log.getTable()) < 0
719                            && !Table.TABLE_LINK.equals(tableType)
720                            && !Table.EXTERNAL_TABLE_ENGINE.equals(tableType)) {
721                        DbException.throwInternalError();
722                    }
723                }
724            }
725            undoLog.add(log);
726        } else {
727            if (database.isMultiVersion()) {
728                // see also UndoLogRecord.commit
729                ArrayList<Index> indexes = table.getIndexes();
730                for (int i = 0, size = indexes.size(); i < size; i++) {
731                    Index index = indexes.get(i);
732                    index.commit(operation, row);
733                }
734                row.commit();
735            }
736        }
737    }
738 
739    /**
740     * Unlock all read locks. This is done if the transaction isolation mode is
741     * READ_COMMITTED.
742     */
743    public void unlockReadLocks() {
744        if (database.isMultiVersion()) {
745            // MVCC: keep shared locks (insert / update / delete)
746            return;
747        }
748        // locks is modified in the loop
749        for (int i = 0; i < locks.size(); i++) {
750            Table t = locks.get(i);
751            if (!t.isLockedExclusively()) {
752                synchronized (database) {
753                    t.unlock(this);
754                    locks.remove(i);
755                }
756                i--;
757            }
758        }
759    }
760 
761    /**
762     * Unlock just this table.
763     *
764     * @param t the table to unlock
765     */
766    void unlock(Table t) {
767        locks.remove(t);
768    }
769 
770    private void unlockAll() {
771        if (SysProperties.CHECK) {
772            if (undoLog.size() > 0) {
773                DbException.throwInternalError();
774            }
775        }
776        if (locks.size() > 0) {
777            // don't use the enhanced for loop to save memory
778            for (int i = 0, size = locks.size(); i < size; i++) {
779                Table t = locks.get(i);
780                t.unlock(this);
781            }
782            locks.clear();
783        }
784        savepoints = null;
785        sessionStateChanged = true;
786    }
787 
788    private void cleanTempTables(boolean closeSession) {
789        if (localTempTables != null && localTempTables.size() > 0) {
790            synchronized (database) {
791                for (Table table : New.arrayList(localTempTables.values())) {
792                    if (closeSession || table.getOnCommitDrop()) {
793                        modificationId++;
794                        table.setModified();
795                        localTempTables.remove(table.getName());
796                        table.removeChildrenAndResources(this);
797                        if (closeSession) {
798                            // need to commit, otherwise recovery might
799                            // ignore the table removal
800                            database.commit(this);
801                        }
802                    } else if (table.getOnCommitTruncate()) {
803                        table.truncate(this);
804                    }
805                }
806            }
807        }
808    }
809 
810    public Random getRandom() {
811        if (random == null) {
812            random = new Random();
813        }
814        return random;
815    }
816 
817    @Override
818    public Trace getTrace() {
819        if (trace != null && !closed) {
820            return trace;
821        }
822        String traceModuleName = Trace.JDBC + "[" + id + "]";
823        if (closed) {
824            return new TraceSystem(null).getTrace(traceModuleName);
825        }
826        trace = database.getTrace(traceModuleName);
827        return trace;
828    }
829 
830    public void setLastIdentity(Value last) {
831        this.lastIdentity = last;
832        this.lastScopeIdentity = last;
833    }
834 
835    public Value getLastIdentity() {
836        return lastIdentity;
837    }
838 
839    public void setLastScopeIdentity(Value last) {
840        this.lastScopeIdentity = last;
841    }
842 
843    public Value getLastScopeIdentity() {
844        return lastScopeIdentity;
845    }
846 
847    /**
848     * Called when a log entry for this session is added. The session keeps
849     * track of the first entry in the transaction log that is not yet
850     * committed.
851     *
852     * @param logId the transaction log id
853     * @param pos the position of the log entry in the transaction log
854     */
855    public void addLogPos(int logId, int pos) {
856        if (firstUncommittedLog == Session.LOG_WRITTEN) {
857            firstUncommittedLog = logId;
858            firstUncommittedPos = pos;
859        }
860    }
861 
862    public int getFirstUncommittedLog() {
863        return firstUncommittedLog;
864    }
865 
866    /**
867     * This method is called after the transaction log has written the commit
868     * entry for this session.
869     */
870    void setAllCommitted() {
871        firstUncommittedLog = Session.LOG_WRITTEN;
872        firstUncommittedPos = Session.LOG_WRITTEN;
873    }
874 
875    /**
876     * Whether the session contains any uncommitted changes.
877     *
878     * @return true if yes
879     */
880    public boolean containsUncommitted() {
881        if (database.getMvStore() != null) {
882            return transaction != null;
883        }
884        return firstUncommittedLog != Session.LOG_WRITTEN;
885    }
886 
887    /**
888     * Create a savepoint that is linked to the current log position.
889     *
890     * @param name the savepoint name
891     */
892    public void addSavepoint(String name) {
893        if (savepoints == null) {
894            savepoints = database.newStringMap();
895        }
896        Savepoint sp = new Savepoint();
897        sp.logIndex = undoLog.size();
898        if (database.getMvStore() != null) {
899            sp.transactionSavepoint = getStatementSavepoint();
900        }
901        savepoints.put(name, sp);
902    }
903 
904    /**
905     * Undo all operations back to the log position of the given savepoint.
906     *
907     * @param name the savepoint name
908     */
909    public void rollbackToSavepoint(String name) {
910        checkCommitRollback();
911        if (savepoints == null) {
912            throw DbException.get(ErrorCode.SAVEPOINT_IS_INVALID_1, name);
913        }
914        Savepoint savepoint = savepoints.get(name);
915        if (savepoint == null) {
916            throw DbException.get(ErrorCode.SAVEPOINT_IS_INVALID_1, name);
917        }
918        rollbackTo(savepoint, false);
919    }
920 
921    /**
922     * Prepare the given transaction.
923     *
924     * @param transactionName the name of the transaction
925     */
926    public void prepareCommit(String transactionName) {
927        if (transaction != null) {
928            database.prepareCommit(this, transactionName);
929        }
930        if (containsUncommitted()) {
931            // need to commit even if rollback is not possible (create/drop
932            // table and so on)
933            database.prepareCommit(this, transactionName);
934        }
935        currentTransactionName = transactionName;
936    }
937 
938    /**
939     * Commit or roll back the given transaction.
940     *
941     * @param transactionName the name of the transaction
942     * @param commit true for commit, false for rollback
943     */
944    public void setPreparedTransaction(String transactionName, boolean commit) {
945        if (currentTransactionName != null &&
946                currentTransactionName.equals(transactionName)) {
947            if (commit) {
948                commit(false);
949            } else {
950                rollback();
951            }
952        } else {
953            ArrayList<InDoubtTransaction> list = database
954                    .getInDoubtTransactions();
955            int state = commit ? InDoubtTransaction.COMMIT
956                    : InDoubtTransaction.ROLLBACK;
957            boolean found = false;
958            if (list != null) {
959                for (InDoubtTransaction p: list) {
960                    if (p.getTransactionName().equals(transactionName)) {
961                        p.setState(state);
962                        found = true;
963                        break;
964                    }
965                }
966            }
967            if (!found) {
968                throw DbException.get(ErrorCode.TRANSACTION_NOT_FOUND_1,
969                        transactionName);
970            }
971        }
972    }
973 
974    @Override
975    public boolean isClosed() {
976        return closed;
977    }
978 
979    public void setThrottle(int throttle) {
980        this.throttle = throttle;
981    }
982 
983    /**
984     * Wait for some time if this session is throttled (slowed down).
985     */
986    public void throttle() {
987        if (currentCommandStart == 0) {
988            currentCommandStart = System.currentTimeMillis();
989        }
990        if (throttle == 0) {
991            return;
992        }
993        long time = System.currentTimeMillis();
994        if (lastThrottle + Constants.THROTTLE_DELAY > time) {
995            return;
996        }
997        lastThrottle = time + throttle;
998        try {
999            Thread.sleep(throttle);
1000        } catch (Exception e) {
1001            // ignore InterruptedException
1002        }
1003    }
1004 
1005    /**
1006     * Set the current command of this session. This is done just before
1007     * executing the statement.
1008     *
1009     * @param command the command
1010     */
1011    public void setCurrentCommand(Command command) {
1012        this.currentCommand = command;
1013        if (queryTimeout > 0 && command != null) {
1014            long now = System.currentTimeMillis();
1015            currentCommandStart = now;
1016            cancelAt = now + queryTimeout;
1017        }
1018    }
1019 
1020    /**
1021     * Check if the current transaction is canceled by calling
1022     * Statement.cancel() or because a session timeout was set and expired.
1023     *
1024     * @throws DbException if the transaction is canceled
1025     */
1026    public void checkCanceled() {
1027        throttle();
1028        if (cancelAt == 0) {
1029            return;
1030        }
1031        long time = System.currentTimeMillis();
1032        if (time >= cancelAt) {
1033            cancelAt = 0;
1034            throw DbException.get(ErrorCode.STATEMENT_WAS_CANCELED);
1035        }
1036    }
1037 
1038    /**
1039     * Get the cancel time.
1040     *
1041     * @return the time or 0 if not set
1042     */
1043    public long getCancel() {
1044        return cancelAt;
1045    }
1046 
1047    public Command getCurrentCommand() {
1048        return currentCommand;
1049    }
1050 
1051    public long getCurrentCommandStart() {
1052        return currentCommandStart;
1053    }
1054 
1055    public boolean getAllowLiterals() {
1056        return allowLiterals;
1057    }
1058 
1059    public void setAllowLiterals(boolean b) {
1060        this.allowLiterals = b;
1061    }
1062 
1063    public void setCurrentSchema(Schema schema) {
1064        modificationId++;
1065        this.currentSchemaName = schema.getName();
1066    }
1067 
1068    public String getCurrentSchemaName() {
1069        return currentSchemaName;
1070    }
1071 
1072    /**
1073     * Create an internal connection. This connection is used when initializing
1074     * triggers, and when calling user defined functions.
1075     *
1076     * @param columnList if the url should be 'jdbc:columnlist:connection'
1077     * @return the internal connection
1078     */
1079    public JdbcConnection createConnection(boolean columnList) {
1080        String url;
1081        if (columnList) {
1082            url = Constants.CONN_URL_COLUMNLIST;
1083        } else {
1084            url = Constants.CONN_URL_INTERNAL;
1085        }
1086        return new JdbcConnection(this, getUser().getName(), url);
1087    }
1088 
1089    @Override
1090    public DataHandler getDataHandler() {
1091        return database;
1092    }
1093 
1094    /**
1095     * Remember that the given LOB value must be un-linked (disconnected from
1096     * the table) at commit.
1097     *
1098     * @param v the value
1099     */
1100    public void unlinkAtCommit(Value v) {
1101        if (SysProperties.CHECK && !v.isLinked()) {
1102            DbException.throwInternalError();
1103        }
1104        if (unlinkLobMap == null) {
1105            unlinkLobMap = New.hashMap();
1106        }
1107        unlinkLobMap.put(v.toString(), v);
1108    }
1109 
1110    /**
1111     * Do not unlink this LOB value at commit any longer.
1112     *
1113     * @param v the value
1114     */
1115    public void unlinkAtCommitStop(Value v) {
1116        if (unlinkLobMap != null) {
1117            unlinkLobMap.remove(v.toString());
1118        }
1119    }
1120 
1121    /**
1122     * Get the next system generated identifiers. The identifier returned does
1123     * not occur within the given SQL statement.
1124     *
1125     * @param sql the SQL statement
1126     * @return the new identifier
1127     */
1128    public String getNextSystemIdentifier(String sql) {
1129        String identifier;
1130        do {
1131            identifier = SYSTEM_IDENTIFIER_PREFIX + systemIdentifier++;
1132        } while (sql.contains(identifier));
1133        return identifier;
1134    }
1135 
1136    /**
1137     * Add a procedure to this session.
1138     *
1139     * @param procedure the procedure to add
1140     */
1141    public void addProcedure(Procedure procedure) {
1142        if (procedures == null) {
1143            procedures = database.newStringMap();
1144        }
1145        procedures.put(procedure.getName(), procedure);
1146    }
1147 
1148    /**
1149     * Remove a procedure from this session.
1150     *
1151     * @param name the name of the procedure to remove
1152     */
1153    public void removeProcedure(String name) {
1154        if (procedures != null) {
1155            procedures.remove(name);
1156        }
1157    }
1158 
1159    /**
1160     * Get the procedure with the given name, or null
1161     * if none exists.
1162     *
1163     * @param name the procedure name
1164     * @return the procedure or null
1165     */
1166    public Procedure getProcedure(String name) {
1167        if (procedures == null) {
1168            return null;
1169        }
1170        return procedures.get(name);
1171    }
1172 
1173    public void setSchemaSearchPath(String[] schemas) {
1174        modificationId++;
1175        this.schemaSearchPath = schemas;
1176    }
1177 
1178    public String[] getSchemaSearchPath() {
1179        return schemaSearchPath;
1180    }
1181 
1182    @Override
1183    public int hashCode() {
1184        return serialId;
1185    }
1186 
1187    @Override
1188    public String toString() {
1189        return "#" + serialId + " (user: " + user.getName() + ")";
1190    }
1191 
1192    public void setUndoLogEnabled(boolean b) {
1193        this.undoLogEnabled = b;
1194    }
1195 
1196    public void setRedoLogBinary(boolean b) {
1197        this.redoLogBinary = b;
1198    }
1199 
1200    public boolean isUndoLogEnabled() {
1201        return undoLogEnabled;
1202    }
1203 
1204    /**
1205     * Begin a transaction.
1206     */
1207    public void begin() {
1208        autoCommitAtTransactionEnd = true;
1209        autoCommit = false;
1210    }
1211 
1212    public long getSessionStart() {
1213        return sessionStart;
1214    }
1215 
1216    public long getTransactionStart() {
1217        if (transactionStart == 0) {
1218            transactionStart = System.currentTimeMillis();
1219        }
1220        return transactionStart;
1221    }
1222 
1223    public Table[] getLocks() {
1224        // copy the data without synchronizing
1225        ArrayList<Table> copy = New.arrayList();
1226        for (int i = 0; i < locks.size(); i++) {
1227            try {
1228                copy.add(locks.get(i));
1229            } catch (Exception e) {
1230                // ignore
1231                break;
1232            }
1233        }
1234        Table[] list = new Table[copy.size()];
1235        copy.toArray(list);
1236        return list;
1237    }
1238 
1239    /**
1240     * Wait if the exclusive mode has been enabled for another session. This
1241     * method returns as soon as the exclusive mode has been disabled.
1242     */
1243    public void waitIfExclusiveModeEnabled() {
1244        // Even in exclusive mode, we have to let the LOB session proceed, or we
1245        // will get deadlocks.
1246        if (database.getLobSession() == this) {
1247            return;
1248        }
1249        while (true) {
1250            Session exclusive = database.getExclusiveSession();
1251            if (exclusive == null || exclusive == this) {
1252                break;
1253            }
1254            if (Thread.holdsLock(exclusive)) {
1255                // if another connection is used within the connection
1256                break;
1257            }
1258            try {
1259                Thread.sleep(100);
1260            } catch (InterruptedException e) {
1261                // ignore
1262            }
1263        }
1264    }
1265 
1266    /**
1267     * Remember the result set and close it as soon as the transaction is
1268     * committed (if it needs to be closed). This is done to delete temporary
1269     * files as soon as possible, and free object ids of temporary tables.
1270     *
1271     * @param result the temporary result set
1272     */
1273    public void addTemporaryResult(LocalResult result) {
1274        if (!result.needToClose()) {
1275            return;
1276        }
1277        if (temporaryResults == null) {
1278            temporaryResults = New.hashSet();
1279        }
1280        if (temporaryResults.size() < 100) {
1281            // reference at most 100 result sets to avoid memory problems
1282            temporaryResults.add(result);
1283        }
1284    }
1285 
1286    private void closeTemporaryResults() {
1287        if (temporaryResults != null) {
1288            for (LocalResult result : temporaryResults) {
1289                result.close();
1290            }
1291            temporaryResults = null;
1292        }
1293    }
1294 
1295    public void setQueryTimeout(int queryTimeout) {
1296        int max = database.getSettings().maxQueryTimeout;
1297        if (max != 0 && (max < queryTimeout || queryTimeout == 0)) {
1298            // the value must be at most max
1299            queryTimeout = max;
1300        }
1301        this.queryTimeout = queryTimeout;
1302        // must reset the cancel at here,
1303        // otherwise it is still used
1304        this.cancelAt = 0;
1305    }
1306 
1307    public int getQueryTimeout() {
1308        return queryTimeout;
1309    }
1310 
1311    /**
1312     * Set the table this session is waiting for, and the thread that is
1313     * waiting.
1314     *
1315     * @param waitForLock the table
1316     * @param waitForLockThread the current thread (the one that is waiting)
1317     */
1318    public void setWaitForLock(Table waitForLock, Thread waitForLockThread) {
1319        this.waitForLock = waitForLock;
1320        this.waitForLockThread = waitForLockThread;
1321    }
1322 
1323    public Table getWaitForLock() {
1324        return waitForLock;
1325    }
1326 
1327    public Thread getWaitForLockThread() {
1328        return waitForLockThread;
1329    }
1330 
1331    public int getModificationId() {
1332        return modificationId;
1333    }
1334 
1335    @Override
1336    public boolean isReconnectNeeded(boolean write) {
1337        while (true) {
1338            boolean reconnect = database.isReconnectNeeded();
1339            if (reconnect) {
1340                return true;
1341            }
1342            if (write) {
1343                if (database.beforeWriting()) {
1344                    return false;
1345                }
1346            } else {
1347                return false;
1348            }
1349        }
1350    }
1351 
1352    @Override
1353    public void afterWriting() {
1354        database.afterWriting();
1355    }
1356 
1357    @Override
1358    public SessionInterface reconnect(boolean write) {
1359        readSessionState();
1360        close();
1361        Session newSession = Engine.getInstance().createSession(connectionInfo);
1362        newSession.sessionState = sessionState;
1363        newSession.recreateSessionState();
1364        if (write) {
1365            while (!newSession.database.beforeWriting()) {
1366                // wait until we are allowed to write
1367            }
1368        }
1369        return newSession;
1370    }
1371 
1372    public void setConnectionInfo(ConnectionInfo ci) {
1373        connectionInfo = ci;
1374    }
1375 
1376    public Value getTransactionId() {
1377        if (database.getMvStore() != null) {
1378            if (transaction == null) {
1379                return ValueNull.INSTANCE;
1380            }
1381            return ValueString.get(Long.toString(getTransaction().getId()));
1382        }
1383        if (!database.isPersistent()) {
1384            return ValueNull.INSTANCE;
1385        }
1386        if (undoLog.size() == 0) {
1387            return ValueNull.INSTANCE;
1388        }
1389        return ValueString.get(firstUncommittedLog + "-" + firstUncommittedPos +
1390                "-" + id);
1391    }
1392 
1393    /**
1394     * Get the next object id.
1395     *
1396     * @return the next object id
1397     */
1398    public int nextObjectId() {
1399        return objectId++;
1400    }
1401 
1402    public boolean isRedoLogBinaryEnabled() {
1403        return redoLogBinary;
1404    }
1405 
1406    /**
1407     * Get the transaction to use for this session.
1408     *
1409     * @return the transaction
1410     */
1411    public Transaction getTransaction() {
1412        if (transaction == null) {
1413            if (database.getMvStore().getStore().isClosed()) {
1414                database.shutdownImmediately();
1415                throw DbException.get(ErrorCode.DATABASE_IS_CLOSED);
1416            }
1417            transaction = database.getMvStore().getTransactionStore().begin();
1418            startStatement = -1;
1419        }
1420        return transaction;
1421    }
1422 
1423    public long getStatementSavepoint() {
1424        if (startStatement == -1) {
1425            startStatement = getTransaction().setSavepoint();
1426        }
1427        return startStatement;
1428    }
1429 
1430    /**
1431     * Start a new statement within a transaction.
1432     */
1433    public void startStatementWithinTransaction() {
1434        startStatement = -1;
1435    }
1436 
1437    /**
1438     * Mark the statement as completed. This also close all temporary result
1439     * set, and deletes all temporary files held by the result sets.
1440     */
1441    public void endStatement() {
1442        startStatement = -1;
1443        closeTemporaryResults();
1444    }
1445 
1446    @Override
1447    public void addTemporaryLob(Value v) {
1448        if (temporaryLobs == null) {
1449            temporaryLobs = new ArrayList<Value>();
1450        }
1451        temporaryLobs.add(v);
1452    }
1453 
1454    /**
1455     * Represents a savepoint (a position in a transaction to where one can roll
1456     * back to).
1457     */
1458    public static class Savepoint {
1459 
1460        /**
1461         * The undo log index.
1462         */
1463        int logIndex;
1464 
1465        /**
1466         * The transaction savepoint id.
1467         */
1468        long transactionSavepoint;
1469    }
1470 
1471}

[all classes][org.h2.engine]
EMMA 2.0.5312 (C) Vladimir Roubtsov