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

COVERAGE SUMMARY FOR SOURCE FILE [Database.java]

nameclass, %method, %block, %line, %
Database.java100% (1/1)96%  (171/179)77%  (3792/4903)79%  (976.5/1243)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class Database100% (1/1)96%  (171/179)77%  (3792/4903)79%  (976.5/1243)
checkpointIfRequired (): void 0%   (0/1)0%   (0/99)0%   (0/22)
flushSequences (): void 0%   (0/1)0%   (0/19)0%   (0/5)
getLobCompressionAlgorithm (int): String 0%   (0/1)0%   (0/3)0%   (0/1)
getLobFileListCache (): SmallLRUCache 0%   (0/1)0%   (0/10)0%   (0/3)
getSessionCount (): int 0%   (0/1)0%   (0/4)0%   (0/1)
readLob (long, byte [], long, byte [], int, int): int 0%   (0/1)0%   (0/2)0%   (0/1)
setReadOnly (boolean): void 0%   (0/1)0%   (0/4)0%   (0/2)
toString (): String 0%   (0/1)0%   (0/13)0%   (0/1)
isReconnectNeeded (): boolean 100% (1/1)5%   (6/123)7%   (2/28)
reconnectModified (boolean): boolean 100% (1/1)6%   (12/204)5%   (2/42)
beforeWriting (): boolean 100% (1/1)9%   (6/65)12%  (2/16)
afterWriting (): void 100% (1/1)16%  (5/32)25%  (2/8)
setLogMode (int): void 100% (1/1)32%  (15/47)50%  (5/10)
exceptionThrown (SQLException, String): void 100% (1/1)36%  (4/11)40%  (2/5)
setMultiThreaded (boolean): void 100% (1/1)37%  (10/27)43%  (3/7)
shutdownImmediately (): void 100% (1/1)40%  (4/10)50%  (3/6)
getInDoubtTransactions (): ArrayList 100% (1/1)44%  (7/16)67%  (2/3)
prepareCommit (Session, String): void 100% (1/1)48%  (12/25)44%  (4/9)
createTempFile (): String 100% (1/1)50%  (15/30)57%  (4/7)
setMaxLogSize (long): void 100% (1/1)50%  (4/8)67%  (2/3)
setLockMode (int): void 100% (1/1)53%  (10/19)71%  (5/7)
setWriteDelay (int): void 100% (1/1)56%  (19/34)73%  (5.8/8)
openDatabase (int, int, boolean): void 100% (1/1)62%  (40/65)69%  (13.9/20)
setEventListenerClass (String): void 100% (1/1)64%  (35/55)73%  (8/11)
checkWritingAllowed (): void 100% (1/1)65%  (11/17)67%  (4/6)
getSchema (String): Schema 100% (1/1)67%  (8/12)75%  (3/4)
getUser (String): User 100% (1/1)67%  (8/12)75%  (3/4)
flush (): void 100% (1/1)68%  (17/25)64%  (7/11)
closeOpenFilesAndUnlock (boolean): void 100% (1/1)68%  (118/173)66%  (32.1/49)
closeAllSessionsException (Session): void 100% (1/1)71%  (35/49)80%  (8/10)
renameDatabaseObject (Session, DbObject, String): void 100% (1/1)73%  (56/77)88%  (15/17)
setJavaObjectSerializerName (String): void 100% (1/1)74%  (14/19)94%  (4.7/5)
setQueryStatistics (boolean): void 100% (1/1)74%  (14/19)94%  (4.7/5)
closeFiles (): void 100% (1/1)74%  (17/23)78%  (7/9)
checkpoint (): void 100% (1/1)74%  (23/31)82%  (7.4/9)
isFileLockSerialized (): boolean 100% (1/1)75%  (6/8)75%  (0.8/1)
sync (): void 100% (1/1)76%  (13/17)71%  (5/7)
initJavaObjectSerializer (): void 100% (1/1)77%  (40/52)78%  (12.5/16)
getMap (int): HashMap 100% (1/1)78%  (36/46)95%  (18/19)
close (boolean): void 100% (1/1)80%  (198/248)75%  (55.5/74)
removeDatabaseObject (Session, DbObject): void 100% (1/1)82%  (47/57)93%  (14/15)
removeOrphanedLobs (): void 100% (1/1)83%  (35/42)75%  (9/12)
validateFilePasswordHash (String, byte []): boolean 100% (1/1)83%  (10/12)67%  (2/3)
getQueryStatisticsData (): QueryStatisticsData 100% (1/1)84%  (26/31)92%  (7.4/8)
checkPowerOff (): void 100% (1/1)85%  (62/73)78%  (21/27)
openFile (String, String, boolean): FileStore 100% (1/1)86%  (24/28)89%  (8/9)
setCacheSize (int): void 100% (1/1)86%  (30/35)89%  (8/9)
verifyMetaLocked (Session): void 100% (1/1)86%  (12/14)67%  (2/3)
getLogMode (): int 100% (1/1)87%  (13/15)80%  (4/5)
open (int, int): void 100% (1/1)88%  (647/735)87%  (118.7/136)
initMetaTables (): void 100% (1/1)89%  (42/47)94%  (10.4/11)
getSessions (boolean): Session [] 100% (1/1)90%  (44/49)98%  (11.7/12)
exists (String): boolean 100% (1/1)91%  (20/22)67%  (2/3)
checkMetaFree (Session, int): void 100% (1/1)91%  (21/23)83%  (5/6)
setProgress (int, String, int, int): void 100% (1/1)92%  (12/13)80%  (4/5)
getPageStore (): PageStore 100% (1/1)92%  (61/66)92%  (12/13)
getAllSchemaObjects (int): ArrayList 100% (1/1)93%  (25/27)86%  (6/7)
updateMetaAndFirstLevelChildren (Session, DbObject): void 100% (1/1)94%  (34/36)91%  (10/11)
startServer (String): void 100% (1/1)95%  (74/78)83%  (10/12)
addDatabaseObject (Session, DbObject): void 100% (1/1)95%  (59/62)93%  (14/15)
getFirstUserTable (): Table 100% (1/1)96%  (23/24)86%  (6/7)
recompileInvalidViews (Session): void 100% (1/1)97%  (63/65)94%  (16/17)
removeMeta (Session, int): void 100% (1/1)97%  (75/77)95%  (19/20)
Database (ConnectionInfo, String): void 100% (1/1)99%  (289/292)99%  (70/71)
removeSession (Session): void 100% (1/1)99%  (109/110)94%  (17/18)
addMeta (Session, DbObject): void 100% (1/1)100% (47/47)100% (12/12)
addSchemaObject (Session, SchemaObject): void 100% (1/1)100% (23/23)100% (7/7)
allocateObjectId (): int 100% (1/1)100% (11/11)100% (3/3)
areEqual (Value, Value): boolean 100% (1/1)100% (10/10)100% (1/1)
commit (Session): void 100% (1/1)100% (16/16)100% (7/7)
compare (Value, Value): int 100% (1/1)100% (6/6)100% (1/1)
compareTypeSave (Value, Value): int 100% (1/1)100% (6/6)100% (1/1)
createSession (User): Session 100% (1/1)100% (52/52)100% (9/9)
deleteOldTempFiles (): void 100% (1/1)100% (29/29)100% (6/6)
equalsIdentifiers (String, String): boolean 100% (1/1)100% (21/21)100% (5/5)
findAggregate (String): UserAggregate 100% (1/1)100% (6/6)100% (1/1)
findComment (DbObject): Comment 100% (1/1)100% (15/15)100% (4/4)
findRole (String): Role 100% (1/1)100% (6/6)100% (1/1)
findSchema (String): Schema 100% (1/1)100% (14/14)100% (4/4)
findSetting (String): Setting 100% (1/1)100% (6/6)100% (1/1)
findUser (String): User 100% (1/1)100% (6/6)100% (1/1)
findUserDataType (String): UserDataType 100% (1/1)100% (6/6)100% (1/1)
getAllAggregates (): ArrayList 100% (1/1)100% (5/5)100% (1/1)
getAllComments (): ArrayList 100% (1/1)100% (5/5)100% (1/1)
getAllRights (): ArrayList 100% (1/1)100% (5/5)100% (1/1)
getAllRoles (): ArrayList 100% (1/1)100% (5/5)100% (1/1)
getAllSchemaObjects (): ArrayList 100% (1/1)100% (24/24)100% (6/6)
getAllSchemas (): ArrayList 100% (1/1)100% (7/7)100% (2/2)
getAllSettings (): ArrayList 100% (1/1)100% (5/5)100% (1/1)
getAllTablesAndViews (boolean): ArrayList 100% (1/1)100% (26/26)100% (7/7)
getAllUserDataTypes (): ArrayList 100% (1/1)100% (5/5)100% (1/1)
getAllUsers (): ArrayList 100% (1/1)100% (5/5)100% (1/1)
getAllowLiterals (): int 100% (1/1)100% (8/8)100% (3/3)
getCacheType (): String 100% (1/1)100% (3/3)100% (1/1)
getCluster (): String 100% (1/1)100% (3/3)100% (1/1)
getCompareMode (): CompareMode 100% (1/1)100% (3/3)100% (1/1)
getCompiler (): SourceCompiler 100% (1/1)100% (11/11)100% (3/3)
getDatabasePath (): String 100% (1/1)100% (9/9)100% (3/3)
getDefaultTableType (): int 100% (1/1)100% (3/3)100% (1/1)
getDependentTable (SchemaObject, Table): Table 100% (1/1)100% (43/43)100% (14/14)
getExclusiveSession (): Session 100% (1/1)100% (3/3)100% (1/1)
getFileEncryptionKey (): byte [] 100% (1/1)100% (3/3)100% (1/1)
getFlushOnEachCommit (): boolean 100% (1/1)100% (3/3)100% (1/1)
getIgnoreCase (): boolean 100% (1/1)100% (8/8)100% (3/3)
getJavaObjectSerializer (): JavaObjectSerializer 100% (1/1)100% (5/5)100% (2/2)
getLinkConnection (String, String, String, String): TableLinkConnection 100% (1/1)100% (17/17)100% (3/3)
getLobConnectionForInit (): JdbcConnection 100% (1/1)100% (17/17)100% (4/4)
getLobConnectionForRegularUse (): JdbcConnection 100% (1/1)100% (17/17)100% (4/4)
getLobSession (): Session 100% (1/1)100% (3/3)100% (1/1)
getLobStorage (): LobStorageInterface 100% (1/1)100% (23/23)100% (5/5)
getLobSyncObject (): Object 100% (1/1)100% (3/3)100% (1/1)
getLockMode (): int 100% (1/1)100% (3/3)100% (1/1)
getMaxLengthInplaceLob (): int 100% (1/1)100% (3/3)100% (1/1)
getMaxMemoryRows (): int 100% (1/1)100% (3/3)100% (1/1)
getMaxMemoryUndo (): int 100% (1/1)100% (3/3)100% (1/1)
getMaxOperationMemory (): int 100% (1/1)100% (3/3)100% (1/1)
getMode (): Mode 100% (1/1)100% (3/3)100% (1/1)
getModificationDataId (): long 100% (1/1)100% (3/3)100% (1/1)
getModificationMetaId (): long 100% (1/1)100% (3/3)100% (1/1)
getMvStore (): MVTableEngine$Store 100% (1/1)100% (3/3)100% (1/1)
getName (): String 100% (1/1)100% (3/3)100% (1/1)
getNextModificationDataId (): long 100% (1/1)100% (8/8)100% (1/1)
getNextModificationMetaId (): long 100% (1/1)100% (14/14)100% (2/2)
getOptimizeReuseResults (): boolean 100% (1/1)100% (3/3)100% (1/1)
getPageSize (): int 100% (1/1)100% (3/3)100% (1/1)
getPowerOffCount (): int 100% (1/1)100% (3/3)100% (1/1)
getPublicRole (): Role 100% (1/1)100% (3/3)100% (1/1)
getQueryStatistics (): boolean 100% (1/1)100% (3/3)100% (1/1)
getReferentialIntegrity (): boolean 100% (1/1)100% (3/3)100% (1/1)
getRetentionTime (): int 100% (1/1)100% (3/3)100% (1/1)
getSettings (): DbSettings 100% (1/1)100% (3/3)100% (1/1)
getShortName (): String 100% (1/1)100% (3/3)100% (1/1)
getSystemSession (): Session 100% (1/1)100% (3/3)100% (1/1)
getTempFileDeleter (): TempFileDeleter 100% (1/1)100% (3/3)100% (1/1)
getTempTableName (String, Session): String 100% (1/1)100% (30/30)100% (3/3)
getTrace (String): Trace 100% (1/1)100% (5/5)100% (1/1)
getTraceSystem (): TraceSystem 100% (1/1)100% (3/3)100% (1/1)
isClosing (): boolean 100% (1/1)100% (3/3)100% (1/1)
isMultiThreaded (): boolean 100% (1/1)100% (3/3)100% (1/1)
isMultiVersion (): boolean 100% (1/1)100% (3/3)100% (1/1)
isPersistent (): boolean 100% (1/1)100% (3/3)100% (1/1)
isReadOnly (): boolean 100% (1/1)100% (3/3)100% (1/1)
isStarting (): boolean 100% (1/1)100% (3/3)100% (1/1)
isSysTableLocked (): boolean 100% (1/1)100% (11/11)100% (1/1)
lockMeta (Session): boolean 100% (1/1)100% (14/14)100% (4/4)
newStringMap (): HashMap 100% (1/1)100% (12/12)100% (1/1)
opened (): void 100% (1/1)100% (13/13)100% (5/5)
parseDatabaseShortName (): String 100% (1/1)100% (40/40)100% (10/10)
removeSchemaObject (Session, SchemaObject): void 100% (1/1)100% (118/118)100% (33/33)
renameSchemaObject (Session, SchemaObject, String): void 100% (1/1)100% (12/12)100% (4/4)
setAllowLiterals (int): void 100% (1/1)100% (4/4)100% (2/2)
setBackgroundException (DbException): void 100% (1/1)100% (18/18)100% (6/6)
setCloseDelay (int): void 100% (1/1)100% (4/4)100% (2/2)
setCluster (String): void 100% (1/1)100% (4/4)100% (2/2)
setCompactMode (int): void 100% (1/1)100% (4/4)100% (2/2)
setCompareMode (CompareMode): void 100% (1/1)100% (4/4)100% (2/2)
setDefaultTableType (int): void 100% (1/1)100% (4/4)100% (2/2)
setDeleteFilesOnDisconnect (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setEventListener (DatabaseEventListener): void 100% (1/1)100% (4/4)100% (2/2)
setExclusiveSession (Session, boolean): void 100% (1/1)100% (9/9)100% (4/4)
setIgnoreCase (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setInitialPowerOffCount (int): void 100% (1/1)100% (3/3)100% (2/2)
setLobCompressionAlgorithm (String): void 100% (1/1)100% (4/4)100% (2/2)
setMasterUser (User): void 100% (1/1)100% (15/15)100% (4/4)
setMaxLengthInplaceLob (int): void 100% (1/1)100% (4/4)100% (2/2)
setMaxMemoryRows (int): void 100% (1/1)100% (4/4)100% (2/2)
setMaxMemoryUndo (int): void 100% (1/1)100% (4/4)100% (2/2)
setMaxOperationMemory (int): void 100% (1/1)100% (4/4)100% (2/2)
setMode (Mode): void 100% (1/1)100% (4/4)100% (2/2)
setMultiVersion (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setMvStore (MVTableEngine$Store): void 100% (1/1)100% (9/9)100% (3/3)
setOptimizeReuseResults (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setPowerOffCount (int): void 100% (1/1)100% (9/9)100% (4/4)
setReferentialIntegrity (boolean): void 100% (1/1)100% (4/4)100% (2/2)
setRetentionTime (int): void 100% (1/1)100% (12/12)100% (4/4)
stopServer (): void 100% (1/1)100% (12/12)100% (5/5)
stopWriter (): void 100% (1/1)100% (10/10)100% (4/4)
throwLastBackgroundException (): void 100% (1/1)100% (14/14)100% (6/6)
updateMeta (Session, DbObject): void 100% (1/1)100% (16/16)100% (5/5)

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.io.IOException;
9import java.sql.SQLException;
10import java.util.ArrayList;
11import java.util.Collections;
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.Properties;
15import java.util.Set;
16import java.util.StringTokenizer;
17import org.h2.api.DatabaseEventListener;
18import org.h2.api.ErrorCode;
19import org.h2.api.JavaObjectSerializer;
20import org.h2.command.CommandInterface;
21import org.h2.command.ddl.CreateTableData;
22import org.h2.command.dml.SetTypes;
23import org.h2.constraint.Constraint;
24import org.h2.index.Cursor;
25import org.h2.index.Index;
26import org.h2.index.IndexType;
27import org.h2.jdbc.JdbcConnection;
28import org.h2.message.DbException;
29import org.h2.message.Trace;
30import org.h2.message.TraceSystem;
31import org.h2.mvstore.db.MVTableEngine;
32import org.h2.result.Row;
33import org.h2.result.SearchRow;
34import org.h2.schema.Schema;
35import org.h2.schema.SchemaObject;
36import org.h2.schema.Sequence;
37import org.h2.schema.TriggerObject;
38import org.h2.store.DataHandler;
39import org.h2.store.FileLock;
40import org.h2.store.FileStore;
41import org.h2.store.InDoubtTransaction;
42import org.h2.store.LobStorageBackend;
43import org.h2.store.LobStorageFrontend;
44import org.h2.store.LobStorageInterface;
45import org.h2.store.LobStorageMap;
46import org.h2.store.PageStore;
47import org.h2.store.WriterThread;
48import org.h2.store.fs.FileUtils;
49import org.h2.table.Column;
50import org.h2.table.IndexColumn;
51import org.h2.table.MetaTable;
52import org.h2.table.Table;
53import org.h2.table.TableLinkConnection;
54import org.h2.table.TableView;
55import org.h2.tools.DeleteDbFiles;
56import org.h2.tools.Server;
57import org.h2.util.BitField;
58import org.h2.util.JdbcUtils;
59import org.h2.util.MathUtils;
60import org.h2.util.NetUtils;
61import org.h2.util.New;
62import org.h2.util.SmallLRUCache;
63import org.h2.util.SourceCompiler;
64import org.h2.util.StringUtils;
65import org.h2.util.TempFileDeleter;
66import org.h2.util.Utils;
67import org.h2.value.CaseInsensitiveMap;
68import org.h2.value.CompareMode;
69import org.h2.value.Value;
70import org.h2.value.ValueInt;
71 
72/**
73 * There is one database object per open database.
74 *
75 * The format of the meta data table is:
76 *  id int, 0, objectType int, sql varchar
77 *
78 * @since 2004-04-15 22:49
79 */
80public class Database implements DataHandler {
81 
82    private static int initialPowerOffCount;
83 
84    /**
85     * The default name of the system user. This name is only used as long as
86     * there is no administrator user registered.
87     */
88    private static final String SYSTEM_USER_NAME = "DBA";
89 
90    private final boolean persistent;
91    private final String databaseName;
92    private final String databaseShortName;
93    private final String databaseURL;
94    private final String cipher;
95    private final byte[] filePasswordHash;
96    private final byte[] fileEncryptionKey;
97 
98    private final HashMap<String, Role> roles = New.hashMap();
99    private final HashMap<String, User> users = New.hashMap();
100    private final HashMap<String, Setting> settings = New.hashMap();
101    private final HashMap<String, Schema> schemas = New.hashMap();
102    private final HashMap<String, Right> rights = New.hashMap();
103    private final HashMap<String, UserDataType> userDataTypes = New.hashMap();
104    private final HashMap<String, UserAggregate> aggregates = New.hashMap();
105    private final HashMap<String, Comment> comments = New.hashMap();
106 
107    private final Set<Session> userSessions =
108            Collections.synchronizedSet(new HashSet<Session>());
109    private Session exclusiveSession;
110    private final BitField objectIds = new BitField();
111    private final Object lobSyncObject = new Object();
112 
113    private Schema mainSchema;
114    private Schema infoSchema;
115    private int nextSessionId;
116    private int nextTempTableId;
117    private User systemUser;
118    private Session systemSession;
119    private Session lobSession;
120    private Table meta;
121    private Index metaIdIndex;
122    private FileLock lock;
123    private WriterThread writer;
124    private boolean starting;
125    private TraceSystem traceSystem;
126    private Trace trace;
127    private final int fileLockMethod;
128    private Role publicRole;
129    private long modificationDataId;
130    private long modificationMetaId;
131    private CompareMode compareMode;
132    private String cluster = Constants.CLUSTERING_DISABLED;
133    private boolean readOnly;
134    private int writeDelay = Constants.DEFAULT_WRITE_DELAY;
135    private DatabaseEventListener eventListener;
136    private int maxMemoryRows = SysProperties.MAX_MEMORY_ROWS;
137    private int maxMemoryUndo = Constants.DEFAULT_MAX_MEMORY_UNDO;
138    private int lockMode = Constants.DEFAULT_LOCK_MODE;
139    private int maxLengthInplaceLob;
140    private int allowLiterals = Constants.ALLOW_LITERALS_ALL;
141 
142    private int powerOffCount = initialPowerOffCount;
143    private int closeDelay;
144    private DatabaseCloser delayedCloser;
145    private volatile boolean closing;
146    private boolean ignoreCase;
147    private boolean deleteFilesOnDisconnect;
148    private String lobCompressionAlgorithm;
149    private boolean optimizeReuseResults = true;
150    private final String cacheType;
151    private final String accessModeData;
152    private boolean referentialIntegrity = true;
153    private boolean multiVersion;
154    private DatabaseCloser closeOnExit;
155    private Mode mode = Mode.getInstance(Mode.REGULAR);
156    private boolean multiThreaded;
157    private int maxOperationMemory =
158            Constants.DEFAULT_MAX_OPERATION_MEMORY;
159    private SmallLRUCache<String, String[]> lobFileListCache;
160    private final boolean autoServerMode;
161    private final int autoServerPort;
162    private Server server;
163    private HashMap<TableLinkConnection, TableLinkConnection> linkConnections;
164    private final TempFileDeleter tempFileDeleter = TempFileDeleter.getInstance();
165    private PageStore pageStore;
166    private Properties reconnectLastLock;
167    private volatile long reconnectCheckNext;
168    private volatile boolean reconnectChangePending;
169    private volatile int checkpointAllowed;
170    private volatile boolean checkpointRunning;
171    private final Object reconnectSync = new Object();
172    private int cacheSize;
173    private int compactMode;
174    private SourceCompiler compiler;
175    private volatile boolean metaTablesInitialized;
176    private boolean flushOnEachCommit;
177    private LobStorageInterface lobStorage;
178    private final int pageSize;
179    private int defaultTableType = Table.TYPE_CACHED;
180    private final DbSettings dbSettings;
181    private final int reconnectCheckDelay;
182    private int logMode;
183    private MVTableEngine.Store mvStore;
184    private int retentionTime;
185    private DbException backgroundException;
186    private JavaObjectSerializer javaObjectSerializer;
187    private String javaObjectSerializerName;
188    private volatile boolean javaObjectSerializerInitialized;
189    private boolean queryStatistics;
190    private QueryStatisticsData queryStatisticsData;
191 
192    public Database(ConnectionInfo ci, String cipher) {
193        String name = ci.getName();
194        this.dbSettings = ci.getDbSettings();
195        this.reconnectCheckDelay = dbSettings.reconnectCheckDelay;
196        this.compareMode = CompareMode.getInstance(null, 0);
197        this.persistent = ci.isPersistent();
198        this.filePasswordHash = ci.getFilePasswordHash();
199        this.fileEncryptionKey = ci.getFileEncryptionKey();
200        this.databaseName = name;
201        this.databaseShortName = parseDatabaseShortName();
202        this.maxLengthInplaceLob = Constants.DEFAULT_MAX_LENGTH_INPLACE_LOB;
203        this.cipher = cipher;
204        String lockMethodName = ci.getProperty("FILE_LOCK", null);
205        this.accessModeData = StringUtils.toLowerEnglish(
206                ci.getProperty("ACCESS_MODE_DATA", "rw"));
207        this.autoServerMode = ci.getProperty("AUTO_SERVER", false);
208        this.autoServerPort = ci.getProperty("AUTO_SERVER_PORT", 0);
209        int defaultCacheSize = Utils.scaleForAvailableMemory(
210                Constants.CACHE_SIZE_DEFAULT);
211        this.cacheSize =
212                ci.getProperty("CACHE_SIZE", defaultCacheSize);
213        this.pageSize = ci.getProperty("PAGE_SIZE",
214                Constants.DEFAULT_PAGE_SIZE);
215        if ("r".equals(accessModeData)) {
216            readOnly = true;
217        }
218        if (dbSettings.mvStore && lockMethodName == null) {
219            if (autoServerMode) {
220                fileLockMethod = FileLock.LOCK_FILE;
221            } else {
222                fileLockMethod = FileLock.LOCK_FS;
223            }
224        } else {
225            fileLockMethod = FileLock.getFileLockMethod(lockMethodName);
226        }
227        if (dbSettings.mvStore && fileLockMethod == FileLock.LOCK_SERIALIZED) {
228            throw DbException.getUnsupportedException(
229                    "MV_STORE combined with FILE_LOCK=SERIALIZED");
230        }
231        this.databaseURL = ci.getURL();
232        String listener = ci.removeProperty("DATABASE_EVENT_LISTENER", null);
233        if (listener != null) {
234            listener = StringUtils.trim(listener, true, true, "'");
235            setEventListenerClass(listener);
236        }
237        String modeName = ci.removeProperty("MODE", null);
238        if (modeName != null) {
239            this.mode = Mode.getInstance(modeName);
240        }
241        this.multiVersion =
242                ci.getProperty("MVCC", dbSettings.mvStore);
243        this.logMode =
244                ci.getProperty("LOG", PageStore.LOG_MODE_SYNC);
245        this.javaObjectSerializerName =
246                ci.getProperty("JAVA_OBJECT_SERIALIZER", null);
247        this.multiThreaded =
248                ci.getProperty("MULTI_THREADED", false);
249 
250        boolean closeAtVmShutdown =
251                dbSettings.dbCloseOnExit;
252        int traceLevelFile =
253                ci.getIntProperty(SetTypes.TRACE_LEVEL_FILE,
254                TraceSystem.DEFAULT_TRACE_LEVEL_FILE);
255        int traceLevelSystemOut =
256                ci.getIntProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT,
257                TraceSystem.DEFAULT_TRACE_LEVEL_SYSTEM_OUT);
258        this.cacheType = StringUtils.toUpperEnglish(
259                ci.removeProperty("CACHE_TYPE", Constants.CACHE_TYPE_DEFAULT));
260        openDatabase(traceLevelFile, traceLevelSystemOut, closeAtVmShutdown);
261    }
262 
263    private void openDatabase(int traceLevelFile, int traceLevelSystemOut,
264            boolean closeAtVmShutdown) {
265        try {
266            open(traceLevelFile, traceLevelSystemOut);
267            if (closeAtVmShutdown) {
268                try {
269                    closeOnExit = new DatabaseCloser(this, 0, true);
270                    Runtime.getRuntime().addShutdownHook(closeOnExit);
271                } catch (IllegalStateException e) {
272                    // shutdown in progress - just don't register the handler
273                    // (maybe an application wants to write something into a
274                    // database at shutdown time)
275                } catch (SecurityException  e) {
276                    // applets may not do that - ignore
277                    // Google App Engine doesn't allow
278                    // to instantiate classes that extend Thread
279                }
280            }
281        } catch (Throwable e) {
282            if (e instanceof OutOfMemoryError) {
283                e.fillInStackTrace();
284            }
285            if (traceSystem != null) {
286                if (e instanceof SQLException) {
287                    SQLException e2 = (SQLException) e;
288                    if (e2.getErrorCode() != ErrorCode.
289                            DATABASE_ALREADY_OPEN_1) {
290                        // only write if the database is not already in use
291                        trace.error(e, "opening {0}", databaseName);
292                    }
293                }
294                traceSystem.close();
295            }
296            closeOpenFilesAndUnlock(false);
297            throw DbException.convert(e);
298        }
299    }
300 
301    public static void setInitialPowerOffCount(int count) {
302        initialPowerOffCount = count;
303    }
304 
305    public void setPowerOffCount(int count) {
306        if (powerOffCount == -1) {
307            return;
308        }
309        powerOffCount = count;
310    }
311 
312    public MVTableEngine.Store getMvStore() {
313        return mvStore;
314    }
315 
316    public void setMvStore(MVTableEngine.Store mvStore) {
317        this.mvStore = mvStore;
318        this.retentionTime = mvStore.getStore().getRetentionTime();
319    }
320 
321    /**
322     * Check if two values are equal with the current comparison mode.
323     *
324     * @param a the first value
325     * @param b the second value
326     * @return true if both objects are equal
327     */
328    public boolean areEqual(Value a, Value b) {
329        // can not use equals because ValueDecimal 0.0 is not equal to 0.00.
330        return a.compareTo(b, compareMode) == 0;
331    }
332 
333    /**
334     * Compare two values with the current comparison mode. The values may not
335     * be of the same type.
336     *
337     * @param a the first value
338     * @param b the second value
339     * @return 0 if both values are equal, -1 if the first value is smaller, and
340     *         1 otherwise
341     */
342    public int compare(Value a, Value b) {
343        return a.compareTo(b, compareMode);
344    }
345 
346    /**
347     * Compare two values with the current comparison mode. The values must be
348     * of the same type.
349     *
350     * @param a the first value
351     * @param b the second value
352     * @return 0 if both values are equal, -1 if the first value is smaller, and
353     *         1 otherwise
354     */
355    public int compareTypeSave(Value a, Value b) {
356        return a.compareTypeSave(b, compareMode);
357    }
358 
359    public long getModificationDataId() {
360        return modificationDataId;
361    }
362 
363    /**
364     * Set or reset the pending change flag in the .lock.db file.
365     *
366     * @param pending the new value of the flag
367     * @return true if the call was successful,
368     *          false if another connection was faster
369     */
370    private synchronized boolean reconnectModified(boolean pending) {
371        if (readOnly || lock == null ||
372                fileLockMethod != FileLock.LOCK_SERIALIZED) {
373            return true;
374        }
375        try {
376            if (pending == reconnectChangePending) {
377                long now = System.currentTimeMillis();
378                if (now > reconnectCheckNext) {
379                    if (pending) {
380                        String pos = pageStore == null ?
381                                null : "" + pageStore.getWriteCountTotal();
382                        lock.setProperty("logPos", pos);
383                        lock.save();
384                    }
385                    reconnectCheckNext = now + reconnectCheckDelay;
386                }
387                return true;
388            }
389            Properties old = lock.load();
390            if (pending) {
391                if (old.getProperty("changePending") != null) {
392                    return false;
393                }
394                trace.debug("wait before writing");
395                Thread.sleep((int) (reconnectCheckDelay * 1.1));
396                Properties now = lock.load();
397                if (!now.equals(old)) {
398                    // somebody else was faster
399                    return false;
400                }
401            }
402            String pos = pageStore == null ?
403                    null : "" + pageStore.getWriteCountTotal();
404            lock.setProperty("logPos", pos);
405            if (pending) {
406                lock.setProperty("changePending", "true-" + Math.random());
407            } else {
408                lock.setProperty("changePending", null);
409            }
410            // ensure that the writer thread will
411            // not reset the flag before we are done
412            reconnectCheckNext = System.currentTimeMillis() +
413                    2 * reconnectCheckDelay;
414            old = lock.save();
415            if (pending) {
416                trace.debug("wait before writing again");
417                Thread.sleep((int) (reconnectCheckDelay * 1.1));
418                Properties now = lock.load();
419                if (!now.equals(old)) {
420                    // somebody else was faster
421                    return false;
422                }
423            } else {
424                Thread.sleep(1);
425            }
426            reconnectLastLock = old;
427            reconnectChangePending = pending;
428            reconnectCheckNext = System.currentTimeMillis() +
429                    reconnectCheckDelay;
430            return true;
431        } catch (Exception e) {
432            trace.error(e, "pending {0}", pending);
433            return false;
434        }
435    }
436 
437    public long getNextModificationDataId() {
438        return ++modificationDataId;
439    }
440 
441    public long getModificationMetaId() {
442        return modificationMetaId;
443    }
444 
445    public long getNextModificationMetaId() {
446        // if the meta data has been modified, the data is modified as well
447        // (because MetaTable returns modificationDataId)
448        modificationDataId++;
449        return modificationMetaId++;
450    }
451 
452    public int getPowerOffCount() {
453        return powerOffCount;
454    }
455 
456    @Override
457    public void checkPowerOff() {
458        if (powerOffCount == 0) {
459            return;
460        }
461        if (powerOffCount > 1) {
462            powerOffCount--;
463            return;
464        }
465        if (powerOffCount != -1) {
466            try {
467                powerOffCount = -1;
468                stopWriter();
469                if (mvStore != null) {
470                    mvStore.closeImmediately();
471                }
472                if (pageStore != null) {
473                    try {
474                        pageStore.close();
475                    } catch (DbException e) {
476                        // ignore
477                    }
478                    pageStore = null;
479                }
480                if (lock != null) {
481                    stopServer();
482                    if (fileLockMethod != FileLock.LOCK_SERIALIZED) {
483                        // allow testing shutdown
484                        lock.unlock();
485                    }
486                    lock = null;
487                }
488                if (traceSystem != null) {
489                    traceSystem.close();
490                }
491            } catch (DbException e) {
492                DbException.traceThrowable(e);
493            }
494        }
495        Engine.getInstance().close(databaseName);
496        throw DbException.get(ErrorCode.DATABASE_IS_CLOSED);
497    }
498 
499    /**
500     * Check if a database with the given name exists.
501     *
502     * @param name the name of the database (including path)
503     * @return true if one exists
504     */
505    static boolean exists(String name) {
506        if (FileUtils.exists(name + Constants.SUFFIX_PAGE_FILE)) {
507            return true;
508        }
509        return FileUtils.exists(name + Constants.SUFFIX_MV_FILE);
510    }
511 
512    /**
513     * Get the trace object for the given module.
514     *
515     * @param module the module name
516     * @return the trace object
517     */
518    public Trace getTrace(String module) {
519        return traceSystem.getTrace(module);
520    }
521 
522    @Override
523    public FileStore openFile(String name, String openMode, boolean mustExist) {
524        if (mustExist && !FileUtils.exists(name)) {
525            throw DbException.get(ErrorCode.FILE_NOT_FOUND_1, name);
526        }
527        FileStore store = FileStore.open(this, name, openMode, cipher,
528                filePasswordHash);
529        try {
530            store.init();
531        } catch (DbException e) {
532            store.closeSilently();
533            throw e;
534        }
535        return store;
536    }
537 
538    /**
539     * Check if the file password hash is correct.
540     *
541     * @param testCipher the cipher algorithm
542     * @param testHash the hash code
543     * @return true if the cipher algorithm and the password match
544     */
545    boolean validateFilePasswordHash(String testCipher, byte[] testHash) {
546        if (!StringUtils.equals(testCipher, this.cipher)) {
547            return false;
548        }
549        return Utils.compareSecure(testHash, filePasswordHash);
550    }
551 
552    private String parseDatabaseShortName() {
553        String n = databaseName;
554        if (n.endsWith(":")) {
555            n = null;
556        }
557        if (n != null) {
558            StringTokenizer tokenizer = new StringTokenizer(n, "/\\:,;");
559            while (tokenizer.hasMoreTokens()) {
560                n = tokenizer.nextToken();
561            }
562        }
563        if (n == null || n.length() == 0) {
564            n = "unnamed";
565        }
566        return dbSettings.databaseToUpper ? StringUtils.toUpperEnglish(n) : n;
567    }
568 
569    private synchronized void open(int traceLevelFile, int traceLevelSystemOut) {
570        if (persistent) {
571            String dataFileName = databaseName + Constants.SUFFIX_OLD_DATABASE_FILE;
572            boolean existsData = FileUtils.exists(dataFileName);
573            String pageFileName = databaseName + Constants.SUFFIX_PAGE_FILE;
574            String mvFileName = databaseName + Constants.SUFFIX_MV_FILE;
575            boolean existsPage = FileUtils.exists(pageFileName);
576            boolean existsMv = FileUtils.exists(mvFileName);
577            if (existsData && (!existsPage && !existsMv)) {
578                throw DbException.get(
579                        ErrorCode.FILE_VERSION_ERROR_1, "Old database: " +
580                        dataFileName +
581                        " - please convert the database " +
582                        "to a SQL script and re-create it.");
583            }
584            if (existsPage && !FileUtils.canWrite(pageFileName)) {
585                readOnly = true;
586            }
587            if (existsMv && !FileUtils.canWrite(mvFileName)) {
588                readOnly = true;
589            }
590            if (existsPage && !existsMv) {
591                dbSettings.mvStore = false;
592            }
593            if (readOnly) {
594                if (traceLevelFile >= TraceSystem.DEBUG) {
595                    String traceFile = Utils.getProperty("java.io.tmpdir", ".") +
596                            "/" + "h2_" + System.currentTimeMillis();
597                    traceSystem = new TraceSystem(traceFile +
598                            Constants.SUFFIX_TRACE_FILE);
599                } else {
600                    traceSystem = new TraceSystem(null);
601                }
602            } else {
603                traceSystem = new TraceSystem(databaseName +
604                        Constants.SUFFIX_TRACE_FILE);
605            }
606            traceSystem.setLevelFile(traceLevelFile);
607            traceSystem.setLevelSystemOut(traceLevelSystemOut);
608            trace = traceSystem.getTrace(Trace.DATABASE);
609            trace.info("opening {0} (build {1})", databaseName, Constants.BUILD_ID);
610            if (autoServerMode) {
611                if (readOnly ||
612                        fileLockMethod == FileLock.LOCK_NO ||
613                        fileLockMethod == FileLock.LOCK_SERIALIZED ||
614                        fileLockMethod == FileLock.LOCK_FS ||
615                        !persistent) {
616                    throw DbException.getUnsupportedException(
617                            "autoServerMode && (readOnly || fileLockMethod == NO" +
618                            " || fileLockMethod == SERIALIZED || inMemory)");
619                }
620            }
621            String lockFileName = databaseName + Constants.SUFFIX_LOCK_FILE;
622            if (readOnly) {
623                if (FileUtils.exists(lockFileName)) {
624                    throw DbException.get(ErrorCode.DATABASE_ALREADY_OPEN_1,
625                            "Lock file exists: " + lockFileName);
626                }
627            }
628            if (!readOnly && fileLockMethod != FileLock.LOCK_NO) {
629                if (fileLockMethod != FileLock.LOCK_FS) {
630                    lock = new FileLock(traceSystem, lockFileName, Constants.LOCK_SLEEP);
631                    lock.lock(fileLockMethod);
632                    if (autoServerMode) {
633                        startServer(lock.getUniqueId());
634                    }
635                }
636            }
637            if (SysProperties.MODIFY_ON_WRITE) {
638                while (isReconnectNeeded()) {
639                    // wait until others stopped writing
640                }
641            } else {
642                while (isReconnectNeeded() && !beforeWriting()) {
643                    // wait until others stopped writing and
644                    // until we can write (the file is not yet open -
645                    // no need to re-connect)
646                }
647            }
648            deleteOldTempFiles();
649            starting = true;
650            if (SysProperties.MODIFY_ON_WRITE) {
651                try {
652                    getPageStore();
653                } catch (DbException e) {
654                    if (e.getErrorCode() != ErrorCode.DATABASE_IS_READ_ONLY) {
655                        throw e;
656                    }
657                    pageStore = null;
658                    while (!beforeWriting()) {
659                        // wait until others stopped writing and
660                        // until we can write (the file is not yet open -
661                        // no need to re-connect)
662                    }
663                    getPageStore();
664                }
665            } else {
666                getPageStore();
667            }
668            starting = false;
669            if (mvStore == null) {
670                writer = WriterThread.create(this, writeDelay);
671            } else {
672                setWriteDelay(writeDelay);
673            }
674        } else {
675            if (autoServerMode) {
676                throw DbException.getUnsupportedException(
677                        "autoServerMode && inMemory");
678            }
679            traceSystem = new TraceSystem(null);
680            trace = traceSystem.getTrace(Trace.DATABASE);
681            if (dbSettings.mvStore) {
682                getPageStore();
683            }
684        }
685        systemUser = new User(this, 0, SYSTEM_USER_NAME, true);
686        mainSchema = new Schema(this, 0, Constants.SCHEMA_MAIN, systemUser, true);
687        infoSchema = new Schema(this, -1, "INFORMATION_SCHEMA", systemUser, true);
688        schemas.put(mainSchema.getName(), mainSchema);
689        schemas.put(infoSchema.getName(), infoSchema);
690        publicRole = new Role(this, 0, Constants.PUBLIC_ROLE_NAME, true);
691        roles.put(Constants.PUBLIC_ROLE_NAME, publicRole);
692        systemUser.setAdmin(true);
693        systemSession = new Session(this, systemUser, ++nextSessionId);
694        lobSession = new Session(this, systemUser, ++nextSessionId);
695        CreateTableData data = new CreateTableData();
696        ArrayList<Column> cols = data.columns;
697        Column columnId = new Column("ID", Value.INT);
698        columnId.setNullable(false);
699        cols.add(columnId);
700        cols.add(new Column("HEAD", Value.INT));
701        cols.add(new Column("TYPE", Value.INT));
702        cols.add(new Column("SQL", Value.STRING));
703        boolean create = true;
704        if (pageStore != null) {
705            create = pageStore.isNew();
706        }
707        data.tableName = "SYS";
708        data.id = 0;
709        data.temporary = false;
710        data.persistData = persistent;
711        data.persistIndexes = persistent;
712        data.create = create;
713        data.isHidden = true;
714        data.session = systemSession;
715        meta = mainSchema.createTable(data);
716        IndexColumn[] pkCols = IndexColumn.wrap(new Column[] { columnId });
717        metaIdIndex = meta.addIndex(systemSession, "SYS_ID",
718                0, pkCols, IndexType.createPrimaryKey(
719                false, false), true, null);
720        objectIds.set(0);
721        starting = true;
722        Cursor cursor = metaIdIndex.find(systemSession, null, null);
723        ArrayList<MetaRecord> records = New.arrayList();
724        while (cursor.next()) {
725            MetaRecord rec = new MetaRecord(cursor.get());
726            objectIds.set(rec.getId());
727            records.add(rec);
728        }
729        Collections.sort(records);
730        synchronized (systemSession) {
731            for (MetaRecord rec : records) {
732                rec.execute(this, systemSession, eventListener);
733            }
734        }
735        if (mvStore != null) {
736            mvStore.initTransactions();
737            mvStore.removeTemporaryMaps(objectIds);
738        }
739        recompileInvalidViews(systemSession);
740        starting = false;
741        if (!readOnly) {
742            // set CREATE_BUILD in a new database
743            String name = SetTypes.getTypeName(SetTypes.CREATE_BUILD);
744            if (settings.get(name) == null) {
745                Setting setting = new Setting(this, allocateObjectId(), name);
746                setting.setIntValue(Constants.BUILD_ID);
747                lockMeta(systemSession);
748                addDatabaseObject(systemSession, setting);
749            }
750            // mark all ids used in the page store
751            if (pageStore != null) {
752                BitField f = pageStore.getObjectIds();
753                for (int i = 0, len = f.length(); i < len; i++) {
754                    if (f.get(i) && !objectIds.get(i)) {
755                        trace.info("unused object id: " + i);
756                        objectIds.set(i);
757                    }
758                }
759            }
760        }
761        getLobStorage().init();
762        systemSession.commit(true);
763 
764        trace.info("opened {0}", databaseName);
765        if (checkpointAllowed > 0) {
766            afterWriting();
767        }
768    }
769 
770    private void startServer(String key) {
771        try {
772            server = Server.createTcpServer(
773                    "-tcpPort", Integer.toString(autoServerPort),
774                    "-tcpAllowOthers",
775                    "-tcpDaemon",
776                    "-key", key, databaseName);
777            server.start();
778        } catch (SQLException e) {
779            throw DbException.convert(e);
780        }
781        String localAddress = NetUtils.getLocalAddress();
782        String address = localAddress + ":" + server.getPort();
783        lock.setProperty("server", address);
784        String hostName = NetUtils.getHostName(localAddress);
785        lock.setProperty("hostName", hostName);
786        lock.save();
787    }
788 
789    private void stopServer() {
790        if (server != null) {
791            Server s = server;
792            // avoid calling stop recursively
793            // because stopping the server will
794            // try to close the database as well
795            server = null;
796            s.stop();
797        }
798    }
799 
800    private void recompileInvalidViews(Session session) {
801        boolean recompileSuccessful;
802        do {
803            recompileSuccessful = false;
804            for (Table obj : getAllTablesAndViews(false)) {
805                if (obj instanceof TableView) {
806                    TableView view = (TableView) obj;
807                    if (view.isInvalid()) {
808                        view.recompile(session, true);
809                        if (!view.isInvalid()) {
810                            recompileSuccessful = true;
811                        }
812                    }
813                }
814            }
815        } while (recompileSuccessful);
816        // when opening a database, views are initialized before indexes,
817        // so they may not have the optimal plan yet
818        // this is not a problem, it is just nice to see the newest plan
819        for (Table obj : getAllTablesAndViews(false)) {
820            if (obj instanceof TableView) {
821                TableView view = (TableView) obj;
822                if (!view.isInvalid()) {
823                    view.recompile(systemSession, true);
824                }
825            }
826        }
827    }
828 
829    private void initMetaTables() {
830        if (metaTablesInitialized) {
831            return;
832        }
833        synchronized (infoSchema) {
834            if (!metaTablesInitialized) {
835                for (int type = 0, count = MetaTable.getMetaTableTypeCount();
836                        type < count; type++) {
837                    MetaTable m = new MetaTable(infoSchema, -1 - type, type);
838                    infoSchema.add(m);
839                }
840                metaTablesInitialized = true;
841            }
842        }
843    }
844 
845    private synchronized void addMeta(Session session, DbObject obj) {
846        int id = obj.getId();
847        if (id > 0 && !starting && !obj.isTemporary()) {
848            Row r = meta.getTemplateRow();
849            MetaRecord rec = new MetaRecord(obj);
850            rec.setRecord(r);
851            objectIds.set(id);
852            if (SysProperties.CHECK) {
853                verifyMetaLocked(session);
854            }
855            meta.addRow(session, r);
856            if (isMultiVersion()) {
857                // TODO this should work without MVCC, but avoid risks at the
858                // moment
859                session.log(meta, UndoLogRecord.INSERT, r);
860            }
861        }
862    }
863 
864    /**
865     * Verify the meta table is locked.
866     *
867     * @param session the session
868     */
869    public void verifyMetaLocked(Session session) {
870        if (meta != null && !meta.isLockedExclusivelyBy(session)
871                && lockMode != Constants.LOCK_MODE_OFF) {
872            throw DbException.throwInternalError();
873        }
874    }
875 
876    /**
877     * Lock the metadata table for updates.
878     *
879     * @param session the session
880     * @return whether it was already locked before by this session
881     */
882    public boolean lockMeta(Session session) {
883        // this method can not be synchronized on the database object,
884        // as unlocking is also synchronized on the database object -
885        // so if locking starts just before unlocking, locking could
886        // never be successful
887        if (meta == null) {
888            return true;
889        }
890        boolean wasLocked = meta.lock(session, true, true);
891        return wasLocked;
892    }
893 
894    /**
895     * Remove the given object from the meta data.
896     *
897     * @param session the session
898     * @param id the id of the object to remove
899     */
900    public synchronized void removeMeta(Session session, int id) {
901        if (id > 0 && !starting) {
902            SearchRow r = meta.getTemplateSimpleRow(false);
903            r.setValue(0, ValueInt.get(id));
904            boolean wasLocked = lockMeta(session);
905            Cursor cursor = metaIdIndex.find(session, r, r);
906            if (cursor.next()) {
907                if (SysProperties.CHECK) {
908                    if (lockMode != Constants.LOCK_MODE_OFF && !wasLocked) {
909                        throw DbException.throwInternalError();
910                    }
911                }
912                Row found = cursor.get();
913                meta.removeRow(session, found);
914                if (isMultiVersion()) {
915                    // TODO this should work without MVCC, but avoid risks at
916                    // the moment
917                    session.log(meta, UndoLogRecord.DELETE, found);
918                }
919                objectIds.clear(id);
920                if (SysProperties.CHECK) {
921                    checkMetaFree(session, id);
922                }
923            } else if (!wasLocked) {
924                // must not keep the lock if it was not locked
925                // otherwise updating sequences may cause a deadlock
926                meta.unlock(session);
927                session.unlock(meta);
928            }
929        }
930    }
931 
932    @SuppressWarnings("unchecked")
933    private HashMap<String, DbObject> getMap(int type) {
934        HashMap<String, ? extends DbObject> result;
935        switch (type) {
936        case DbObject.USER:
937            result = users;
938            break;
939        case DbObject.SETTING:
940            result = settings;
941            break;
942        case DbObject.ROLE:
943            result = roles;
944            break;
945        case DbObject.RIGHT:
946            result = rights;
947            break;
948        case DbObject.SCHEMA:
949            result = schemas;
950            break;
951        case DbObject.USER_DATATYPE:
952            result = userDataTypes;
953            break;
954        case DbObject.COMMENT:
955            result = comments;
956            break;
957        case DbObject.AGGREGATE:
958            result = aggregates;
959            break;
960        default:
961            throw DbException.throwInternalError("type=" + type);
962        }
963        return (HashMap<String, DbObject>) result;
964    }
965 
966    /**
967     * Add a schema object to the database.
968     *
969     * @param session the session
970     * @param obj the object to add
971     */
972    public synchronized void addSchemaObject(Session session, SchemaObject obj) {
973        int id = obj.getId();
974        if (id > 0 && !starting) {
975            checkWritingAllowed();
976        }
977        lockMeta(session);
978        obj.getSchema().add(obj);
979        addMeta(session, obj);
980    }
981 
982    /**
983     * Add an object to the database.
984     *
985     * @param session the session
986     * @param obj the object to add
987     */
988    public synchronized void addDatabaseObject(Session session, DbObject obj) {
989        int id = obj.getId();
990        if (id > 0 && !starting) {
991            checkWritingAllowed();
992        }
993        HashMap<String, DbObject> map = getMap(obj.getType());
994        if (obj.getType() == DbObject.USER) {
995            User user = (User) obj;
996            if (user.isAdmin() && systemUser.getName().equals(SYSTEM_USER_NAME)) {
997                systemUser.rename(user.getName());
998            }
999        }
1000        String name = obj.getName();
1001        if (SysProperties.CHECK && map.get(name) != null) {
1002            DbException.throwInternalError("object already exists");
1003        }
1004        lockMeta(session);
1005        addMeta(session, obj);
1006        map.put(name, obj);
1007    }
1008 
1009    /**
1010     * Get the user defined aggregate function if it exists, or null if not.
1011     *
1012     * @param name the name of the user defined aggregate function
1013     * @return the aggregate function or null
1014     */
1015    public UserAggregate findAggregate(String name) {
1016        return aggregates.get(name);
1017    }
1018 
1019    /**
1020     * Get the comment for the given database object if one exists, or null if
1021     * not.
1022     *
1023     * @param object the database object
1024     * @return the comment or null
1025     */
1026    public Comment findComment(DbObject object) {
1027        if (object.getType() == DbObject.COMMENT) {
1028            return null;
1029        }
1030        String key = Comment.getKey(object);
1031        return comments.get(key);
1032    }
1033 
1034    /**
1035     * Get the role if it exists, or null if not.
1036     *
1037     * @param roleName the name of the role
1038     * @return the role or null
1039     */
1040    public Role findRole(String roleName) {
1041        return roles.get(roleName);
1042    }
1043 
1044    /**
1045     * Get the schema if it exists, or null if not.
1046     *
1047     * @param schemaName the name of the schema
1048     * @return the schema or null
1049     */
1050    public Schema findSchema(String schemaName) {
1051        Schema schema = schemas.get(schemaName);
1052        if (schema == infoSchema) {
1053            initMetaTables();
1054        }
1055        return schema;
1056    }
1057 
1058    /**
1059     * Get the setting if it exists, or null if not.
1060     *
1061     * @param name the name of the setting
1062     * @return the setting or null
1063     */
1064    public Setting findSetting(String name) {
1065        return settings.get(name);
1066    }
1067 
1068    /**
1069     * Get the user if it exists, or null if not.
1070     *
1071     * @param name the name of the user
1072     * @return the user or null
1073     */
1074    public User findUser(String name) {
1075        return users.get(name);
1076    }
1077 
1078    /**
1079     * Get the user defined data type if it exists, or null if not.
1080     *
1081     * @param name the name of the user defined data type
1082     * @return the user defined data type or null
1083     */
1084    public UserDataType findUserDataType(String name) {
1085        return userDataTypes.get(name);
1086    }
1087 
1088    /**
1089     * Get user with the given name. This method throws an exception if the user
1090     * does not exist.
1091     *
1092     * @param name the user name
1093     * @return the user
1094     * @throws DbException if the user does not exist
1095     */
1096    public User getUser(String name) {
1097        User user = findUser(name);
1098        if (user == null) {
1099            throw DbException.get(ErrorCode.USER_NOT_FOUND_1, name);
1100        }
1101        return user;
1102    }
1103 
1104    /**
1105     * Create a session for the given user.
1106     *
1107     * @param user the user
1108     * @return the session
1109     * @throws DbException if the database is in exclusive mode
1110     */
1111    synchronized Session createSession(User user) {
1112        if (exclusiveSession != null) {
1113            throw DbException.get(ErrorCode.DATABASE_IS_IN_EXCLUSIVE_MODE);
1114        }
1115        Session session = new Session(this, user, ++nextSessionId);
1116        userSessions.add(session);
1117        trace.info("connecting session #{0} to {1}", session.getId(), databaseName);
1118        if (delayedCloser != null) {
1119            delayedCloser.reset();
1120            delayedCloser = null;
1121        }
1122        return session;
1123    }
1124 
1125    /**
1126     * Remove a session. This method is called after the user has disconnected.
1127     *
1128     * @param session the session
1129     */
1130    public synchronized void removeSession(Session session) {
1131        if (session != null) {
1132            if (exclusiveSession == session) {
1133                exclusiveSession = null;
1134            }
1135            userSessions.remove(session);
1136            if (session != systemSession && session != lobSession) {
1137                trace.info("disconnecting session #{0}", session.getId());
1138            }
1139        }
1140        if (userSessions.size() == 0 &&
1141                session != systemSession && session != lobSession) {
1142            if (closeDelay == 0) {
1143                close(false);
1144            } else if (closeDelay < 0) {
1145                return;
1146            } else {
1147                delayedCloser = new DatabaseCloser(this, closeDelay * 1000, false);
1148                delayedCloser.setName("H2 Close Delay " + getShortName());
1149                delayedCloser.setDaemon(true);
1150                delayedCloser.start();
1151            }
1152        }
1153        if (session != systemSession &&
1154                session != lobSession && session != null) {
1155            trace.info("disconnected session #{0}", session.getId());
1156        }
1157    }
1158 
1159    private synchronized void closeAllSessionsException(Session except) {
1160        Session[] all = new Session[userSessions.size()];
1161        userSessions.toArray(all);
1162        for (Session s : all) {
1163            if (s != except) {
1164                try {
1165                    // must roll back, otherwise the session is removed and
1166                    // the transaction log that contains its uncommitted
1167                    // operations as well
1168                    s.rollback();
1169                    s.close();
1170                } catch (DbException e) {
1171                    trace.error(e, "disconnecting session #{0}", s.getId());
1172                }
1173            }
1174        }
1175    }
1176 
1177    /**
1178     * Close the database.
1179     *
1180     * @param fromShutdownHook true if this method is called from the shutdown
1181     *            hook
1182     */
1183    synchronized void close(boolean fromShutdownHook) {
1184        if (closing) {
1185            return;
1186        }
1187        throwLastBackgroundException();
1188        if (fileLockMethod == FileLock.LOCK_SERIALIZED &&
1189                !reconnectChangePending) {
1190            // another connection may have written something - don't write
1191            try {
1192                closeOpenFilesAndUnlock(false);
1193            } catch (DbException e) {
1194                // ignore
1195            }
1196            traceSystem.close();
1197            Engine.getInstance().close(databaseName);
1198            return;
1199        }
1200        closing = true;
1201        stopServer();
1202        if (userSessions.size() > 0) {
1203            if (!fromShutdownHook) {
1204                return;
1205            }
1206            trace.info("closing {0} from shutdown hook", databaseName);
1207            closeAllSessionsException(null);
1208        }
1209        trace.info("closing {0}", databaseName);
1210        if (eventListener != null) {
1211            // allow the event listener to connect to the database
1212            closing = false;
1213            DatabaseEventListener e = eventListener;
1214            // set it to null, to make sure it's called only once
1215            eventListener = null;
1216            e.closingDatabase();
1217            if (userSessions.size() > 0) {
1218                // if a connection was opened, we can't close the database
1219                return;
1220            }
1221            closing = true;
1222        }
1223        removeOrphanedLobs();
1224        try {
1225            if (systemSession != null) {
1226                if (powerOffCount != -1) {
1227                    for (Table table : getAllTablesAndViews(false)) {
1228                        if (table.isGlobalTemporary()) {
1229                            table.removeChildrenAndResources(systemSession);
1230                        } else {
1231                            table.close(systemSession);
1232                        }
1233                    }
1234                    for (SchemaObject obj : getAllSchemaObjects(
1235                            DbObject.SEQUENCE)) {
1236                        Sequence sequence = (Sequence) obj;
1237                        sequence.close();
1238                    }
1239                }
1240                for (SchemaObject obj : getAllSchemaObjects(
1241                        DbObject.TRIGGER)) {
1242                    TriggerObject trigger = (TriggerObject) obj;
1243                    try {
1244                        trigger.close();
1245                    } catch (SQLException e) {
1246                        trace.error(e, "close");
1247                    }
1248                }
1249                if (powerOffCount != -1) {
1250                    meta.close(systemSession);
1251                    systemSession.commit(true);
1252                }
1253            }
1254        } catch (DbException e) {
1255            trace.error(e, "close");
1256        }
1257        tempFileDeleter.deleteAll();
1258        try {
1259            closeOpenFilesAndUnlock(true);
1260        } catch (DbException e) {
1261            trace.error(e, "close");
1262        }
1263        trace.info("closed");
1264        traceSystem.close();
1265        if (closeOnExit != null) {
1266            closeOnExit.reset();
1267            try {
1268                Runtime.getRuntime().removeShutdownHook(closeOnExit);
1269            } catch (IllegalStateException e) {
1270                // ignore
1271            } catch (SecurityException  e) {
1272                // applets may not do that - ignore
1273            }
1274            closeOnExit = null;
1275        }
1276        Engine.getInstance().close(databaseName);
1277        if (deleteFilesOnDisconnect && persistent) {
1278            deleteFilesOnDisconnect = false;
1279            try {
1280                String directory = FileUtils.getParent(databaseName);
1281                String name = FileUtils.getName(databaseName);
1282                DeleteDbFiles.execute(directory, name, true);
1283            } catch (Exception e) {
1284                // ignore (the trace is closed already)
1285            }
1286        }
1287    }
1288 
1289    private void removeOrphanedLobs() {
1290        // remove all session variables and temporary lobs
1291        if (!persistent) {
1292            return;
1293        }
1294        boolean lobStorageIsUsed = infoSchema.findTableOrView(
1295                systemSession, LobStorageBackend.LOB_DATA_TABLE) != null;
1296        lobStorageIsUsed |= mvStore != null;
1297        if (!lobStorageIsUsed) {
1298            return;
1299        }
1300        try {
1301            getLobStorage();
1302            lobStorage.removeAllForTable(
1303                    LobStorageFrontend.TABLE_ID_SESSION_VARIABLE);
1304        } catch (DbException e) {
1305            trace.error(e, "close");
1306        }
1307    }
1308 
1309    private void stopWriter() {
1310        if (writer != null) {
1311            writer.stopThread();
1312            writer = null;
1313        }
1314    }
1315 
1316    /**
1317     * Close all open files and unlock the database.
1318     *
1319     * @param flush whether writing is allowed
1320     */
1321    private synchronized void closeOpenFilesAndUnlock(boolean flush) {
1322        stopWriter();
1323        if (pageStore != null) {
1324            if (flush) {
1325                try {
1326                    pageStore.checkpoint();
1327                    if (!readOnly) {
1328                        lockMeta(pageStore.getPageStoreSession());
1329                        pageStore.compact(compactMode);
1330                    }
1331                } catch (DbException e) {
1332                    if (SysProperties.CHECK2) {
1333                        int code = e.getErrorCode();
1334                        if (code != ErrorCode.DATABASE_IS_CLOSED &&
1335                                code != ErrorCode.LOCK_TIMEOUT_1 &&
1336                                code != ErrorCode.IO_EXCEPTION_2) {
1337                            e.printStackTrace();
1338                        }
1339                    }
1340                    trace.error(e, "close");
1341                } catch (Throwable t) {
1342                    if (SysProperties.CHECK2) {
1343                        t.printStackTrace();
1344                    }
1345                    trace.error(t, "close");
1346                }
1347            }
1348        }
1349        reconnectModified(false);
1350        if (mvStore != null) {
1351            long maxCompactTime = dbSettings.maxCompactTime;
1352            if (compactMode == CommandInterface.SHUTDOWN_COMPACT) {
1353                mvStore.compactFile(dbSettings.maxCompactTime);
1354            } else if (compactMode == CommandInterface.SHUTDOWN_DEFRAG) {
1355                maxCompactTime = Long.MAX_VALUE;
1356            } else if (getSettings().defragAlways) {
1357                maxCompactTime = Long.MAX_VALUE;
1358            }
1359            mvStore.close(maxCompactTime);
1360        }
1361        closeFiles();
1362        if (persistent && lock == null &&
1363                fileLockMethod != FileLock.LOCK_NO &&
1364                fileLockMethod != FileLock.LOCK_FS) {
1365            // everything already closed (maybe in checkPowerOff)
1366            // don't delete temp files in this case because
1367            // the database could be open now (even from within another process)
1368            return;
1369        }
1370        if (persistent) {
1371            deleteOldTempFiles();
1372        }
1373        if (systemSession != null) {
1374            systemSession.close();
1375            systemSession = null;
1376        }
1377        if (lobSession != null) {
1378            lobSession.close();
1379            lobSession = null;
1380        }
1381        if (lock != null) {
1382            if (fileLockMethod == FileLock.LOCK_SERIALIZED) {
1383                // wait before deleting the .lock file,
1384                // otherwise other connections can not detect that
1385                if (lock.load().containsKey("changePending")) {
1386                    try {
1387                        Thread.sleep((int) (reconnectCheckDelay * 1.1));
1388                    } catch (InterruptedException e) {
1389                        trace.error(e, "close");
1390                    }
1391                }
1392            }
1393            lock.unlock();
1394            lock = null;
1395        }
1396    }
1397 
1398    private synchronized void closeFiles() {
1399        try {
1400            if (mvStore != null) {
1401                mvStore.closeImmediately();
1402            }
1403            if (pageStore != null) {
1404                pageStore.close();
1405                pageStore = null;
1406            }
1407        } catch (DbException e) {
1408            trace.error(e, "close");
1409        }
1410    }
1411 
1412    private void checkMetaFree(Session session, int id) {
1413        SearchRow r = meta.getTemplateSimpleRow(false);
1414        r.setValue(0, ValueInt.get(id));
1415        Cursor cursor = metaIdIndex.find(session, r, r);
1416        if (cursor.next()) {
1417            DbException.throwInternalError();
1418        }
1419    }
1420 
1421    /**
1422     * Allocate a new object id.
1423     *
1424     * @return the id
1425     */
1426    public synchronized int allocateObjectId() {
1427        int i = objectIds.nextClearBit(0);
1428        objectIds.set(i);
1429        return i;
1430    }
1431 
1432    public ArrayList<UserAggregate> getAllAggregates() {
1433        return New.arrayList(aggregates.values());
1434    }
1435 
1436    public ArrayList<Comment> getAllComments() {
1437        return New.arrayList(comments.values());
1438    }
1439 
1440    public int getAllowLiterals() {
1441        if (starting) {
1442            return Constants.ALLOW_LITERALS_ALL;
1443        }
1444        return allowLiterals;
1445    }
1446 
1447    public ArrayList<Right> getAllRights() {
1448        return New.arrayList(rights.values());
1449    }
1450 
1451    public ArrayList<Role> getAllRoles() {
1452        return New.arrayList(roles.values());
1453    }
1454 
1455    /**
1456     * Get all schema objects.
1457     *
1458     * @return all objects of all types
1459     */
1460    public ArrayList<SchemaObject> getAllSchemaObjects() {
1461        initMetaTables();
1462        ArrayList<SchemaObject> list = New.arrayList();
1463        for (Schema schema : schemas.values()) {
1464            list.addAll(schema.getAll());
1465        }
1466        return list;
1467    }
1468 
1469    /**
1470     * Get all schema objects of the given type.
1471     *
1472     * @param type the object type
1473     * @return all objects of that type
1474     */
1475    public ArrayList<SchemaObject> getAllSchemaObjects(int type) {
1476        if (type == DbObject.TABLE_OR_VIEW) {
1477            initMetaTables();
1478        }
1479        ArrayList<SchemaObject> list = New.arrayList();
1480        for (Schema schema : schemas.values()) {
1481            list.addAll(schema.getAll(type));
1482        }
1483        return list;
1484    }
1485 
1486    /**
1487     * Get all tables and views.
1488     *
1489     * @param includeMeta whether to force including the meta data tables (if
1490     *            true, metadata tables are always included; if false, metadata
1491     *            tables are only included if they are already initialized)
1492     * @return all objects of that type
1493     */
1494    public ArrayList<Table> getAllTablesAndViews(boolean includeMeta) {
1495        if (includeMeta) {
1496            initMetaTables();
1497        }
1498        ArrayList<Table> list = New.arrayList();
1499        for (Schema schema : schemas.values()) {
1500            list.addAll(schema.getAllTablesAndViews());
1501        }
1502        return list;
1503    }
1504 
1505    public ArrayList<Schema> getAllSchemas() {
1506        initMetaTables();
1507        return New.arrayList(schemas.values());
1508    }
1509 
1510    public ArrayList<Setting> getAllSettings() {
1511        return New.arrayList(settings.values());
1512    }
1513 
1514    public ArrayList<UserDataType> getAllUserDataTypes() {
1515        return New.arrayList(userDataTypes.values());
1516    }
1517 
1518    public ArrayList<User> getAllUsers() {
1519        return New.arrayList(users.values());
1520    }
1521 
1522    public String getCacheType() {
1523        return cacheType;
1524    }
1525 
1526    public String getCluster() {
1527        return cluster;
1528    }
1529 
1530    public CompareMode getCompareMode() {
1531        return compareMode;
1532    }
1533 
1534    @Override
1535    public String getDatabasePath() {
1536        if (persistent) {
1537            return FileUtils.toRealPath(databaseName);
1538        }
1539        return null;
1540    }
1541 
1542    public String getShortName() {
1543        return databaseShortName;
1544    }
1545 
1546    public String getName() {
1547        return databaseName;
1548    }
1549 
1550    /**
1551     * Get all sessions that are currently connected to the database.
1552     *
1553     * @param includingSystemSession if the system session should also be
1554     *            included
1555     * @return the list of sessions
1556     */
1557    public Session[] getSessions(boolean includingSystemSession) {
1558        ArrayList<Session> list;
1559        // need to synchronized on userSession, otherwise the list
1560        // may contain null elements
1561        synchronized (userSessions) {
1562            list = New.arrayList(userSessions);
1563        }
1564        // copy, to ensure the reference is stable
1565        Session sys = systemSession;
1566        Session lob = lobSession;
1567        if (includingSystemSession && sys != null) {
1568            list.add(sys);
1569        }
1570        if (includingSystemSession && lob != null) {
1571            list.add(lob);
1572        }
1573        Session[] array = new Session[list.size()];
1574        list.toArray(array);
1575        return array;
1576    }
1577 
1578    /**
1579     * Update an object in the system table.
1580     *
1581     * @param session the session
1582     * @param obj the database object
1583     */
1584    public synchronized void updateMeta(Session session, DbObject obj) {
1585        lockMeta(session);
1586        int id = obj.getId();
1587        removeMeta(session, id);
1588        addMeta(session, obj);
1589    }
1590 
1591    /**
1592     * Rename a schema object.
1593     *
1594     * @param session the session
1595     * @param obj the object
1596     * @param newName the new name
1597     */
1598    public synchronized void renameSchemaObject(Session session,
1599            SchemaObject obj, String newName) {
1600        checkWritingAllowed();
1601        obj.getSchema().rename(obj, newName);
1602        updateMetaAndFirstLevelChildren(session, obj);
1603    }
1604 
1605    private synchronized void updateMetaAndFirstLevelChildren(Session session, DbObject obj) {
1606        ArrayList<DbObject> list = obj.getChildren();
1607        Comment comment = findComment(obj);
1608        if (comment != null) {
1609            DbException.throwInternalError();
1610        }
1611        updateMeta(session, obj);
1612        // remember that this scans only one level deep!
1613        if (list != null) {
1614            for (DbObject o : list) {
1615                if (o.getCreateSQL() != null) {
1616                    updateMeta(session, o);
1617                }
1618            }
1619        }
1620    }
1621 
1622    /**
1623     * Rename a database object.
1624     *
1625     * @param session the session
1626     * @param obj the object
1627     * @param newName the new name
1628     */
1629    public synchronized void renameDatabaseObject(Session session,
1630            DbObject obj, String newName) {
1631        checkWritingAllowed();
1632        int type = obj.getType();
1633        HashMap<String, DbObject> map = getMap(type);
1634        if (SysProperties.CHECK) {
1635            if (!map.containsKey(obj.getName())) {
1636                DbException.throwInternalError("not found: " + obj.getName());
1637            }
1638            if (obj.getName().equals(newName) || map.containsKey(newName)) {
1639                DbException.throwInternalError("object already exists: " + newName);
1640            }
1641        }
1642        obj.checkRename();
1643        int id = obj.getId();
1644        lockMeta(session);
1645        removeMeta(session, id);
1646        map.remove(obj.getName());
1647        obj.rename(newName);
1648        map.put(newName, obj);
1649        updateMetaAndFirstLevelChildren(session, obj);
1650    }
1651 
1652    /**
1653     * Create a temporary file in the database folder.
1654     *
1655     * @return the file name
1656     */
1657    public String createTempFile() {
1658        try {
1659            boolean inTempDir = readOnly;
1660            String name = databaseName;
1661            if (!persistent) {
1662                name = "memFS:" + name;
1663            }
1664            return FileUtils.createTempFile(name,
1665                    Constants.SUFFIX_TEMP_FILE, true, inTempDir);
1666        } catch (IOException e) {
1667            throw DbException.convertIOException(e, databaseName);
1668        }
1669    }
1670 
1671    private void deleteOldTempFiles() {
1672        String path = FileUtils.getParent(databaseName);
1673        for (String name : FileUtils.newDirectoryStream(path)) {
1674            if (name.endsWith(Constants.SUFFIX_TEMP_FILE) &&
1675                    name.startsWith(databaseName)) {
1676                // can't always delete the files, they may still be open
1677                FileUtils.tryDelete(name);
1678            }
1679        }
1680    }
1681 
1682    /**
1683     * Get the schema. If the schema does not exist, an exception is thrown.
1684     *
1685     * @param schemaName the name of the schema
1686     * @return the schema
1687     * @throws DbException no schema with that name exists
1688     */
1689    public Schema getSchema(String schemaName) {
1690        Schema schema = findSchema(schemaName);
1691        if (schema == null) {
1692            throw DbException.get(ErrorCode.SCHEMA_NOT_FOUND_1, schemaName);
1693        }
1694        return schema;
1695    }
1696 
1697    /**
1698     * Remove the object from the database.
1699     *
1700     * @param session the session
1701     * @param obj the object to remove
1702     */
1703    public synchronized void removeDatabaseObject(Session session, DbObject obj) {
1704        checkWritingAllowed();
1705        String objName = obj.getName();
1706        int type = obj.getType();
1707        HashMap<String, DbObject> map = getMap(type);
1708        if (SysProperties.CHECK && !map.containsKey(objName)) {
1709            DbException.throwInternalError("not found: " + objName);
1710        }
1711        Comment comment = findComment(obj);
1712        lockMeta(session);
1713        if (comment != null) {
1714            removeDatabaseObject(session, comment);
1715        }
1716        int id = obj.getId();
1717        obj.removeChildrenAndResources(session);
1718        map.remove(objName);
1719        removeMeta(session, id);
1720    }
1721 
1722    /**
1723     * Get the first table that depends on this object.
1724     *
1725     * @param obj the object to find
1726     * @param except the table to exclude (or null)
1727     * @return the first dependent table, or null
1728     */
1729    public Table getDependentTable(SchemaObject obj, Table except) {
1730        switch (obj.getType()) {
1731        case DbObject.COMMENT:
1732        case DbObject.CONSTRAINT:
1733        case DbObject.INDEX:
1734        case DbObject.RIGHT:
1735        case DbObject.TRIGGER:
1736        case DbObject.USER:
1737            return null;
1738        default:
1739        }
1740        HashSet<DbObject> set = New.hashSet();
1741        for (Table t : getAllTablesAndViews(false)) {
1742            if (except == t) {
1743                continue;
1744            } else if (Table.VIEW.equals(t.getTableType())) {
1745                continue;
1746            }
1747            set.clear();
1748            t.addDependencies(set);
1749            if (set.contains(obj)) {
1750                return t;
1751            }
1752        }
1753        return null;
1754    }
1755 
1756    /**
1757     * Remove an object from the system table.
1758     *
1759     * @param session the session
1760     * @param obj the object to be removed
1761     */
1762    public synchronized void removeSchemaObject(Session session,
1763            SchemaObject obj) {
1764        int type = obj.getType();
1765        if (type == DbObject.TABLE_OR_VIEW) {
1766            Table table = (Table) obj;
1767            if (table.isTemporary() && !table.isGlobalTemporary()) {
1768                session.removeLocalTempTable(table);
1769                return;
1770            }
1771        } else if (type == DbObject.INDEX) {
1772            Index index = (Index) obj;
1773            Table table = index.getTable();
1774            if (table.isTemporary() && !table.isGlobalTemporary()) {
1775                session.removeLocalTempTableIndex(index);
1776                return;
1777            }
1778        } else if (type == DbObject.CONSTRAINT) {
1779            Constraint constraint = (Constraint) obj;
1780            Table table = constraint.getTable();
1781            if (table.isTemporary() && !table.isGlobalTemporary()) {
1782                session.removeLocalTempTableConstraint(constraint);
1783                return;
1784            }
1785        }
1786        checkWritingAllowed();
1787        lockMeta(session);
1788        Comment comment = findComment(obj);
1789        if (comment != null) {
1790            removeDatabaseObject(session, comment);
1791        }
1792        obj.getSchema().remove(obj);
1793        int id = obj.getId();
1794        if (!starting) {
1795            Table t = getDependentTable(obj, null);
1796            if (t != null) {
1797                obj.getSchema().add(obj);
1798                throw DbException.get(ErrorCode.CANNOT_DROP_2, obj.getSQL(),
1799                        t.getSQL());
1800            }
1801            obj.removeChildrenAndResources(session);
1802        }
1803        removeMeta(session, id);
1804    }
1805 
1806    /**
1807     * Check if this database disk-based.
1808     *
1809     * @return true if it is disk-based, false it it is in-memory only.
1810     */
1811    public boolean isPersistent() {
1812        return persistent;
1813    }
1814 
1815    public TraceSystem getTraceSystem() {
1816        return traceSystem;
1817    }
1818 
1819    public synchronized void setCacheSize(int kb) {
1820        if (starting) {
1821            int max = MathUtils.convertLongToInt(Utils.getMemoryMax()) / 2;
1822            kb = Math.min(kb, max);
1823        }
1824        cacheSize = kb;
1825        if (pageStore != null) {
1826            pageStore.getCache().setMaxMemory(kb);
1827        }
1828        if (mvStore != null) {
1829            mvStore.setCacheSize(Math.max(1, kb / 1024));
1830        }
1831    }
1832 
1833    public synchronized void setMasterUser(User user) {
1834        lockMeta(systemSession);
1835        addDatabaseObject(systemSession, user);
1836        systemSession.commit(true);
1837    }
1838 
1839    public Role getPublicRole() {
1840        return publicRole;
1841    }
1842 
1843    /**
1844     * Get a unique temporary table name.
1845     *
1846     * @param baseName the prefix of the returned name
1847     * @param session the session
1848     * @return a unique name
1849     */
1850    public synchronized String getTempTableName(String baseName, Session session) {
1851        String tempName;
1852        do {
1853            tempName = baseName + "_COPY_" + session.getId() +
1854                    "_" + nextTempTableId++;
1855        } while (mainSchema.findTableOrView(session, tempName) != null);
1856        return tempName;
1857    }
1858 
1859    public void setCompareMode(CompareMode compareMode) {
1860        this.compareMode = compareMode;
1861    }
1862 
1863    public void setCluster(String cluster) {
1864        this.cluster = cluster;
1865    }
1866 
1867    @Override
1868    public void checkWritingAllowed() {
1869        if (readOnly) {
1870            throw DbException.get(ErrorCode.DATABASE_IS_READ_ONLY);
1871        }
1872        if (fileLockMethod == FileLock.LOCK_SERIALIZED) {
1873            if (!reconnectChangePending) {
1874                throw DbException.get(ErrorCode.DATABASE_IS_READ_ONLY);
1875            }
1876        }
1877    }
1878 
1879    public boolean isReadOnly() {
1880        return readOnly;
1881    }
1882 
1883    public void setWriteDelay(int value) {
1884        writeDelay = value;
1885        if (writer != null) {
1886            writer.setWriteDelay(value);
1887            // TODO check if MIN_WRITE_DELAY is a good value
1888            flushOnEachCommit = writeDelay < Constants.MIN_WRITE_DELAY;
1889        }
1890        if (mvStore != null) {
1891            int millis = value < 0 ? 0 : value;
1892            mvStore.getStore().setAutoCommitDelay(millis);
1893        }
1894    }
1895 
1896    public int getRetentionTime() {
1897        return retentionTime;
1898    }
1899 
1900    public void setRetentionTime(int value) {
1901        retentionTime = value;
1902        if (mvStore != null) {
1903            mvStore.getStore().setRetentionTime(value);
1904        }
1905    }
1906 
1907    /**
1908     * Check if flush-on-each-commit is enabled.
1909     *
1910     * @return true if it is
1911     */
1912    public boolean getFlushOnEachCommit() {
1913        return flushOnEachCommit;
1914    }
1915 
1916    /**
1917     * Get the list of in-doubt transactions.
1918     *
1919     * @return the list
1920     */
1921    public ArrayList<InDoubtTransaction> getInDoubtTransactions() {
1922        if (mvStore != null) {
1923            return mvStore.getInDoubtTransactions();
1924        }
1925        return pageStore == null ? null : pageStore.getInDoubtTransactions();
1926    }
1927 
1928    /**
1929     * Prepare a transaction.
1930     *
1931     * @param session the session
1932     * @param transaction the name of the transaction
1933     */
1934    synchronized void prepareCommit(Session session, String transaction) {
1935        if (readOnly) {
1936            return;
1937        }
1938        if (mvStore != null) {
1939            mvStore.prepareCommit(session, transaction);
1940            return;
1941        }
1942        if (pageStore != null) {
1943            pageStore.flushLog();
1944            pageStore.prepareCommit(session, transaction);
1945        }
1946    }
1947 
1948    /**
1949     * Commit the current transaction of the given session.
1950     *
1951     * @param session the session
1952     */
1953    synchronized void commit(Session session) {
1954        throwLastBackgroundException();
1955        if (readOnly) {
1956            return;
1957        }
1958        if (pageStore != null) {
1959            pageStore.commit(session);
1960        }
1961        session.setAllCommitted();
1962    }
1963 
1964    private void throwLastBackgroundException() {
1965        if (backgroundException != null) {
1966            // we don't care too much about concurrency here,
1967            // we just want to make sure the exception is _normally_
1968            // not just logged to the .trace.db file
1969            DbException b = backgroundException;
1970            backgroundException = null;
1971            if (b != null) {
1972                throw b;
1973            }
1974        }
1975    }
1976 
1977    public void setBackgroundException(DbException e) {
1978        if (backgroundException == null) {
1979            backgroundException = e;
1980            TraceSystem t = getTraceSystem();
1981            if (t != null) {
1982                t.getTrace(Trace.DATABASE).error(e, "flush");
1983            }
1984        }
1985    }
1986 
1987    /**
1988     * Flush all pending changes to the transaction log.
1989     */
1990    public synchronized void flush() {
1991        if (readOnly) {
1992            return;
1993        }
1994        if (pageStore != null) {
1995            pageStore.flushLog();
1996        }
1997        if (mvStore != null) {
1998            try {
1999                mvStore.flush();
2000            } catch (RuntimeException e) {
2001                backgroundException = DbException.convert(e);
2002                throw e;
2003            }
2004        }
2005    }
2006 
2007    public void setEventListener(DatabaseEventListener eventListener) {
2008        this.eventListener = eventListener;
2009    }
2010 
2011    public void setEventListenerClass(String className) {
2012        if (className == null || className.length() == 0) {
2013            eventListener = null;
2014        } else {
2015            try {
2016                eventListener = (DatabaseEventListener)
2017                        JdbcUtils.loadUserClass(className).newInstance();
2018                String url = databaseURL;
2019                if (cipher != null) {
2020                    url += ";CIPHER=" + cipher;
2021                }
2022                eventListener.init(url);
2023            } catch (Throwable e) {
2024                throw DbException.get(
2025                        ErrorCode.ERROR_SETTING_DATABASE_EVENT_LISTENER_2, e,
2026                        className, e.toString());
2027            }
2028        }
2029    }
2030 
2031    /**
2032     * Set the progress of a long running operation.
2033     * This method calls the {@link DatabaseEventListener} if one is registered.
2034     *
2035     * @param state the {@link DatabaseEventListener} state
2036     * @param name the object name
2037     * @param x the current position
2038     * @param max the highest value
2039     */
2040    public void setProgress(int state, String name, int x, int max) {
2041        if (eventListener != null) {
2042            try {
2043                eventListener.setProgress(state, name, x, max);
2044            } catch (Exception e2) {
2045                // ignore this (user made) exception
2046            }
2047        }
2048    }
2049 
2050    /**
2051     * This method is called after an exception occurred, to inform the database
2052     * event listener (if one is set).
2053     *
2054     * @param e the exception
2055     * @param sql the SQL statement
2056     */
2057    public void exceptionThrown(SQLException e, String sql) {
2058        if (eventListener != null) {
2059            try {
2060                eventListener.exceptionThrown(e, sql);
2061            } catch (Exception e2) {
2062                // ignore this (user made) exception
2063            }
2064        }
2065    }
2066 
2067    /**
2068     * Synchronize the files with the file system. This method is called when
2069     * executing the SQL statement CHECKPOINT SYNC.
2070     */
2071    public synchronized void sync() {
2072        if (readOnly) {
2073            return;
2074        }
2075        if (mvStore != null) {
2076            mvStore.sync();
2077        }
2078        if (pageStore != null) {
2079            pageStore.sync();
2080        }
2081    }
2082 
2083    public int getMaxMemoryRows() {
2084        return maxMemoryRows;
2085    }
2086 
2087    public void setMaxMemoryRows(int value) {
2088        this.maxMemoryRows = value;
2089    }
2090 
2091    public void setMaxMemoryUndo(int value) {
2092        this.maxMemoryUndo = value;
2093    }
2094 
2095    public int getMaxMemoryUndo() {
2096        return maxMemoryUndo;
2097    }
2098 
2099    public void setLockMode(int lockMode) {
2100        switch (lockMode) {
2101        case Constants.LOCK_MODE_OFF:
2102            if (multiThreaded) {
2103                // currently the combination of LOCK_MODE=0 and MULTI_THREADED
2104                // is not supported. also see code in
2105                // JdbcDatabaseMetaData#supportsTransactionIsolationLevel(int)
2106                throw DbException.get(
2107                        ErrorCode.UNSUPPORTED_SETTING_COMBINATION,
2108                        "LOCK_MODE=0 & MULTI_THREADED");
2109            }
2110            break;
2111        case Constants.LOCK_MODE_READ_COMMITTED:
2112        case Constants.LOCK_MODE_TABLE:
2113        case Constants.LOCK_MODE_TABLE_GC:
2114            break;
2115        default:
2116            throw DbException.getInvalidValueException("lock mode", lockMode);
2117        }
2118        this.lockMode = lockMode;
2119    }
2120 
2121    public int getLockMode() {
2122        return lockMode;
2123    }
2124 
2125    public synchronized void setCloseDelay(int value) {
2126        this.closeDelay = value;
2127    }
2128 
2129    public Session getSystemSession() {
2130        return systemSession;
2131    }
2132 
2133    /**
2134     * Check if the database is in the process of closing.
2135     *
2136     * @return true if the database is closing
2137     */
2138    public boolean isClosing() {
2139        return closing;
2140    }
2141 
2142    public void setMaxLengthInplaceLob(int value) {
2143        this.maxLengthInplaceLob = value;
2144    }
2145 
2146    @Override
2147    public int getMaxLengthInplaceLob() {
2148        return maxLengthInplaceLob;
2149    }
2150 
2151    public void setIgnoreCase(boolean b) {
2152        ignoreCase = b;
2153    }
2154 
2155    public boolean getIgnoreCase() {
2156        if (starting) {
2157            // tables created at startup must not be converted to ignorecase
2158            return false;
2159        }
2160        return ignoreCase;
2161    }
2162 
2163    public synchronized void setDeleteFilesOnDisconnect(boolean b) {
2164        this.deleteFilesOnDisconnect = b;
2165    }
2166 
2167    @Override
2168    public String getLobCompressionAlgorithm(int type) {
2169        return lobCompressionAlgorithm;
2170    }
2171 
2172    public void setLobCompressionAlgorithm(String stringValue) {
2173        this.lobCompressionAlgorithm = stringValue;
2174    }
2175 
2176    public synchronized void setMaxLogSize(long value) {
2177        if (pageStore != null) {
2178            pageStore.setMaxLogSize(value);
2179        }
2180    }
2181 
2182    public void setAllowLiterals(int value) {
2183        this.allowLiterals = value;
2184    }
2185 
2186    public boolean getOptimizeReuseResults() {
2187        return optimizeReuseResults;
2188    }
2189 
2190    public void setOptimizeReuseResults(boolean b) {
2191        optimizeReuseResults = b;
2192    }
2193 
2194    @Override
2195    public Object getLobSyncObject() {
2196        return lobSyncObject;
2197    }
2198 
2199    public int getSessionCount() {
2200        return userSessions.size();
2201    }
2202 
2203    public void setReferentialIntegrity(boolean b) {
2204        referentialIntegrity = b;
2205    }
2206 
2207    public boolean getReferentialIntegrity() {
2208        return referentialIntegrity;
2209    }
2210 
2211    public void setQueryStatistics(boolean b) {
2212        queryStatistics = b;
2213        synchronized (this) {
2214            queryStatisticsData = null;
2215        }
2216    }
2217 
2218    public boolean getQueryStatistics() {
2219        return queryStatistics;
2220    }
2221 
2222    public QueryStatisticsData getQueryStatisticsData() {
2223        if (!queryStatistics) {
2224            return null;
2225        }
2226        if (queryStatisticsData == null) {
2227            synchronized (this) {
2228                if (queryStatisticsData == null) {
2229                    queryStatisticsData = new QueryStatisticsData();
2230                }
2231            }
2232        }
2233        return queryStatisticsData;
2234    }
2235 
2236    /**
2237     * Check if the database is currently opening. This is true until all stored
2238     * SQL statements have been executed.
2239     *
2240     * @return true if the database is still starting
2241     */
2242    public boolean isStarting() {
2243        return starting;
2244    }
2245 
2246    /**
2247     * Check if multi version concurrency is enabled for this database.
2248     *
2249     * @return true if it is enabled
2250     */
2251    public boolean isMultiVersion() {
2252        return multiVersion;
2253    }
2254 
2255    /**
2256     * Called after the database has been opened and initialized. This method
2257     * notifies the event listener if one has been set.
2258     */
2259    void opened() {
2260        if (eventListener != null) {
2261            eventListener.opened();
2262        }
2263        if (writer != null) {
2264            writer.startThread();
2265        }
2266    }
2267 
2268    public void setMode(Mode mode) {
2269        this.mode = mode;
2270    }
2271 
2272    public Mode getMode() {
2273        return mode;
2274    }
2275 
2276    public boolean isMultiThreaded() {
2277        return multiThreaded;
2278    }
2279 
2280    public void setMultiThreaded(boolean multiThreaded) {
2281        if (multiThreaded && this.multiThreaded != multiThreaded) {
2282            if (multiVersion && mvStore == null) {
2283                // currently the combination of MVCC and MULTI_THREADED is not
2284                // supported
2285                throw DbException.get(
2286                        ErrorCode.UNSUPPORTED_SETTING_COMBINATION,
2287                        "MVCC & MULTI_THREADED");
2288            }
2289            if (lockMode == 0) {
2290                // currently the combination of LOCK_MODE=0 and MULTI_THREADED
2291                // is not supported
2292                throw DbException.get(
2293                        ErrorCode.UNSUPPORTED_SETTING_COMBINATION,
2294                        "LOCK_MODE=0 & MULTI_THREADED");
2295            }
2296        }
2297        this.multiThreaded = multiThreaded;
2298    }
2299 
2300    public void setMaxOperationMemory(int maxOperationMemory) {
2301        this.maxOperationMemory  = maxOperationMemory;
2302    }
2303 
2304    public int getMaxOperationMemory() {
2305        return maxOperationMemory;
2306    }
2307 
2308    public Session getExclusiveSession() {
2309        return exclusiveSession;
2310    }
2311 
2312    /**
2313     * Set the session that can exclusively access the database.
2314     *
2315     * @param session the session
2316     * @param closeOthers whether other sessions are closed
2317     */
2318    public void setExclusiveSession(Session session, boolean closeOthers) {
2319        this.exclusiveSession = session;
2320        if (closeOthers) {
2321            closeAllSessionsException(session);
2322        }
2323    }
2324 
2325    @Override
2326    public SmallLRUCache<String, String[]> getLobFileListCache() {
2327        if (lobFileListCache == null) {
2328            lobFileListCache = SmallLRUCache.newInstance(128);
2329        }
2330        return lobFileListCache;
2331    }
2332 
2333    /**
2334     * Checks if the system table (containing the catalog) is locked.
2335     *
2336     * @return true if it is currently locked
2337     */
2338    public boolean isSysTableLocked() {
2339        return meta == null || meta.isLockedExclusively();
2340    }
2341 
2342    /**
2343     * Open a new connection or get an existing connection to another database.
2344     *
2345     * @param driver the database driver or null
2346     * @param url the database URL
2347     * @param user the user name
2348     * @param password the password
2349     * @return the connection
2350     */
2351    public TableLinkConnection getLinkConnection(String driver, String url,
2352            String user, String password) {
2353        if (linkConnections == null) {
2354            linkConnections = New.hashMap();
2355        }
2356        return TableLinkConnection.open(linkConnections, driver, url, user,
2357                password, dbSettings.shareLinkedConnections);
2358    }
2359 
2360    @Override
2361    public String toString() {
2362        return databaseShortName + ":" + super.toString();
2363    }
2364 
2365    /**
2366     * Immediately close the database.
2367     */
2368    public void shutdownImmediately() {
2369        setPowerOffCount(1);
2370        try {
2371            checkPowerOff();
2372        } catch (DbException e) {
2373            // ignore
2374        }
2375        closeFiles();
2376    }
2377 
2378    @Override
2379    public TempFileDeleter getTempFileDeleter() {
2380        return tempFileDeleter;
2381    }
2382 
2383    public PageStore getPageStore() {
2384        if (dbSettings.mvStore) {
2385            if (mvStore == null) {
2386                mvStore = MVTableEngine.init(this);
2387            }
2388            return null;
2389        }
2390        if (pageStore == null) {
2391            pageStore = new PageStore(this, databaseName +
2392                    Constants.SUFFIX_PAGE_FILE, accessModeData, cacheSize);
2393            if (pageSize != Constants.DEFAULT_PAGE_SIZE) {
2394                pageStore.setPageSize(pageSize);
2395            }
2396            if (!readOnly && fileLockMethod == FileLock.LOCK_FS) {
2397                pageStore.setLockFile(true);
2398            }
2399            pageStore.setLogMode(logMode);
2400            pageStore.open();
2401        }
2402        return pageStore;
2403    }
2404 
2405    /**
2406     * Get the first user defined table.
2407     *
2408     * @return the table or null if no table is defined
2409     */
2410    public Table getFirstUserTable() {
2411        for (Table table : getAllTablesAndViews(false)) {
2412            if (table.getCreateSQL() != null) {
2413                if (table.isHidden()) {
2414                    // LOB tables
2415                    continue;
2416                }
2417                return table;
2418            }
2419        }
2420        return null;
2421    }
2422 
2423    /**
2424     * Check if the contents of the database was changed and therefore it is
2425     * required to re-connect. This method waits until pending changes are
2426     * completed. If a pending change takes too long (more than 2 seconds), the
2427     * pending change is broken (removed from the properties file).
2428     *
2429     * @return true if reconnecting is required
2430     */
2431    public boolean isReconnectNeeded() {
2432        if (fileLockMethod != FileLock.LOCK_SERIALIZED) {
2433            return false;
2434        }
2435        if (reconnectChangePending) {
2436            return false;
2437        }
2438        long now = System.currentTimeMillis();
2439        if (now < reconnectCheckNext) {
2440            return false;
2441        }
2442        reconnectCheckNext = now + reconnectCheckDelay;
2443        if (lock == null) {
2444            lock = new FileLock(traceSystem, databaseName +
2445                    Constants.SUFFIX_LOCK_FILE, Constants.LOCK_SLEEP);
2446        }
2447        try {
2448            Properties prop = lock.load(), first = prop;
2449            while (true) {
2450                if (prop.equals(reconnectLastLock)) {
2451                    return false;
2452                }
2453                if (prop.getProperty("changePending", null) == null) {
2454                    break;
2455                }
2456                if (System.currentTimeMillis() >
2457                        now + reconnectCheckDelay * 10) {
2458                    if (first.equals(prop)) {
2459                        // the writing process didn't update the file -
2460                        // it may have terminated
2461                        lock.setProperty("changePending", null);
2462                        lock.save();
2463                        break;
2464                    }
2465                }
2466                trace.debug("delay (change pending)");
2467                Thread.sleep(reconnectCheckDelay);
2468                prop = lock.load();
2469            }
2470            reconnectLastLock = prop;
2471        } catch (Exception e) {
2472            // DbException, InterruptedException
2473            trace.error(e, "readOnly {0}", readOnly);
2474            // ignore
2475        }
2476        return true;
2477    }
2478 
2479    /**
2480     * Flush all changes when using the serialized mode, and if there are
2481     * pending changes, and some time has passed. This switches to a new
2482     * transaction log and resets the change pending flag in
2483     * the .lock.db file.
2484     */
2485    public void checkpointIfRequired() {
2486        if (fileLockMethod != FileLock.LOCK_SERIALIZED ||
2487                readOnly || !reconnectChangePending || closing) {
2488            return;
2489        }
2490        long now = System.currentTimeMillis();
2491        if (now > reconnectCheckNext + reconnectCheckDelay) {
2492            if (SysProperties.CHECK && checkpointAllowed < 0) {
2493                DbException.throwInternalError();
2494            }
2495            synchronized (reconnectSync) {
2496                if (checkpointAllowed > 0) {
2497                    return;
2498                }
2499                checkpointRunning = true;
2500            }
2501            synchronized (this) {
2502                trace.debug("checkpoint start");
2503                flushSequences();
2504                checkpoint();
2505                reconnectModified(false);
2506                trace.debug("checkpoint end");
2507            }
2508            synchronized (reconnectSync) {
2509                checkpointRunning = false;
2510            }
2511        }
2512    }
2513 
2514    public boolean isFileLockSerialized() {
2515        return fileLockMethod == FileLock.LOCK_SERIALIZED;
2516    }
2517 
2518    private void flushSequences() {
2519        for (SchemaObject obj : getAllSchemaObjects(DbObject.SEQUENCE)) {
2520            Sequence sequence = (Sequence) obj;
2521            sequence.flushWithoutMargin();
2522        }
2523    }
2524 
2525    /**
2526     * Flush all changes and open a new transaction log.
2527     */
2528    public void checkpoint() {
2529        if (persistent) {
2530            synchronized (this) {
2531                if (pageStore != null) {
2532                    pageStore.checkpoint();
2533                }
2534            }
2535            if (mvStore != null) {
2536                mvStore.flush();
2537            }
2538        }
2539        getTempFileDeleter().deleteUnused();
2540    }
2541 
2542    /**
2543     * This method is called before writing to the transaction log.
2544     *
2545     * @return true if the call was successful and writing is allowed,
2546     *          false if another connection was faster
2547     */
2548    public boolean beforeWriting() {
2549        if (fileLockMethod != FileLock.LOCK_SERIALIZED) {
2550            return true;
2551        }
2552        while (checkpointRunning) {
2553            try {
2554                Thread.sleep(10 + (int) (Math.random() * 10));
2555            } catch (Exception e) {
2556                // ignore InterruptedException
2557            }
2558        }
2559        synchronized (reconnectSync) {
2560            if (reconnectModified(true)) {
2561                checkpointAllowed++;
2562                if (SysProperties.CHECK && checkpointAllowed > 20) {
2563                    throw DbException.throwInternalError();
2564                }
2565                return true;
2566            }
2567        }
2568        // make sure the next call to isReconnectNeeded() returns true
2569        reconnectCheckNext = System.currentTimeMillis() - 1;
2570        reconnectLastLock = null;
2571        return false;
2572    }
2573 
2574    /**
2575     * This method is called after updates are finished.
2576     */
2577    public void afterWriting() {
2578        if (fileLockMethod != FileLock.LOCK_SERIALIZED) {
2579            return;
2580        }
2581        synchronized (reconnectSync) {
2582            checkpointAllowed--;
2583        }
2584        if (SysProperties.CHECK && checkpointAllowed < 0) {
2585            throw DbException.throwInternalError();
2586        }
2587    }
2588 
2589    /**
2590     * Switch the database to read-only mode.
2591     *
2592     * @param readOnly the new value
2593     */
2594    public void setReadOnly(boolean readOnly) {
2595        this.readOnly = readOnly;
2596    }
2597 
2598    public void setCompactMode(int compactMode) {
2599        this.compactMode = compactMode;
2600    }
2601 
2602    public SourceCompiler getCompiler() {
2603        if (compiler == null) {
2604            compiler = new SourceCompiler();
2605        }
2606        return compiler;
2607    }
2608 
2609    @Override
2610    public LobStorageInterface getLobStorage() {
2611        if (lobStorage == null) {
2612            if (dbSettings.mvStore) {
2613                lobStorage = new LobStorageMap(this);
2614            } else {
2615                lobStorage = new LobStorageBackend(this);
2616            }
2617        }
2618        return lobStorage;
2619    }
2620 
2621    public JdbcConnection getLobConnectionForInit() {
2622        String url = Constants.CONN_URL_INTERNAL;
2623        JdbcConnection conn = new JdbcConnection(
2624                systemSession, systemUser.getName(), url);
2625        conn.setTraceLevel(TraceSystem.OFF);
2626        return conn;
2627    }
2628 
2629    public JdbcConnection getLobConnectionForRegularUse() {
2630        String url = Constants.CONN_URL_INTERNAL;
2631        JdbcConnection conn = new JdbcConnection(
2632                lobSession, systemUser.getName(), url);
2633        conn.setTraceLevel(TraceSystem.OFF);
2634        return conn;
2635    }
2636 
2637    public Session getLobSession() {
2638        return lobSession;
2639    }
2640 
2641    public void setLogMode(int log) {
2642        if (log < 0 || log > 2) {
2643            throw DbException.getInvalidValueException("LOG", log);
2644        }
2645        if (pageStore != null) {
2646            if (log != PageStore.LOG_MODE_SYNC ||
2647                    pageStore.getLogMode() != PageStore.LOG_MODE_SYNC) {
2648                // write the log mode in the trace file when enabling or
2649                // disabling a dangerous mode
2650                trace.error(null, "log {0}", log);
2651            }
2652            this.logMode = log;
2653            pageStore.setLogMode(log);
2654        }
2655        if (mvStore != null) {
2656            this.logMode = log;
2657        }
2658    }
2659 
2660    public int getLogMode() {
2661        if (pageStore != null) {
2662            return pageStore.getLogMode();
2663        }
2664        if (mvStore != null) {
2665            return logMode;
2666        }
2667        return PageStore.LOG_MODE_OFF;
2668    }
2669 
2670    public int getDefaultTableType() {
2671        return defaultTableType;
2672    }
2673 
2674    public void setDefaultTableType(int defaultTableType) {
2675        this.defaultTableType = defaultTableType;
2676    }
2677 
2678    public void setMultiVersion(boolean multiVersion) {
2679        this.multiVersion = multiVersion;
2680    }
2681 
2682    public DbSettings getSettings() {
2683        return dbSettings;
2684    }
2685 
2686    /**
2687     * Create a new hash map. Depending on the configuration, the key is case
2688     * sensitive or case insensitive.
2689     *
2690     * @param <V> the value type
2691     * @return the hash map
2692     */
2693    public <V> HashMap<String, V> newStringMap() {
2694        return dbSettings.databaseToUpper ?
2695                new HashMap<String, V>() :
2696                new CaseInsensitiveMap<V>();
2697    }
2698 
2699    /**
2700     * Compare two identifiers (table names, column names,...) and verify they
2701     * are equal. Case sensitivity depends on the configuration.
2702     *
2703     * @param a the first identifier
2704     * @param b the second identifier
2705     * @return true if they match
2706     */
2707    public boolean equalsIdentifiers(String a, String b) {
2708        if (a == b || a.equals(b)) {
2709            return true;
2710        }
2711        if (!dbSettings.databaseToUpper && a.equalsIgnoreCase(b)) {
2712            return true;
2713        }
2714        return false;
2715    }
2716 
2717    @Override
2718    public int readLob(long lobId, byte[] hmac, long offset, byte[] buff,
2719            int off, int length) {
2720        throw DbException.throwInternalError();
2721    }
2722 
2723    public byte[] getFileEncryptionKey() {
2724        return fileEncryptionKey;
2725    }
2726 
2727    public int getPageSize() {
2728        return pageSize;
2729    }
2730 
2731    @Override
2732    public JavaObjectSerializer getJavaObjectSerializer() {
2733        initJavaObjectSerializer();
2734        return javaObjectSerializer;
2735    }
2736 
2737    private void initJavaObjectSerializer() {
2738        if (javaObjectSerializerInitialized) {
2739            return;
2740        }
2741        synchronized (this) {
2742            if (javaObjectSerializerInitialized) {
2743                return;
2744            }
2745            String serializerName = javaObjectSerializerName;
2746            if (serializerName != null) {
2747                serializerName = serializerName.trim();
2748                if (!serializerName.isEmpty() &&
2749                        !serializerName.equals("null")) {
2750                    try {
2751                        javaObjectSerializer = (JavaObjectSerializer)
2752                                JdbcUtils.loadUserClass(serializerName).newInstance();
2753                    } catch (Exception e) {
2754                        throw DbException.convert(e);
2755                    }
2756                }
2757            }
2758            javaObjectSerializerInitialized = true;
2759        }
2760    }
2761 
2762    public void setJavaObjectSerializerName(String serializerName) {
2763        synchronized (this) {
2764            javaObjectSerializerInitialized = false;
2765            javaObjectSerializerName = serializerName;
2766        }
2767    }
2768 
2769}

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