michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_places_Database_h_ michael@0: #define mozilla_places_Database_h_ michael@0: michael@0: #include "MainThreadUtils.h" michael@0: #include "nsWeakReference.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsIObserver.h" michael@0: #include "mozilla/storage.h" michael@0: #include "mozilla/storage/StatementCache.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsIEventTarget.h" michael@0: michael@0: // This is the schema version. Update it at any schema change and add a michael@0: // corresponding migrateVxx method below. michael@0: #define DATABASE_SCHEMA_VERSION 23 michael@0: michael@0: // Fired after Places inited. michael@0: #define TOPIC_PLACES_INIT_COMPLETE "places-init-complete" michael@0: // Fired when initialization fails due to a locked database. michael@0: #define TOPIC_DATABASE_LOCKED "places-database-locked" michael@0: // This topic is received when the profile is about to be lost. Places does michael@0: // initial shutdown work and notifies TOPIC_PLACES_SHUTDOWN to all listeners. michael@0: // Any shutdown work that requires the Places APIs should happen here. michael@0: #define TOPIC_PROFILE_CHANGE_TEARDOWN "profile-change-teardown" michael@0: // This topic is received just before the profile is lost. Places begins michael@0: // shutting down the connection and notifies TOPIC_PLACES_WILL_CLOSE_CONNECTION michael@0: // to all listeners. Only critical database cleanups should happen here, michael@0: // some APIs may bail out already. michael@0: #define TOPIC_PROFILE_BEFORE_CHANGE "profile-before-change" michael@0: // Fired when Places is shutting down. Any code should stop accessing Places michael@0: // APIs after this notification. If you need to listen for Places shutdown michael@0: // you should only use this notification, next ones are intended only for michael@0: // internal Places use. michael@0: #define TOPIC_PLACES_SHUTDOWN "places-shutdown" michael@0: // For Internal use only. Fired when connection is about to be closed, only michael@0: // cleanup tasks should run at this stage, nothing should be added to the michael@0: // database, nor APIs should be called. michael@0: #define TOPIC_PLACES_WILL_CLOSE_CONNECTION "places-will-close-connection" michael@0: // Fired when the connection has gone, nothing will work from now on. michael@0: #define TOPIC_PLACES_CONNECTION_CLOSED "places-connection-closed" michael@0: michael@0: class nsIStringBundle; michael@0: class nsIRunnable; michael@0: michael@0: namespace mozilla { michael@0: namespace places { michael@0: michael@0: enum JournalMode { michael@0: // Default SQLite journal mode. michael@0: JOURNAL_DELETE = 0 michael@0: // Can reduce fsyncs on Linux when journal is deleted (See bug 460315). michael@0: // We fallback to this mode when WAL is unavailable. michael@0: , JOURNAL_TRUNCATE michael@0: // Unsafe in case of crashes on database swap or low memory. michael@0: , JOURNAL_MEMORY michael@0: // Can reduce number of fsyncs. We try to use this mode by default. michael@0: , JOURNAL_WAL michael@0: }; michael@0: michael@0: class Database MOZ_FINAL : public nsIObserver michael@0: , public nsSupportsWeakReference michael@0: { michael@0: typedef mozilla::storage::StatementCache StatementCache; michael@0: typedef mozilla::storage::StatementCache AsyncStatementCache; michael@0: michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: Database(); michael@0: michael@0: /** michael@0: * Initializes the database connection and the schema. michael@0: * In case of corruption the database is copied to a backup file and replaced. michael@0: */ michael@0: nsresult Init(); michael@0: michael@0: /** michael@0: * Finalizes the cached statements and closes the database connection. michael@0: * A TOPIC_PLACES_CONNECTION_CLOSED notification is fired when done. michael@0: */ michael@0: void Shutdown(); michael@0: michael@0: /** michael@0: * Getter to use when instantiating the class. michael@0: * michael@0: * @return Singleton instance of this class. michael@0: */ michael@0: static already_AddRefed GetDatabase() michael@0: { michael@0: return GetSingleton(); michael@0: } michael@0: michael@0: /** michael@0: * Returns last known database status. michael@0: * michael@0: * @return one of the nsINavHistoryService::DATABASE_STATUS_* constants. michael@0: */ michael@0: uint16_t GetDatabaseStatus() const michael@0: { michael@0: return mDatabaseStatus; michael@0: } michael@0: michael@0: /** michael@0: * Returns a pointer to the storage connection. michael@0: * michael@0: * @return The connection handle. michael@0: */ michael@0: mozIStorageConnection* MainConn() const michael@0: { michael@0: return mMainConn; michael@0: } michael@0: michael@0: /** michael@0: * Dispatches a runnable to the connection async thread, to be serialized michael@0: * with async statements. michael@0: * michael@0: * @param aEvent michael@0: * The runnable to be dispatched. michael@0: */ michael@0: void DispatchToAsyncThread(nsIRunnable* aEvent) const michael@0: { michael@0: if (mClosed) { michael@0: return; michael@0: } michael@0: nsCOMPtr target = do_GetInterface(mMainConn); michael@0: if (target) { michael@0: (void)target->Dispatch(aEvent, NS_DISPATCH_NORMAL); michael@0: } michael@0: } michael@0: michael@0: ////////////////////////////////////////////////////////////////////////////// michael@0: //// Statements Getters. michael@0: michael@0: /** michael@0: * Gets a cached synchronous statement. michael@0: * michael@0: * @param aQuery michael@0: * SQL query literal. michael@0: * @return The cached statement. michael@0: * @note Always null check the result. michael@0: * @note Always use a scoper to reset the statement. michael@0: */ michael@0: template michael@0: already_AddRefed michael@0: GetStatement(const char (&aQuery)[N]) const michael@0: { michael@0: nsDependentCString query(aQuery, N - 1); michael@0: return GetStatement(query); michael@0: } michael@0: michael@0: /** michael@0: * Gets a cached synchronous statement. michael@0: * michael@0: * @param aQuery michael@0: * nsCString of SQL query. michael@0: * @return The cached statement. michael@0: * @note Always null check the result. michael@0: * @note Always use a scoper to reset the statement. michael@0: */ michael@0: already_AddRefed michael@0: GetStatement(const nsACString& aQuery) const michael@0: { michael@0: if (mShuttingDown) { michael@0: return nullptr; michael@0: } michael@0: if (NS_IsMainThread()) { michael@0: return mMainThreadStatements.GetCachedStatement(aQuery); michael@0: } michael@0: return mAsyncThreadStatements.GetCachedStatement(aQuery); michael@0: } michael@0: michael@0: /** michael@0: * Gets a cached asynchronous statement. michael@0: * michael@0: * @param aQuery michael@0: * SQL query literal. michael@0: * @return The cached statement. michael@0: * @note Always null check the result. michael@0: * @note AsyncStatements are automatically reset on execution. michael@0: */ michael@0: template michael@0: already_AddRefed michael@0: GetAsyncStatement(const char (&aQuery)[N]) const michael@0: { michael@0: nsDependentCString query(aQuery, N - 1); michael@0: return GetAsyncStatement(query); michael@0: } michael@0: michael@0: /** michael@0: * Gets a cached asynchronous statement. michael@0: * michael@0: * @param aQuery michael@0: * nsCString of SQL query. michael@0: * @return The cached statement. michael@0: * @note Always null check the result. michael@0: * @note AsyncStatements are automatically reset on execution. michael@0: */ michael@0: already_AddRefed michael@0: GetAsyncStatement(const nsACString& aQuery) const michael@0: { michael@0: if (mShuttingDown) { michael@0: return nullptr; michael@0: } michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: return mMainThreadAsyncStatements.GetCachedStatement(aQuery); michael@0: } michael@0: michael@0: protected: michael@0: /** michael@0: * Initializes the database file. If the database does not exist or is michael@0: * corrupt, a new one is created. In case of corruption it also creates a michael@0: * backup copy of the database. michael@0: * michael@0: * @param aStorage michael@0: * mozStorage service instance. michael@0: * @param aNewDatabaseCreated michael@0: * whether a new database file has been created. michael@0: */ michael@0: nsresult InitDatabaseFile(nsCOMPtr& aStorage, michael@0: bool* aNewDatabaseCreated); michael@0: michael@0: /** michael@0: * Creates a database backup and replaces the original file with a new michael@0: * one. michael@0: * michael@0: * @param aStorage michael@0: * mozStorage service instance. michael@0: */ michael@0: nsresult BackupAndReplaceDatabaseFile(nsCOMPtr& aStorage); michael@0: michael@0: /** michael@0: * Initializes the database. This performs any necessary migrations for the michael@0: * database. All migration is done inside a transaction that is rolled back michael@0: * if any error occurs. michael@0: * @param aDatabaseMigrated michael@0: * Whether a schema upgrade happened. michael@0: */ michael@0: nsresult InitSchema(bool* aDatabaseMigrated); michael@0: michael@0: /** michael@0: * Creates bookmark roots in a new DB. michael@0: */ michael@0: nsresult CreateBookmarkRoots(); michael@0: michael@0: /** michael@0: * Initializes additionale SQLite functions, defined in SQLFunctions.h michael@0: */ michael@0: nsresult InitFunctions(); michael@0: michael@0: /** michael@0: * Initializes triggers defined in nsPlacesTriggers.h michael@0: */ michael@0: nsresult InitTempTriggers(); michael@0: michael@0: /** michael@0: * Helpers used by schema upgrades. michael@0: */ michael@0: nsresult MigrateV7Up(); michael@0: nsresult MigrateV8Up(); michael@0: nsresult MigrateV9Up(); michael@0: nsresult MigrateV10Up(); michael@0: nsresult MigrateV11Up(); michael@0: nsresult MigrateV13Up(); michael@0: nsresult MigrateV14Up(); michael@0: nsresult MigrateV15Up(); michael@0: nsresult MigrateV16Up(); michael@0: nsresult MigrateV17Up(); michael@0: nsresult MigrateV18Up(); michael@0: nsresult MigrateV19Up(); michael@0: nsresult MigrateV20Up(); michael@0: nsresult MigrateV21Up(); michael@0: nsresult MigrateV22Up(); michael@0: nsresult MigrateV23Up(); michael@0: michael@0: nsresult UpdateBookmarkRootTitles(); michael@0: nsresult CheckAndUpdateGUIDs(); michael@0: michael@0: private: michael@0: ~Database(); michael@0: michael@0: /** michael@0: * Singleton getter, invoked by class instantiation. michael@0: */ michael@0: static already_AddRefed GetSingleton(); michael@0: michael@0: static Database* gDatabase; michael@0: michael@0: nsCOMPtr mMainConn; michael@0: michael@0: mutable StatementCache mMainThreadStatements; michael@0: mutable AsyncStatementCache mMainThreadAsyncStatements; michael@0: mutable StatementCache mAsyncThreadStatements; michael@0: michael@0: int32_t mDBPageSize; michael@0: uint16_t mDatabaseStatus; michael@0: bool mShuttingDown; michael@0: bool mClosed; michael@0: }; michael@0: michael@0: } // namespace places michael@0: } // namespace mozilla michael@0: michael@0: #endif // mozilla_places_Database_h_