class LealoneDatabase extends Database
        implements com.lealone.transaction.TransactionEngine.GcTask {

    // ID固定为0
    public static final int ID = 0;
    public static final String NAME = Constants.PROJECT_NAME;

    // 仅用于支持qinsql项目
    private static final CaseInsensitiveMap<String> UNSUPPORTED_SCHEMA_MAP = new CaseInsensitiveMap<>();
    private static final CaseInsensitiveMap<Object[]> CLOSED_DATABASES = new CaseInsensitiveMap<>();
    private static LealoneDatabase INSTANCE = new LealoneDatabase();

    public static LealoneDatabase getInstance() {
        return INSTANCE;
    }

    public static void addUnsupportedSchema(String schemaName) {
        UNSUPPORTED_SCHEMA_MAP.put(schemaName, schemaName);
    }

    public static boolean isUnsupportedSchema(String schemaName) {
        return UNSUPPORTED_SCHEMA_MAP.containsKey(schemaName);
    }

    public static boolean isMe(String dbName) {
        return LealoneDatabase.NAME.equalsIgnoreCase(dbName);
    }

    private LealoneDatabase() {
        super(ID, NAME, null);

        // init执行过程中会触发getInstance()，此时INSTANCE为null，会导致NPE
        INSTANCE = this;
        // 把自己也加进去，这样通过lealone这个名字能找到自己
        addDatabaseObject(null, this, null);

        init();
        createRootUserIfNotExists();
        getTransactionEngine().addGcTask(this);
    }

    public synchronized Database createEmbeddedDatabase(String name, ConnectionInfo ci) {
        Database db = findDatabase(name);
        if (db != null)
            return db;

        HashMap<String, String> parameters = new HashMap<>();
        for (Entry<Object, Object> e : ci.getProperties().entrySet()) {
            parameters.put(e.getKey().toString(), e.getValue().toString());
        }
        int id = ci.getDatabaseId() < 0 ? INSTANCE.allocateObjectId() : ci.getDatabaseId();
        db = new Database(id, name, parameters);
        db.setRunMode(RunMode.EMBEDDED);
        db.init();
        String userName = ci.getUserName();
        byte[] userPasswordHash = ci.getUserPasswordHash();
        db.createAdminUser(userName, userPasswordHash);
        // 新建session，避免使用system session
        try (ServerSession session = createSession(getSystemUser())) {
            DbObjectLock lock = tryExclusiveDatabaseLock(session);
            addDatabaseObject(session, db, lock);
            session.commit();
        }
        return db;
    }

    void closeDatabase(String dbName) {<FILL_FUNCTION_BODY>}

    void dropDatabase(String dbName) {
        synchronized (CLOSED_DATABASES) {
            CLOSED_DATABASES.remove(dbName);
        }
    }

    Map<String, Database> getDatabasesMap() {
        return getDbObjects(DbObjectType.DATABASE);
    }

    public List<Database> getDatabases() {
        synchronized (CLOSED_DATABASES) {
            return new ArrayList<>(getDatabasesMap().values());
        }
    }

    public Database findDatabase(String dbName) {
        return find(DbObjectType.DATABASE, null, dbName);
    }

    /**
     * Get database with the given name.
     * This method throws an exception if the database does not exist.
     * 
     * @param name the database name
     * @return the database
     * @throws DbException if the database does not exist
     */
    public Database getDatabase(String dbName) {
        Database db = findDatabase(dbName);
        if (db == null) {
            synchronized (CLOSED_DATABASES) {
                Object[] a = CLOSED_DATABASES.remove(dbName);
                if (a != null) {
                    MetaRecord.execute(this, getSystemSession(), getEventListener(), (String) a[0],
                            (int) a[1]);
                    db = findDatabase(dbName);
                }
            }
        }
        if (db == null) {
            throw DbException.get(ErrorCode.DATABASE_NOT_FOUND_1, dbName);
        }
        return db;
    }

    public boolean isClosed(String dbName) {
        synchronized (CLOSED_DATABASES) {
            return CLOSED_DATABASES.containsKey(dbName);
        }
    }

    @Override
    public synchronized Database copy() {
        INSTANCE = new LealoneDatabase();
        getTransactionEngine().removeGcTask(this);
        return INSTANCE;
    }

    // 只有用管理员连接到LealoneDatabase才能执行某些语句，比如CREATE/ALTER/DROP DATABASE
    public static void checkAdminRight(ServerSession session, String stmt) {
        if (!isSuperAdmin(session))
            throw DbException.get(ErrorCode.LEALONE_DATABASE_ADMIN_RIGHT_1, stmt);
    }

    public static boolean isSuperAdmin(ServerSession session) {
        Database db = session.getDatabase();
        // 在MySQL兼容模式下只需要当前用户是管理员即可
        if (db.getMode().isMySQL())
            return session.getUser().isAdmin();
        else
            return LealoneDatabase.getInstance() == db && session.getUser().isAdmin();
    }

    @Override
    public void gc(TransactionEngine te) {
        // getDatabases()会copy一份，因为closeIfNeeded()可能会关闭数据库，避免ConcurrentModificationException
        for (Database db : getDatabases()) {
            long metaId = db.getModificationMetaId();
            if (metaId == db.getLastGcMetaId())
                continue;
            db.setLastGcMetaId(metaId);

            // 数据库没有进行初始化时不进行GC
            if (!db.isInitialized())
                continue;
            if (db.getSessionCount() == 0 && db.closeIfNeeded())
                continue;
            for (TransactionalDbObjects tObjects : db.getTransactionalDbObjectsArray()) {
                if (tObjects != null)
                    tObjects.gc(te);
            }
            for (Schema schema : db.getAllSchemas()) {
                for (TransactionalDbObjects tObjects : schema.getTransactionalDbObjectsArray()) {
                    if (tObjects != null)
                        tObjects.gc(te);
                }
            }
        }
    }
}

        Database db = findDatabase(dbName);
        if (db != null) {
            synchronized (CLOSED_DATABASES) {
                getDatabasesMap().remove(dbName); // 要放到同步块中
                CLOSED_DATABASES.put(dbName, new Object[] { db.getCreateSQL(), db.getId() });
            }
        }
    
