1 5 package org.h2.engine; 6 7 import java.io.File ; 8 import java.io.FileInputStream ; 9 import java.io.IOException ; 10 import java.sql.SQLException ; 11 import java.util.HashMap ; 12 import java.util.HashSet ; 13 import java.util.Iterator ; 14 import java.util.StringTokenizer ; 15 16 import org.h2.api.DatabaseEventListener; 17 import org.h2.command.dml.SetTypes; 18 import org.h2.index.Cursor; 19 import org.h2.index.Index; 20 import org.h2.index.IndexType; 21 import org.h2.jdbc.JdbcSQLException; 22 import org.h2.message.Message; 23 import org.h2.message.Trace; 24 import org.h2.message.TraceSystem; 25 import org.h2.result.Row; 26 import org.h2.result.SearchRow; 27 import org.h2.schema.Schema; 28 import org.h2.schema.SchemaObject; 29 import org.h2.schema.Sequence; 30 import org.h2.store.DataHandler; 31 import org.h2.store.DataPage; 32 import org.h2.store.DiskFile; 33 import org.h2.store.FileLock; 34 import org.h2.store.FileStore; 35 import org.h2.store.LogSystem; 36 import org.h2.store.RecordReader; 37 import org.h2.store.Storage; 38 import org.h2.store.WriterThread; 39 import org.h2.table.Column; 40 import org.h2.table.MetaTable; 41 import org.h2.table.Table; 42 import org.h2.table.TableData; 43 import org.h2.table.TableView; 44 import org.h2.tools.DeleteDbFiles; 45 import org.h2.util.BitField; 46 import org.h2.util.ByteUtils; 47 import org.h2.util.CacheLRU; 48 import org.h2.util.FileUtils; 49 import org.h2.util.IOUtils; 50 import org.h2.util.MemoryFile; 51 import org.h2.util.ObjectArray; 52 import org.h2.util.StringUtils; 53 import org.h2.value.CompareMode; 54 import org.h2.value.Value; 55 import org.h2.value.ValueInt; 56 57 61 62 69 public class Database implements DataHandler { 70 71 private boolean textStorage; 72 private String databaseName; 73 private String databaseShortName; 74 private String databaseURL; 75 private HashMap roles = new HashMap (); 76 private HashMap users = new HashMap (); 77 private HashMap settings = new HashMap (); 78 private HashMap schemas = new HashMap (); 79 private HashMap rights = new HashMap (); 80 private HashMap functionAliases = new HashMap (); 81 private HashMap userDataTypes = new HashMap (); 82 private HashMap comments = new HashMap (); 83 private Schema mainSchema; 84 private Schema infoSchema; 85 private int nextSessionId; 86 private HashSet sessions = new HashSet (); 87 private User systemUser; 88 private Session systemSession; 89 private TableData meta; 90 private Index metaIdIndex; 91 private BitField objectIds = new BitField(); 92 private FileLock lock; 93 private LogSystem log; 94 private WriterThread writer; 95 private ObjectArray storages = new ObjectArray(); 96 private boolean starting; 97 private DiskFile fileData, fileIndex; 98 private TraceSystem traceSystem; 99 private boolean persistent; 100 private String cipher; 101 private byte[] filePasswordHash; 102 private DataPage dummy; 103 private int fileLockMethod; 104 private Role publicRole; 105 private long modificationDataId; 106 private long modificationMetaId; 107 private CompareMode compareMode; 108 private String cluster = Constants.CLUSTERING_DISABLED; 109 private boolean readOnly; 110 private boolean noDiskSpace; 111 private int writeDelay = Constants.DEFAULT_WRITE_DELAY; 112 private DatabaseEventListener eventListener; 113 private FileStore emergencyReserve; 114 private int maxMemoryRows = Constants.DEFAULT_MAX_MEMORY_ROWS; 115 private int maxMemoryUndo = Constants.DEFAULT_MAX_MEMORY_UNDO; 116 private int lockMode = Constants.LOCK_MODE_TABLE; 117 private boolean logIndexChanges; 118 private int logLevel = 1; 119 private int cacheSize; 120 private int maxLengthInplaceLob = Constants.DEFAULT_MAX_LENGTH_INPLACE_LOB; 121 private long biggestFileSize; 122 private int allowLiterals = Constants.DEFAULT_ALLOW_LITERALS; 123 124 private static int initialPowerOffCount; 125 private int powerOffCount = initialPowerOffCount; 126 private int closeDelay; 127 private DatabaseCloser delayedCloser; 128 private boolean recovery; 129 private volatile boolean closing; 130 private boolean ignoreCase; 131 private boolean deleteFilesOnDisconnect; 132 private String lobCompressionAlgorithm; 133 private boolean optimizeReuseResults = true; 134 private String cacheType; 135 private boolean indexSummaryValid = true; 136 137 public static void setInitialPowerOffCount(int count) { 138 initialPowerOffCount = count; 139 } 140 141 public void setPowerOffCount(int count) { 142 if(powerOffCount == -1) { 143 return; 144 } 145 powerOffCount = count; 146 } 147 148 public boolean getTextStorage() { 149 return textStorage; 150 } 151 152 public static boolean isTextStorage(String fileName, boolean defaultValue) throws SQLException { 153 byte[] magicText = Constants.MAGIC_FILE_HEADER_TEXT.getBytes(); 154 byte[] magicBinary = Constants.MAGIC_FILE_HEADER.getBytes(); 155 try { 156 byte[] magic; 157 if(FileUtils.isInMemory(fileName)) { 158 MemoryFile file = FileUtils.getMemoryFile(fileName); 159 magic = file.getMagic(); 160 } else { 161 FileInputStream fin = new FileInputStream (fileName); 162 magic = IOUtils.readBytesAndClose(fin, magicBinary.length); 163 } 164 if(ByteUtils.compareNotNull(magic, magicText) == 0) { 165 return true; 166 } else if(ByteUtils.compareNotNull(magic, magicBinary) == 0) { 167 return false; 168 } else if(magic.length < magicText.length) { 169 return defaultValue; 171 } 172 throw Message.getSQLException(Message.FILE_VERSION_ERROR_1, fileName); 173 } catch(IOException e) { 174 throw Message.convert(e); 175 } 176 } 177 178 public static byte[] getMagic(boolean textStorage) { 179 if(textStorage) { 180 return Constants.MAGIC_FILE_HEADER_TEXT.getBytes(); 181 } else { 182 return Constants.MAGIC_FILE_HEADER.getBytes(); 183 } 184 } 185 186 public byte[] getMagic() { 187 return getMagic(textStorage); 188 } 189 190 public boolean areEqual(Value a, Value b) throws SQLException { 191 return a.compareTo(b, compareMode) == 0; 200 } 201 202 public int compare(Value a, Value b) throws SQLException { 203 return a.compareTo(b, compareMode); 204 } 205 206 public int compareTypeSave(Value a, Value b) throws SQLException { 207 return a.compareTypeSave(b, compareMode); 208 } 209 210 public long getModificationDataId() { 211 return modificationDataId; 212 } 213 214 public long getNextModificationDataId() { 215 return ++modificationDataId; 216 } 217 218 public long getModificationMetaId() { 219 return modificationMetaId; 220 } 221 222 public long getNextModificationMetaId() { 223 modificationDataId++; 225 return modificationMetaId++; 226 } 227 228 public int getPowerOffCount() { 229 return powerOffCount; 230 } 231 232 public void checkPowerOff() throws SQLException { 233 if(powerOffCount==0) { 234 return; 235 } 236 if(powerOffCount > 1) { 237 powerOffCount--; 238 return; 239 } 240 if(powerOffCount != -1) { 241 try { 242 powerOffCount = -1; 243 if(log != null) { 244 try { 245 stopWriter(); 246 log.close(); 247 } catch(SQLException e) { 248 } 250 log = null; 251 } 252 if(fileData != null) { 253 try { 254 fileData.close(); 255 } catch(SQLException e) { 256 } 258 fileData = null; 259 } 260 if(fileIndex != null) { 261 try { 262 fileIndex.close(); 263 } catch(SQLException e) { 264 } 266 fileIndex = null; 267 } 268 if(lock != null) { 269 lock.unlock(); 270 lock = null; 271 } 272 if(emergencyReserve != null) { 273 emergencyReserve.closeAndDeleteSilently(); 274 emergencyReserve = null; 275 } 276 } catch(Exception e) { 277 TraceSystem.traceThrowable(e); 278 } 279 } 280 Engine.getInstance().close(databaseName); 281 throw Message.getSQLException(Message.SIMULATED_POWER_OFF); 282 } 283 284 public static boolean exists(String name) { 285 return FileUtils.exists(name+Constants.SUFFIX_DATA_FILE); 286 } 287 288 public Trace getTrace(String module) { 289 return traceSystem.getTrace(module); 290 } 291 292 public FileStore openFile(String name, boolean mustExist) throws SQLException { 293 return openFile(name, false, mustExist); 294 } 295 296 public FileStore openFile(String name, boolean notEncrypted, boolean mustExist) throws SQLException { 297 String c = notEncrypted ? null : cipher; 298 byte[] h = notEncrypted ? null : filePasswordHash; 299 if(mustExist && !FileUtils.exists(name)) { 300 throw Message.getSQLException(Message.FILE_CORRUPTED_1, name); 301 } 302 FileStore store = FileStore.open(this, name, getMagic(), c, h); 303 try { 304 store.init(); 305 } catch(SQLException e) { 306 store.closeSilently(); 307 throw e; 308 } 309 return store; 310 } 311 312 public void checkFilePasswordHash(String c, byte[] hash) throws JdbcSQLException { 313 if(!ByteUtils.compareSecure(hash, filePasswordHash) || !StringUtils.equals(c, cipher)) { 314 throw Message.getSQLException(Message.WRONG_USER_OR_PASSWORD); 315 } 316 } 317 318 private void openFileData() throws SQLException { 319 fileData = new DiskFile(this, databaseName+Constants.SUFFIX_DATA_FILE, true, true, Constants.DEFAULT_CACHE_SIZE); 320 } 321 322 private void openFileIndex() throws SQLException { 323 fileIndex = new DiskFile(this, databaseName+Constants.SUFFIX_INDEX_FILE, false, logIndexChanges, Constants.DEFAULT_CACHE_SIZE_INDEX); 324 } 325 326 public DataPage getDataPage() { 327 return dummy; 328 } 329 330 private String parseDatabaseShortName() { 331 String n = databaseName; 332 if(n.endsWith(":")) { 333 n = null; 334 } 335 if(n != null) { 336 StringTokenizer tokenizer = new StringTokenizer (n, "/\\:,;"); 337 while(tokenizer.hasMoreTokens()) { 338 n = tokenizer.nextToken(); 339 } 340 } 341 if(n == null || n.length() == 0) { 342 n = "UNNAMED"; 343 } 344 return StringUtils.toUpperEnglish(n); 345 } 346 347 public Database(String name, ConnectionInfo ci, String cipher) throws SQLException { 348 this.compareMode = new CompareMode(null, null); 349 this.persistent = ci.isPersistent(); 350 this.filePasswordHash = ci.getFilePasswordHash(); 351 this.databaseName = name; 352 this.databaseShortName = parseDatabaseShortName(); 353 this.cipher = cipher; 354 String lockMethodName = ci.removeProperty("FILE_LOCK", null); 355 this.fileLockMethod = FileLock.getFileLockMethod(lockMethodName); 356 this.textStorage = ci.getTextStorage(); 357 this.databaseURL = ci.getURL(); 358 String listener = ci.removeProperty("DATABASE_EVENT_LISTENER", null); 359 if(listener != null) { 360 if(listener.startsWith("'")) { 361 listener = listener.substring(1); 362 } 363 if(listener.endsWith("'")) { 364 listener = listener.substring(0, listener.length()-1); 365 } 366 setEventListener(listener); 367 } 368 String log = ci.getProperty(SetTypes.LOG, null); 369 if(log != null) { 370 this.logIndexChanges = log.equals("2"); 371 } 372 String ignoreSummary = ci.getProperty("RECOVER", null); 373 if(ignoreSummary != null) { 374 this.recovery = true; 375 } 376 boolean closeAtVmShutdown = ci.removeProperty("DB_CLOSE_ON_EXIT", true); 377 int traceLevelFile = ci.getIntProperty(SetTypes.TRACE_LEVEL_FILE, TraceSystem.DEFAULT_TRACE_LEVEL_FILE); 378 int traceLevelSystemOut = ci.getIntProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT, TraceSystem.DEFAULT_TRACE_LEVEL_SYSTEM_OUT); 379 this.cacheType = StringUtils.toUpperEnglish(ci.removeProperty("CACHE_TYPE", CacheLRU.TYPE_NAME)); 380 try { 381 synchronized(this) { 382 open(traceLevelFile, traceLevelSystemOut); 383 } 384 if(closeAtVmShutdown) { 385 DatabaseCloser closeOnExit = new DatabaseCloser(this, 0, true); 386 try { 387 Runtime.getRuntime().addShutdownHook(closeOnExit); 388 } catch(IllegalStateException e) { 389 } 392 } 393 } catch(SQLException e) { 394 if(traceSystem != null) { 395 traceSystem.getTrace(Trace.DATABASE).error("opening " + databaseName, e); 396 } 397 synchronized(this) { 398 closeOpenFilesAndUnlock(); 399 } 400 throw Message.convert(e); 401 } 402 } 403 404 private void open(int traceLevelFile, int traceLevelSystemOut) throws SQLException { 405 if(persistent) { 406 String dataFileName = databaseName + Constants.SUFFIX_DATA_FILE; 407 if(FileUtils.exists(dataFileName)) { 408 readOnly = FileUtils.isReadOnly(dataFileName); 409 textStorage = isTextStorage(dataFileName, textStorage); 410 } 411 } 412 dummy = DataPage.create(this, 0); 413 if(persistent) { 414 if(readOnly || FileUtils.isInMemory(databaseName)) { 415 traceSystem = new TraceSystem(null); 416 } else { 417 traceSystem = new TraceSystem(databaseName+Constants.SUFFIX_TRACE_FILE); 418 } 419 traceSystem.setLevelFile(traceLevelFile); 420 traceSystem.setLevelSystemOut(traceLevelSystemOut); 421 traceSystem.getTrace(Trace.DATABASE).info("opening " + databaseName + " (build "+Constants.BUILD_ID+")"); 422 if(!readOnly && fileLockMethod != FileLock.LOCK_NO) { 423 lock = new FileLock(traceSystem, Constants.LOCK_SLEEP); 424 lock.lock(databaseName+Constants.SUFFIX_LOCK_FILE, fileLockMethod == FileLock.LOCK_SOCKET); 425 } 426 deleteOldTempFiles(); 427 log = new LogSystem(this, databaseName, readOnly); 428 openFileData(); 429 openFileIndex(); 430 if(!readOnly) { 431 log.recover(); 432 } 433 fileData.init(); 434 try { 435 fileIndex.init(); 436 } catch(SQLException e) { 437 if(recovery) { 438 traceSystem.getTrace(Trace.DATABASE).error("opening index", e); 439 fileIndex.close(); 440 fileIndex.delete(); 441 openFileIndex(); 442 } else { 443 throw e; 444 } 445 } 446 reserveLobFileObjectIds(); 447 writer = WriterThread.create(this, writeDelay); 448 } else { 449 traceSystem = new TraceSystem(null); 450 log = new LogSystem(null, null, false); 451 } 452 systemUser = new User(this, 0, Constants.DBA_NAME, true); 453 mainSchema = new Schema(this, 0, Constants.SCHEMA_MAIN, systemUser, true); 454 infoSchema = new Schema(this, 0, Constants.SCHEMA_INFORMATION, systemUser, true); 455 schemas.put(mainSchema.getName(), mainSchema); 456 schemas.put(infoSchema.getName(), infoSchema); 457 publicRole = new Role(this, 0, Constants.PUBLIC_ROLE_NAME, true); 458 roles.put(Constants.PUBLIC_ROLE_NAME, publicRole); 459 systemUser.setAdmin(true); 460 systemSession = new Session(this, systemUser, 0); 461 ObjectArray cols = new ObjectArray(); 463 Column columnId = new Column("ID", Value.INT, 0, 0); 464 columnId.setNullable(false); 465 cols.add(columnId); 466 cols.add(new Column("HEAD", Value.INT, 0, 0)); 467 cols.add(new Column("TYPE", Value.INT, 0, 0)); 468 cols.add(new Column("SQL", Value.STRING, 0, 0)); 469 meta = new TableData(mainSchema, "SYS", 0, cols, persistent); 470 metaIdIndex = meta.addIndex(systemSession, "SYS_ID", 0, new Column[]{columnId}, IndexType.createPrimaryKey(false, false), Index.EMPTY_HEAD, null); 471 objectIds.set(0); 472 addMetaData(MetaTable.TABLES); 474 addMetaData(MetaTable.COLUMNS); 475 addMetaData(MetaTable.INDEXES); 476 addMetaData(MetaTable.TABLE_TYPES); 477 addMetaData(MetaTable.TYPE_INFO); 478 addMetaData(MetaTable.CATALOGS); 479 addMetaData(MetaTable.SETTINGS); 480 addMetaData(MetaTable.HELP); 481 addMetaData(MetaTable.SEQUENCES); 482 addMetaData(MetaTable.USERS); 483 addMetaData(MetaTable.ROLES); 484 addMetaData(MetaTable.RIGHTS); 485 addMetaData(MetaTable.FUNCTION_ALIASES); 486 addMetaData(MetaTable.SCHEMATA); 487 addMetaData(MetaTable.TABLE_PRIVILEGES); 488 addMetaData(MetaTable.COLUMN_PRIVILEGES); 489 addMetaData(MetaTable.COLLATIONS); 490 addMetaData(MetaTable.VIEWS); 491 addMetaData(MetaTable.IN_DOUBT); 492 addMetaData(MetaTable.CROSS_REFERENCES); 493 addMetaData(MetaTable.CONSTRAINTS); 494 addMetaData(MetaTable.FUNCTION_COLUMNS); 495 addMetaData(MetaTable.CONSTANTS); 496 addMetaData(MetaTable.DOMAINS); 497 addMetaData(MetaTable.TRIGGERS); 498 starting = true; 499 Cursor cursor = metaIdIndex.find(systemSession, null, null); 500 ObjectArray records = new ObjectArray(); 503 while (cursor.next()) { 504 MetaRecord rec = new MetaRecord(cursor.get()); 505 objectIds.set(rec.getId()); 506 records.add(rec); 507 } 508 MetaRecord.sort(records); 509 for(int i=0; i<records.size(); i++) { 510 MetaRecord rec = (MetaRecord) records.get(i); 511 rec.execute(this, systemSession, eventListener); 512 } 513 recompileInvalidViews(); 515 starting = false; 516 addDefaultSetting(SetTypes.DEFAULT_LOCK_TIMEOUT, null, Constants.INITIAL_LOCK_TIMEOUT); 517 addDefaultSetting(SetTypes.DEFAULT_TABLE_TYPE, null, Constants.DEFAULT_TABLE_TYPE); 518 addDefaultSetting(SetTypes.TRACE_LEVEL_FILE, null, traceSystem.getLevelFile()); 519 addDefaultSetting(SetTypes.TRACE_LEVEL_SYSTEM_OUT, null, traceSystem.getLevelSystemOut()); 520 addDefaultSetting(SetTypes.CACHE_SIZE, null, Constants.DEFAULT_CACHE_SIZE); 521 addDefaultSetting(SetTypes.CLUSTER, Constants.CLUSTERING_DISABLED, 0); 522 addDefaultSetting(SetTypes.WRITE_DELAY, null, Constants.DEFAULT_WRITE_DELAY); 523 removeUnusedStorages(); 524 systemSession.commit(); 525 if(!readOnly) { 526 emergencyReserve = openFile(createTempFile(), false); 527 emergencyReserve.autoDelete(); 528 emergencyReserve.setLength(Constants.EMERGENCY_SPACE_INITIAL); 529 } 530 traceSystem.getTrace(Trace.DATABASE).info("opened " + databaseName); 531 } 532 533 private void recompileInvalidViews() { 534 boolean recompileSuccessful; 535 do { 536 recompileSuccessful = false; 537 ObjectArray list = getAllSchemaObjects(DbObject.TABLE_OR_VIEW); 538 for(int i=0; i<list.size(); i++) { 539 DbObject obj = (DbObject) list.get(i); 540 if(obj instanceof TableView) { 541 TableView view = (TableView) obj; 542 if(view.getInvalid()) { 543 try { 544 view.recompile(systemSession); 545 } catch(Throwable e) { 546 } 548 if(!view.getInvalid()) { 549 recompileSuccessful = true; 550 } 551 } 552 } 553 } 554 } while(recompileSuccessful); 555 } 556 557 private void removeUnusedStorages() throws SQLException { 558 if(persistent) { 559 for(int i=0; i<storages.size(); i++) { 560 Storage storage = (Storage) storages.get(i); 561 if(storage != null && storage.getRecordReader()==null) { 562 storage.delete(systemSession); 563 } 564 } 565 } 566 } 567 568 private void addDefaultSetting(int type, String stringValue, int intValue) throws SQLException { 569 if(readOnly) { 570 return; 571 } 572 String name = SetTypes.getTypeName(type); 573 if(settings.get(name) == null) { 574 Setting setting = new Setting(this, allocateObjectId(false, true), name); 575 if(stringValue == null) { 576 setting.setIntValue(intValue); 577 } else { 578 setting.setStringValue(stringValue); 579 } 580 addDatabaseObject(systemSession, setting); 581 } 582 } 583 584 public void removeStorage(int id, DiskFile file) { 585 if(Constants.CHECK) { 586 Storage s = (Storage) storages.get(id); 587 if(s==null || s.getDiskFile() != file) { 588 throw Message.getInternalError(); 589 } 590 } 591 storages.set(id, null); 592 } 593 594 public Storage getStorage(int id, DiskFile file) { 595 Storage storage = null; 596 if(storages.size() > id) { 597 storage = (Storage) storages.get(id); 598 if(storage != null) { 599 if(Constants.CHECK && storage.getDiskFile() != file) { 600 throw Message.getInternalError(); 601 } 602 } 603 } 604 if(storage == null) { 605 storage = new Storage(this, file, null, id); 606 while(storages.size()<=id) { 607 storages.add(null); 608 } 609 storages.set(id, storage); 610 } 611 return storage; 612 } 613 614 private void addMetaData(int type) throws SQLException { 615 MetaTable m = new MetaTable(infoSchema, type); 616 infoSchema.add(m); 617 } 618 619 private void addMeta(Session session, DbObject obj) throws SQLException { 620 if(obj.getTemporary()) { 621 return; 622 } 623 Row r = meta.getTemplateRow(); 624 MetaRecord rec = new MetaRecord(obj); 625 rec.setRecord(r); 626 objectIds.set(obj.getId()); 627 meta.lock(session, true); 628 meta.addRow(session, r); 629 } 630 631 private void removeMeta(Session session, int id) throws SQLException { 632 SearchRow r = meta.getTemplateSimpleRow(false); 633 r.setValue(0, ValueInt.get(id)); 634 Cursor cursor = metaIdIndex.find(session, r, r); 635 cursor.next(); 636 Row found = cursor.get(); 637 if(found!=null) { 638 meta.lock(session, true); 639 meta.removeRow(session, found); 640 objectIds.clear(id); 641 if(Constants.CHECK) { 642 checkMetaFree(id); 643 } 644 } 645 } 646 647 private HashMap getMap(int type) { 648 switch(type) { 649 case DbObject.USER: 650 return users; 651 case DbObject.SETTING: 652 return settings; 653 case DbObject.ROLE: 654 return roles; 655 case DbObject.RIGHT: 656 return rights; 657 case DbObject.FUNCTION_ALIAS: 658 return functionAliases; 659 case DbObject.SCHEMA: 660 return schemas; 661 case DbObject.USER_DATATYPE: 662 return userDataTypes; 663 case DbObject.COMMENT: 664 return comments; 665 default: 666 throw Message.getInternalError("type="+type); 667 } 668 } 669 670 public void addSchemaObject(Session session, SchemaObject obj) throws SQLException { 671 obj.getSchema().add(obj); 672 int id = obj.getId(); 673 if(id > 0 && !starting) { 674 addMeta(session, obj); 675 } 676 } 677 678 public void addDatabaseObject(Session session, DbObject obj) throws SQLException { 679 HashMap map = getMap(obj.getType()); 680 if(obj.getType()==DbObject.USER) { 681 User user = (User) obj; 682 if(user.getAdmin() && systemUser.getName().equals(Constants.DBA_NAME)) { 683 systemUser.rename(user.getName()); 684 } 685 } 686 String name = obj.getName(); 687 if(Constants.CHECK && map.get(name) != null) { 688 throw Message.getInternalError("object already exists"); 689 } 690 int id = obj.getId(); 691 if(id > 0 && !starting) { 692 addMeta(session, obj); 693 } 694 map.put(name, obj); 695 } 696 697 public Setting findSetting(String name) { 698 return (Setting) settings.get(name); 699 } 700 701 public Comment findComment(DbObject object) { 702 if(object.getType() == DbObject.COMMENT) { 703 return null; 704 } 705 String key = Comment.getKey(object); 706 return (Comment) comments.get(key); 707 } 708 709 public User findUser(String name) { 710 return (User) users.get(name); 711 } 712 713 public FunctionAlias findFunctionAlias(String name) { 714 return (FunctionAlias) functionAliases.get(name); 715 } 716 717 public UserDataType findUserDataType(String name) { 718 return (UserDataType) userDataTypes.get(name); 719 } 720 721 public User getUser(String name) throws SQLException { 722 User user = (User) users.get(name); 723 if (user == null) { 724 throw Message.getSQLException(Message.WRONG_USER_OR_PASSWORD, name); 726 } 727 return user; 728 } 729 730 public synchronized Session createSession(User user) { 731 Session session = new Session(this, user, nextSessionId++); 732 sessions.add(session); 733 traceSystem.getTrace(Trace.SESSION).info("connecting #" + session.getId() + " to " + databaseName); 734 if(delayedCloser != null) { 735 delayedCloser.reset(); 736 delayedCloser = null; 737 } 738 return session; 739 } 740 741 public synchronized void removeSession(Session session) throws SQLException { 742 if(session != null) { 743 sessions.remove(session); 744 if(session != systemSession) { 745 traceSystem.getTrace(Trace.SESSION).info("disconnecting #" + session.getId()); 746 } 747 } 748 if (sessions.size() == 0 && session != systemSession) { 749 if(closeDelay == 0) { 750 close(false); 751 } else if(closeDelay < 0) { 752 return; 753 } else { 754 delayedCloser = new DatabaseCloser(this, closeDelay * 1000, false); 755 delayedCloser.setName("H2 Close Delay " + getShortName()); 756 delayedCloser.setDaemon(true); 757 delayedCloser.start(); 758 } 759 } 760 if(session != systemSession && session != null) { 761 traceSystem.getTrace(Trace.SESSION).info("disconnected #" + session.getId()); 762 } 763 } 764 765 synchronized void close(boolean fromShutdownHook) { 766 this.closing = true; 767 if(sessions.size() > 0) { 768 if(!fromShutdownHook) { 769 return; 770 } 771 traceSystem.getTrace(Trace.DATABASE).info("closing " + databaseName + " from shutdown hook"); 772 Session[] all = new Session[sessions.size()]; 773 sessions.toArray(all); 774 for(int i=0; i<all.length; i++) { 775 Session s = all[i]; 776 try { 777 s.close(); 778 } catch(SQLException e) { 779 traceSystem.getTrace(Trace.SESSION).error("disconnecting #" + s.getId(), e); 780 } 781 } 782 } 783 traceSystem.getTrace(Trace.DATABASE).info("closing " + databaseName); 784 if(eventListener != null) { 785 eventListener.closingDatabase(); 786 eventListener = null; 787 } 788 try { 789 if(persistent && fileData != null) { 790 ObjectArray tablesAndViews = getAllSchemaObjects(DbObject.TABLE_OR_VIEW); 791 for (int i=0; i<tablesAndViews.size(); i++) { 792 Table table = (Table) tablesAndViews.get(i); 793 table.close(systemSession); 794 } 795 ObjectArray sequences = getAllSchemaObjects(DbObject.SEQUENCE); 796 for (int i=0; i<sequences.size(); i++) { 797 Sequence sequence = (Sequence) sequences.get(i); 798 sequence.close(); 799 } 800 meta.close(systemSession); 801 indexSummaryValid = true; 802 } 803 } catch(SQLException e) { 804 traceSystem.getTrace(Trace.DATABASE).error("close", e); 805 } 806 try { 807 closeOpenFilesAndUnlock(); 808 } catch(SQLException e) { 809 traceSystem.getTrace(Trace.DATABASE).error("close", e); 810 } 811 traceSystem.getTrace(Trace.DATABASE).info("closed"); 812 traceSystem.close(); 813 Engine.getInstance().close(databaseName); 814 if(deleteFilesOnDisconnect && persistent) { 815 deleteFilesOnDisconnect = false; 816 String directory = FileUtils.getParent(databaseName); 817 try { 818 DeleteDbFiles.execute(directory, databaseShortName, true); 819 } catch(Exception e) { 820 } 822 } 823 } 824 825 private void stopWriter() { 826 if(writer != null) { 827 writer.stopThread(); 828 writer = null; 829 } 830 } 831 832 private void closeOpenFilesAndUnlock() throws SQLException { 833 if (log != null) { 834 stopWriter(); 835 log.close(); 836 log = null; 837 } 838 closeFiles(); 839 deleteOldTempFiles(); 840 if(systemSession != null) { 841 systemSession.close(); 842 systemSession = null; 843 } 844 if (lock != null) { 845 lock.unlock(); 846 lock = null; 847 } 848 } 849 850 private void closeFiles() throws SQLException { 851 try { 852 if(fileData != null) { 853 fileData.close(); 854 fileData = null; 855 } 856 if(fileIndex != null) { 857 fileIndex.close(); 858 fileIndex = null; 859 } 860 } catch(SQLException e) { 861 traceSystem.getTrace(Trace.DATABASE).error("close", e); 862 } 863 storages = new ObjectArray(); 864 } 865 866 private void checkMetaFree(int id) throws SQLException { 867 SearchRow r = meta.getTemplateSimpleRow(false); 868 r.setValue(0, ValueInt.get(id)); 869 Cursor cursor = metaIdIndex.find(systemSession, r, r); 870 cursor.next(); 871 if(cursor.getPos() != Cursor.POS_NO_ROW) { 872 throw Message.getInternalError(); 873 } 874 } 875 876 public int allocateObjectId(boolean needFresh, boolean dataFile) { 877 needFresh=true; 879 int i; 880 if(needFresh) { 881 i = objectIds.getLastSetBit()+1; 882 if((i & 1) != (dataFile ? 1 : 0)) { 883 i++; 884 } 885 while(i<storages.size() || objectIds.get(i)) { 886 i++; 887 if((i & 1) != (dataFile ? 1 : 0)) { 888 i++; 889 } 890 } 891 } else { 892 i = objectIds.nextClearBit(0); 893 } 894 if(Constants.CHECK && objectIds.get(i)) { 895 throw Message.getInternalError(); 896 } 897 objectIds.set(i); 898 return i; 899 } 900 901 public ObjectArray getAllSettings() { 902 return new ObjectArray(settings.values()); 903 } 904 905 public ObjectArray getAllUsers() { 906 return new ObjectArray(users.values()); 907 } 908 909 public ObjectArray getAllRoles() { 910 return new ObjectArray(roles.values()); 911 } 912 913 public ObjectArray getAllRights() { 914 return new ObjectArray(rights.values()); 915 } 916 917 public ObjectArray getAllComments() { 918 return new ObjectArray(comments.values()); 919 } 920 921 public ObjectArray getAllSchemas() { 922 return new ObjectArray(schemas.values()); 923 } 924 925 public ObjectArray getAllFunctionAliases() { 926 return new ObjectArray(functionAliases.values()); 927 } 928 929 public ObjectArray getAllUserDataTypes() { 930 return new ObjectArray(userDataTypes.values()); 931 } 932 933 public ObjectArray getAllSchemaObjects(int type) { 934 ObjectArray list = new ObjectArray(); 935 for(Iterator it = schemas.values().iterator(); it.hasNext(); ) { 936 Schema schema = (Schema)it.next(); 937 list.addAll(schema.getAll(type)); 938 } 939 return list; 940 } 941 942 public String getShortName() { 943 return databaseShortName; 944 } 945 946 public String getName() { 947 return databaseName; 948 } 949 950 public LogSystem getLog() { 951 return log; 952 } 953 954 public synchronized Session[] getSessions() { 955 Session[] list = new Session[sessions.size()]; 956 sessions.toArray(list); 957 return list; 958 } 959 960 public void update(Session session, DbObject obj) throws SQLException { 961 int id = obj.getId(); 962 removeMeta(session, id); 963 addMeta(session, obj); 964 } 965 966 public void renameSchemaObject(Session session, SchemaObject obj, String newName) throws SQLException { 967 obj.getSchema().rename(obj, newName); 968 updateWithChildren(session, obj); 969 } 970 971 private void updateWithChildren(Session session, DbObject obj) throws SQLException { 972 ObjectArray list = obj.getChildren(); 973 Comment comment = findComment(obj); 974 if(comment != null) { 975 throw Message.getInternalError(); 976 } 977 update(session, obj); 978 for(int i = 0; list!=null && i<list.size(); i++) { 980 DbObject o = (DbObject)list.get(i); 981 if(o.getCreateSQL() != null) { 982 update(session, o); 983 } 984 } 985 } 986 987 public void renameDatabaseObject(Session session, DbObject obj, String newName) throws SQLException { 988 int type = obj.getType(); 989 HashMap map = getMap(type); 990 if(Constants.CHECK) { 991 if(!map.containsKey(obj.getName())) { 992 throw Message.getInternalError("not found: "+obj.getName()); 993 } 994 if(obj.getName().equals(newName) || map.containsKey(newName)) { 995 throw Message.getInternalError("object already exists: "+newName); 996 } 997 } 998 int id = obj.getId(); 999 removeMeta(session, id); 1000 map.remove(obj.getName()); 1001 obj.rename(newName); 1002 map.put(newName, obj); 1003 updateWithChildren(session, obj); 1004 } 1005 1006 public String createTempFile() throws SQLException { 1007 try { 1008 return FileUtils.createTempFile(databaseName, Constants.SUFFIX_TEMP_FILE, true); 1009 } catch (IOException e) { 1010 throw Message.convert(e); 1011 } 1012 } 1013 1014 private void reserveLobFileObjectIds() throws SQLException { 1015 String prefix = FileUtils.normalize(databaseName); 1016 String path = FileUtils.getParent(databaseName); 1017 String [] list = FileUtils.listFiles(path); 1018 for(int i=0; i<list.length; i++) { 1019 String name = list[i]; 1020 if(name.endsWith(Constants.SUFFIX_LOB_FILE) && FileUtils.fileStartsWith(name, prefix)) { 1021 name = name.substring(prefix.length() + 1); 1022 name = name.substring(0, name.length() - Constants.SUFFIX_LOB_FILE.length()); 1023 int dot = name.indexOf('.'); 1024 if(dot >= 0) { 1025 String id = name.substring(dot + 1); 1026 int objectId = Integer.parseInt(id); 1027 objectIds.set(objectId); 1028 } 1029 } 1030 } 1031 } 1032 1033 private void deleteOldTempFiles() throws SQLException { 1034 if(emergencyReserve != null) { 1035 emergencyReserve.closeAndDeleteSilently(); 1036 emergencyReserve = null; 1037 } 1038 String path = FileUtils.getParent(databaseName); 1039 String prefix = FileUtils.normalize(databaseName); 1040 String [] list = FileUtils.listFiles(path); 1041 for(int i=0; i<list.length; i++) { 1042 String name = list[i]; 1043 if(name.endsWith(Constants.SUFFIX_TEMP_FILE) && FileUtils.fileStartsWith(name, prefix)) { 1044 FileUtils.tryDelete(name); 1046 } 1047 } 1048 } 1049 1050 public Storage getStorage(RecordReader reader, int id, boolean dataFile) { 1051 DiskFile file; 1052 if(dataFile) { 1053 file = fileData; 1054 } else { 1055 file = fileIndex; 1056 } 1057 Storage storage = getStorage(id, file); 1058 storage.setReader(reader); 1059 return storage; 1060 } 1061 1062 public Role findRole(String roleName) { 1063 return (Role) roles.get(roleName); 1064 } 1065 1066 public Schema findSchema(String schemaName) { 1067 return (Schema) schemas.get(schemaName); 1068 } 1069 1070 public Schema getSchema(String schemaName) throws SQLException { 1071 Schema schema = findSchema(schemaName); 1072 if(schema == null) { 1073 throw Message.getSQLException(Message.SCHEMA_NOT_FOUND_1, schemaName); 1074 } 1075 return schema; 1076 } 1077 1078 public void removeDatabaseObject(Session session, DbObject obj) throws SQLException { 1079 String objName = obj.getName(); 1080 int type = obj.getType(); 1081 HashMap map = getMap(type); 1082 if(Constants.CHECK && !map.containsKey(objName)) { 1083 throw Message.getInternalError("not found: "+objName); 1084 } 1085 Comment comment = findComment(obj); 1086 if(comment != null) { 1087 removeDatabaseObject(session, comment); 1088 } 1089 int id = obj.getId(); 1090 obj.removeChildrenAndResources(session); 1091 map.remove(objName); 1092 removeMeta(session, id); 1093 } 1094 1095 private String getFirstInvalidTable() { 1096 String conflict = null; 1097 try { 1098 ObjectArray list = getAllSchemaObjects(DbObject.TABLE_OR_VIEW); 1099 for(int i=0; i<list.size(); i++) { 1100 Table t = (Table)list.get(i); 1101 conflict = t.getSQL(); 1102 systemSession.prepare(t.getCreateSQL()); 1103 } 1104 } catch(SQLException e) { 1105 return conflict; 1106 } 1107 return null; 1108 } 1109 1110 public void removeSchemaObject(Session session, SchemaObject obj) throws SQLException { 1111 if(obj.getType() == DbObject.TABLE_OR_VIEW) { 1112 Table table = (Table) obj; 1113 if(table.getTemporary() && !table.getGlobalTemporary()) { 1114 session.removeLocalTempTable(table); 1115 return; 1116 } 1117 } 1118 Comment comment = findComment(obj); 1119 if(comment != null) { 1120 removeDatabaseObject(session, comment); 1121 } 1122 obj.getSchema().remove(session, obj); 1123 String invalid = getFirstInvalidTable(); 1124 if(invalid != null) { 1125 obj.getSchema().add(obj); 1126 throw Message.getSQLException(Message.CANT_DROP_2, new String []{obj.getSQL(), invalid}, null); 1127 } 1128 int id = obj.getId(); 1129 obj.removeChildrenAndResources(session); 1130 removeMeta(session, id); 1131 } 1132 1133 public boolean isPersistent() { 1134 return persistent; 1135 } 1136 1137 public TraceSystem getTraceSystem() { 1138 return traceSystem; 1139 } 1140 1141 public DiskFile getDataFile() { 1142 return fileData; 1143 } 1144 1145 public DiskFile getIndexFile() { 1146 return fileIndex; 1147 } 1148 1149 public void setCacheSize(int value) throws SQLException { 1150 if(fileData != null) { 1151 synchronized(fileData) { 1152 fileData.getCache().setMaxSize(value); 1153 } 1154 int valueIndex = value <= (1<<8) ? value : (value>>>Constants.CACHE_SIZE_INDEX_SHIFT); 1155 synchronized(fileIndex) { 1156 fileIndex.getCache().setMaxSize(valueIndex); 1157 } 1158 cacheSize = value; 1159 } 1160 } 1161 1162 public void setMasterUser(User user) throws SQLException { 1163 synchronized(this) { 1164 addDatabaseObject(systemSession, user); 1165 systemSession.commit(); 1166 } 1167 } 1168 1169 public Role getPublicRole() { 1170 return publicRole; 1171 } 1172 1173 public String getTempTableName(int sessionId) { 1174 String tempName; 1175 for(int i = 0;; i++) { 1176 tempName = Constants.TEMP_TABLE_PREFIX + sessionId + "_" + i; 1177 if(mainSchema.findTableOrView(null, tempName) == null) { 1178 break; 1179 } 1180 } 1181 return tempName; 1182 } 1183 1184 public void setCompareMode(CompareMode compareMode) { 1185 this.compareMode = compareMode; 1186 } 1187 1188 public CompareMode getCompareMode() { 1189 return compareMode; 1190 } 1191 1192 public String getCluster() { 1193 return cluster; 1194 } 1195 1196 public void setCluster(String cluster) { 1197 this.cluster = cluster; 1198 } 1199 1200 public void checkWritingAllowed() throws SQLException { 1201 if(readOnly) { 1202 throw Message.getSQLException(Message.DATABASE_IS_READ_ONLY); 1203 } 1204 if(noDiskSpace) { 1205 throw Message.getSQLException(Message.NO_DISK_SPACE_AVAILABLE); 1206 } 1207 } 1208 1209 public boolean getReadOnly() { 1210 return readOnly; 1211 } 1212 1213 public void setWriteDelay(int value) { 1214 writeDelay = value; 1215 if(writer != null) { 1216 writer.setWriteDelay(value); 1217 } 1218 } 1219 1220 public Class loadClass(String className) throws ClassNotFoundException { 1221 return Class.forName(className); 1222 } 1223 1224 public void setEventListener(String className) throws SQLException { 1225 if(className == null || className.length() == 0) { 1226 eventListener = null; 1227 } else { 1228 try { 1229 eventListener = (DatabaseEventListener)loadClass(className).newInstance(); 1230 eventListener.init(databaseURL); 1231 } catch (Throwable e) { 1232 throw Message.getSQLException(Message.ERROR_SETTING_DATABASE_EVENT_LISTENER, new String []{className}, e); 1233 } 1234 } 1235 } 1236 1237 public void freeUpDiskSpace() throws SQLException { 1238 long sizeAvailable = 0; 1239 if(emergencyReserve != null) { 1240 sizeAvailable = emergencyReserve.length(); 1241 long newLength = sizeAvailable / 2; 1242 if(newLength < Constants.EMERGENCY_SPACE_MIN) { 1243 newLength = 0; 1244 noDiskSpace = true; 1245 } 1246 emergencyReserve.setLength(newLength); 1247 } 1248 if(eventListener != null) { 1249 eventListener.diskSpaceIsLow(sizeAvailable); 1250 } 1251 } 1252 1253 public void setProgress(int state, String name, int x, int max) { 1254 if(eventListener != null) { 1255 try { 1256 eventListener.setProgress(state, name, x, max); 1257 } catch(Exception e2) { 1258 } 1260 } 1261 } 1262 1263 public void exceptionThrown(SQLException e) { 1264 if(eventListener != null) { 1265 try { 1266 eventListener.exceptionThrown(e); 1267 } catch(Exception e2) { 1268 } 1270 } 1271 } 1272 1273 public void sync() throws SQLException { 1274 if(log != null) { 1275 log.sync(); 1276 } 1277 if(fileData != null) { 1278 fileData.sync(); 1279 } 1280 if(fileIndex != null) { 1281 fileIndex.sync(); 1282 } 1283 } 1284 1285 public int getMaxMemoryRows() { 1286 return maxMemoryRows; 1287 } 1288 1289 public void setMaxMemoryRows(int value) { 1290 this.maxMemoryRows = value; 1291 } 1292 1293 public void setMaxMemoryUndo(int value) { 1294 this.maxMemoryUndo = value; 1295 } 1296 1297 public int getMaxMemoryUndo() { 1298 return maxMemoryUndo; 1299 } 1300 1301 public int getChecksum(byte[] data, int start, int end) { 1302 int x = 0; 1303 while(start < end) { 1304 x += data[start++]; 1305 } 1306 return x; 1307 } 1308 1309 public void setLockMode(int lockMode) { 1310 this.lockMode = lockMode; 1311 } 1312 1313 public int getLockMode() { 1314 return lockMode; 1315 } 1316 1317 public synchronized void setCloseDelay(int value) { 1318 this.closeDelay = value; 1319 } 1320 1321 public boolean getLogIndexChanges() { 1322 return logIndexChanges; 1323 } 1324 1325 public void setLog(int level) throws SQLException { 1326 if(logLevel == level) { 1327 return; 1328 } 1329 boolean logData; 1330 boolean logIndex; 1331 switch(level) { 1332 case 0: 1333 logData = false; 1334 logIndex = false; 1335 break; 1336 case 1: 1337 logData = true; 1338 logIndex = false; 1339 break; 1340 case 2: 1341 logData = true; 1342 logIndex = true; 1343 break; 1344 default: 1345 throw Message.getInternalError("level="+level); 1346 } 1347 if(fileIndex != null) { 1348 fileIndex.setLogChanges(logIndex); 1349 } 1350 if(log != null) { 1351 log.setDisabled(!logData); 1352 log.checkpoint(); 1353 } 1354 logLevel = level; 1355 } 1356 1357 public ObjectArray getAllStorages() { 1358 return storages; 1359 } 1360 1361 public boolean getRecovery() { 1362 return recovery; 1363 } 1364 1365 public Session getSystemSession() { 1366 return systemSession; 1367 } 1368 1369 public String getDatabasePath() { 1370 if(persistent) { 1371 File parent = new File (databaseName).getAbsoluteFile(); 1372 return parent.getAbsolutePath(); 1373 } else { 1374 return null; 1375 } 1376 } 1377 1378 public void handleInvalidChecksum() throws SQLException { 1379 SQLException e = Message.getSQLException(Message.FILE_CORRUPTED_1, "wrong checksum"); 1380 if(!recovery) { 1381 throw e; 1382 } else { 1383 traceSystem.getTrace(Trace.DATABASE).error("recover", e); 1384 } 1385 } 1386 1387 public boolean isClosing() { 1388 return closing; 1389 } 1390 1391 public int getWriteDelay() { 1392 return writeDelay; 1393 } 1394 1395 public int getCacheSize() { 1396 return cacheSize; 1397 } 1398 1399 public void setMaxLengthInplaceLob(int value) { 1400 this.maxLengthInplaceLob = value; 1401 } 1402 1403 public int getMaxLengthInplaceLob() { 1404 return maxLengthInplaceLob; 1405 } 1406 1407 public void setIgnoreCase(boolean b) { 1408 ignoreCase = b; 1409 } 1410 1411 public boolean getIgnoreCase() { 1412 if(starting) { 1413 return false; 1415 } 1416 return ignoreCase; 1417 } 1418 1419 public synchronized void setDeleteFilesOnDisconnect(boolean b) { 1420 this.deleteFilesOnDisconnect = b; 1421 } 1422 1423 public String getLobCompressionAlgorithm(int type) { 1424 return lobCompressionAlgorithm; 1425 } 1426 1427 public void setLobCompressionAlgorithm(String stringValue) { 1428 this.lobCompressionAlgorithm = stringValue; 1429 } 1430 1431 public void notifyFileSize(long length) { 1432 if(length > biggestFileSize) { 1433 biggestFileSize = length; 1434 setMaxLogSize(0); 1435 } 1436 } 1437 1438 public void setMaxLogSize(long value) { 1439 long minLogSize = biggestFileSize / Constants.LOG_SIZE_DIVIDER; 1440 minLogSize = Math.max(value, minLogSize); 1441 long currentLogSize = getLog().getMaxLogSize(); 1442 if(minLogSize > currentLogSize || (value > 0 && minLogSize > value)) { 1443 value = minLogSize; 1445 } 1446 if(value > 0) { 1447 getLog().setMaxLogSize(value); 1448 } 1449 } 1450 1451 public void setAllowLiterals(int value) { 1452 this.allowLiterals = value; 1453 } 1454 1455 public int getAllowLiterals() { 1456 if(starting) { 1457 return Constants.ALLOW_LITERALS_ALL; 1458 } 1459 return allowLiterals; 1460 } 1461 1462 public boolean getOptimizeReuseResults() { 1463 return optimizeReuseResults; 1464 } 1465 1466 public void setOptimizeReuseResults(boolean b) { 1467 optimizeReuseResults = b; 1468 } 1469 1470 public String getCacheType() { 1471 return cacheType; 1472 } 1473 1474 public void invalidateIndexSummary() throws SQLException { 1475 if(indexSummaryValid ) { 1476 indexSummaryValid = false; 1477 log.invalidateIndexSummary(); 1478 } 1479 } 1480 1481 public boolean getIndexSummaryValid() { 1482 return indexSummaryValid; 1483 } 1484 1485} 1486 | Popular Tags |