File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\engine\Database.java.html

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