<methods>public void <init>(int, java.lang.String, Map<java.lang.String,java.lang.String>) ,public void addDataHandler(int, com.lealone.db.DataHandler) ,public void addDatabaseObject(com.lealone.db.session.ServerSession, com.lealone.db.DbObject, com.lealone.db.lock.DbObjectLock) ,public synchronized boolean addWaitingSession(com.lealone.db.session.ServerSession) ,public int allocateObjectId() ,public static void appendMap(com.lealone.common.util.StatementBuilder, Map<java.lang.String,java.lang.String>) ,public boolean areEqual(com.lealone.db.value.Value, com.lealone.db.value.Value) ,public void backupTo(java.lang.String, java.lang.Long) ,public void checkPowerOff() ,public void checkWritingAllowed() ,public void checkpoint() ,public void clearObjectId(int) ,public synchronized boolean closeIfNeeded() ,public int compare(com.lealone.db.value.Value, com.lealone.db.value.Value) ,public int compareTypeSafe(com.lealone.db.value.Value, com.lealone.db.value.Value) ,public synchronized com.lealone.db.Database copy() ,public synchronized com.lealone.db.Database copy(boolean) ,public com.lealone.sql.SQLParser createParser(com.lealone.db.session.Session) ,public void createRootUserIfNotExists() ,public com.lealone.db.session.ServerSession createSession(com.lealone.db.auth.User) ,public synchronized com.lealone.db.session.ServerSession createSession(com.lealone.db.auth.User, com.lealone.db.ConnectionInfo) ,public com.lealone.db.session.ServerSession createSession(com.lealone.db.auth.User, com.lealone.db.scheduler.Scheduler) ,public synchronized com.lealone.db.session.ServerSession createSession(com.lealone.db.auth.User, com.lealone.db.ConnectionInfo, com.lealone.db.scheduler.Scheduler) ,public synchronized void drop() ,public boolean equalsIdentifiers(java.lang.String, java.lang.String) ,public void exceptionThrown(java.sql.SQLException, java.lang.String) ,public com.lealone.db.Comment findComment(com.lealone.db.session.ServerSession, com.lealone.db.DbObject) ,public com.lealone.db.result.Row findMeta(com.lealone.db.session.ServerSession, int) ,public com.lealone.db.PluginObject findPluginObject(com.lealone.db.session.ServerSession, java.lang.String) ,public com.lealone.db.auth.Role findRole(com.lealone.db.session.ServerSession, java.lang.String) ,public com.lealone.db.schema.Schema findSchema(com.lealone.db.session.ServerSession, java.lang.String) ,public com.lealone.db.auth.User findUser(com.lealone.db.session.ServerSession, java.lang.String) ,public ArrayList<com.lealone.db.Comment> getAllComments() ,public ArrayList<com.lealone.db.PluginObject> getAllPluginObjects() ,public ArrayList<com.lealone.db.auth.Right> getAllRights() ,public ArrayList<com.lealone.db.auth.Role> getAllRoles() ,public ArrayList<com.lealone.db.schema.SchemaObject> getAllSchemaObjects() ,public ArrayList<com.lealone.db.schema.SchemaObject> getAllSchemaObjects(com.lealone.db.DbObjectType) ,public ArrayList<com.lealone.db.schema.Schema> getAllSchemas() ,public ArrayList<com.lealone.db.table.Table> getAllTablesAndViews(boolean) ,public ArrayList<com.lealone.db.auth.User> getAllUsers() ,public int getAllowLiterals() ,public int getCacheSize() ,public List<? extends com.lealone.db.DbObject> getChildren() ,public com.lealone.db.value.CompareMode getCompareMode() ,public com.lealone.db.util.SourceCompiler getCompiler() ,public java.lang.String getCreateSQL() ,public java.lang.String getCreateSQL(boolean) ,public com.lealone.db.DataHandler getDataHandler(int) ,public java.lang.String getDatabasePath() ,public java.lang.String getDefaultStorageEngineName() ,public int getDefaultTableType() ,public com.lealone.db.table.Table getDependentTable(com.lealone.db.schema.SchemaObject, com.lealone.db.table.Table) ,public long getDiskSpaceUsed() ,public com.lealone.db.api.DatabaseEventListener getEventListener() ,public com.lealone.db.session.ServerSession getExclusiveSession() ,public byte[] getFileEncryptionKey() ,public com.lealone.db.table.Table getFirstUserTable() ,public java.lang.String[] getHostIds() ,public boolean getIgnoreCase() ,public java.sql.Connection getInternalConnection() ,public java.sql.Connection getInternalConnection(com.lealone.db.scheduler.Scheduler) ,public long getLastGcMetaId() ,public java.lang.String getLobCompressionAlgorithm(int) ,public com.lealone.storage.lob.LobStorage getLobStorage() ,public int getMaxLengthInplaceLob() ,public int getMaxMemoryRows() ,public int getMaxMemoryUndo() ,public int getMaxOperationMemory() ,public com.lealone.db.index.Cursor getMetaCursor(com.lealone.db.session.ServerSession) ,public com.lealone.storage.Storage getMetaStorage() ,public com.lealone.db.Mode getMode() ,public long getModificationDataId() ,public long getModificationMetaId() ,public long getNextModificationDataId() ,public long getNextModificationMetaId() ,public boolean getOptimizeReuseResults() ,public int getPageSize() ,public Map<java.lang.String,java.lang.String> getParameters() ,public com.lealone.db.auth.Role getPublicRole() ,public boolean getQueryStatistics() ,public com.lealone.db.stat.QueryStatisticsData getQueryStatisticsData() ,public boolean getReferentialIntegrity() ,public com.lealone.db.auth.Role getRole(com.lealone.db.session.ServerSession, java.lang.String) ,public com.lealone.db.RunMode getRunMode() ,public java.lang.String getSQL() ,public com.lealone.sql.SQLEngine getSQLEngine() ,public com.lealone.db.scheduler.SchedulerLock getSchedulerLock() ,public com.lealone.db.schema.Schema getSchema(com.lealone.db.session.ServerSession, java.lang.String) ,public synchronized int getSessionCount() ,public com.lealone.db.session.ServerSession[] getSessions(boolean) ,public com.lealone.db.DbSettings getSettings() ,public java.lang.String getShortName() ,public com.lealone.storage.Storage getStorage(java.lang.String) ,public synchronized com.lealone.storage.Storage getStorage(com.lealone.storage.StorageEngine) ,public com.lealone.storage.StorageBuilder getStorageBuilder(com.lealone.storage.StorageEngine, java.lang.String) ,public java.lang.String getStoragePath() ,public List<com.lealone.storage.Storage> getStorages() ,public com.lealone.db.session.ServerSession getSystemSession() ,public com.lealone.db.auth.User getSystemUser() ,public com.lealone.db.table.TableAlterHistory getTableAlterHistory() ,public java.lang.String getTargetNodes() ,public com.lealone.common.util.TempFileDeleter getTempFileDeleter() ,public com.lealone.common.trace.Trace getTrace(com.lealone.common.trace.TraceModuleType) ,public com.lealone.common.trace.TraceSystem getTraceSystem() ,public com.lealone.transaction.TransactionEngine getTransactionEngine() ,public com.lealone.db.TransactionalDbObjects[] getTransactionalDbObjectsArray() ,public com.lealone.db.DbObjectType getType() ,public com.lealone.db.auth.User getUser(com.lealone.db.session.ServerSession, java.lang.String) ,public synchronized void init() ,public boolean isClosing() ,public boolean isInitialized() ,public boolean isMultiVersion() ,public boolean isObjectIdEnabled(int) ,public boolean isOpened() ,public boolean isPersistent() ,public boolean isPowerOff() ,public boolean isReadOnly() ,public boolean isStarting() ,public boolean isSystemSchema(com.lealone.db.schema.Schema) ,public boolean isTargetNode(com.lealone.net.NetNode) ,public boolean isTemporary() ,public void markClosed() ,public HashMap<java.lang.String,V> newStringMap() ,public com.lealone.storage.fs.FileStorage openFile(java.lang.String, java.lang.String, boolean) ,public java.lang.String quoteIdentifier(java.lang.String) ,public void removeDataHandler(int) ,public void removeDatabaseObject(com.lealone.db.session.ServerSession, com.lealone.db.DbObject, com.lealone.db.lock.DbObjectLock) ,public synchronized void removeSession(com.lealone.db.session.ServerSession) ,public void renameDatabaseObject(com.lealone.db.session.ServerSession, com.lealone.db.DbObject, java.lang.String, com.lealone.db.lock.DbObjectLock) ,public synchronized void setCloseDelay(int) ,public void setCompareMode(com.lealone.db.value.CompareMode) ,public boolean setDbSetting(com.lealone.db.session.ServerSession, com.lealone.db.DbSetting, java.lang.String) ,public void setEventListenerClass(java.lang.String) ,public synchronized void setExclusiveSession(com.lealone.db.session.ServerSession, boolean) ,public void setHostIds(java.lang.String[]) ,public void setLastConnectionInfo(com.lealone.db.ConnectionInfo) ,public void setLastGcMetaId(long) ,public void setLobStorage(com.lealone.storage.lob.LobStorage) ,public void setMode(com.lealone.db.Mode) ,public void setProgress(int, java.lang.String, int, int) ,public void setQueryStatistics(boolean) ,public void setQueryStatisticsMaxEntries(int) ,public void setReadOnly(boolean) ,public void setRunMode(com.lealone.db.RunMode) ,public synchronized void shutdownImmediately() ,public HashMap<java.lang.String,java.lang.Integer> statisticsEnd() ,public void statisticsStart() ,public void tryAddMeta(com.lealone.db.session.ServerSession, com.lealone.db.DbObject) ,public com.lealone.db.lock.DbObjectLock tryExclusiveAuthLock(com.lealone.db.session.ServerSession) ,public com.lealone.db.lock.DbObjectLock tryExclusiveCommentLock(com.lealone.db.session.ServerSession) ,public com.lealone.db.lock.DbObjectLock tryExclusiveDatabaseLock(com.lealone.db.session.ServerSession) ,public com.lealone.db.lock.DbObjectLock tryExclusivePluginLock(com.lealone.db.session.ServerSession) ,public com.lealone.db.lock.DbObjectLock tryExclusiveSchemaLock(com.lealone.db.session.ServerSession) ,public com.lealone.db.result.Row tryLockDbObject(com.lealone.db.session.ServerSession, com.lealone.db.DbObject, int) ,public void tryRemoveMeta(com.lealone.db.session.ServerSession, com.lealone.db.schema.SchemaObject, com.lealone.db.lock.DbObjectLock) ,public boolean updateDbSettings(com.lealone.db.session.ServerSession, Map<java.lang.String,java.lang.String>) ,public void updateMeta(com.lealone.db.session.ServerSession, com.lealone.db.DbObject) ,public void updateMeta(com.lealone.db.session.ServerSession, com.lealone.db.DbObject, com.lealone.db.result.Row) ,public void updateMetaAndFirstLevelChildren(com.lealone.db.session.ServerSession, com.lealone.db.DbObject) ,public boolean validateFilePasswordHash(java.lang.String, byte[]) <variables>private static final java.lang.String SYSTEM_USER_NAME,private final com.lealone.db.lock.DbObjectLock authLock,private int closeDelay,private java.lang.Thread closeOnExitHook,private final com.lealone.db.lock.DbObjectLock commentsLock,private com.lealone.db.value.CompareMode compareMode,private com.lealone.db.util.SourceCompiler compiler,private final ConcurrentHashMap<java.lang.Integer,com.lealone.db.DataHandler> dataHandlers,private final com.lealone.db.lock.DbObjectLock databasesLock,private final com.lealone.db.TransactionalDbObjects[] dbObjectsArray,private volatile com.lealone.db.DbSettings dbSettings,private com.lealone.db.api.DatabaseEventListener eventListener,private com.lealone.db.session.ServerSession exclusiveSession,private java.lang.String[] hostIds,private com.lealone.db.schema.Schema infoSchema,private volatile boolean infoSchemaMetaTablesInitialized,private com.lealone.db.ConnectionInfo lastConnectionInfo,private long lastGcMetaId,private long lastSessionRemovedAt,private com.lealone.storage.lob.LobStorage lobStorage,private com.lealone.db.schema.Schema mainSchema,private com.lealone.db.table.Table meta,private com.lealone.db.index.Index metaIdIndex,private java.lang.String metaStorageEngineName,private com.lealone.db.Mode mode,private final java.util.concurrent.atomic.AtomicLong modificationDataId,private final java.util.concurrent.atomic.AtomicLong modificationMetaId,private int nextSessionId,private HashSet<com.lealone.net.NetNode> nodes,private final com.lealone.common.util.BitField objectIds,private final non-sealed Map<java.lang.String,java.lang.String> parameters,private com.lealone.db.schema.Schema perfSchema,private final non-sealed boolean persistent,private final com.lealone.db.lock.DbObjectLock pluginsLock,private com.lealone.db.auth.Role publicRole,private com.lealone.db.stat.QueryStatisticsData queryStatisticsData,private boolean readOnly,private com.lealone.db.RunMode runMode,private final com.lealone.db.scheduler.SchedulerLock schedulerLock,private final com.lealone.db.lock.DbObjectLock schemasLock,private final non-sealed com.lealone.sql.SQLEngine sqlEngine,private volatile com.lealone.db.Database.State state,private final non-sealed java.lang.String storagePath,private final ConcurrentHashMap<java.lang.String,com.lealone.storage.Storage> storages,private com.lealone.db.session.ServerSession systemSession,private com.lealone.db.auth.User systemUser,private final com.lealone.db.table.TableAlterHistory tableAlterHistory,private java.lang.String targetNodes,private final com.lealone.common.util.TempFileDeleter tempFileDeleter,private com.lealone.common.trace.Trace trace,private com.lealone.common.trace.TraceSystem traceSystem,private final non-sealed com.lealone.transaction.TransactionEngine transactionEngine,private final Set<com.lealone.db.session.ServerSession> userSessions,private LinkedList<com.lealone.db.session.ServerSession> waitingSessions