diff --git a/node_modules/@nozbe/watermelondb/Database/index.d.ts b/node_modules/@nozbe/watermelondb/Database/index.d.ts index 861ed06..d2d6527 100644 --- a/node_modules/@nozbe/watermelondb/Database/index.d.ts +++ b/node_modules/@nozbe/watermelondb/Database/index.d.ts @@ -88,6 +88,8 @@ export default class Database { // Yes, this sucks and there should be some safety mechanisms or warnings. Please contribute! unsafeResetDatabase(): Promise + unsafeVacuum(): Promise + _ensureInWriter(diagnosticMethodName: string): void // (experimental) puts Database in a broken state diff --git a/node_modules/@nozbe/watermelondb/Database/index.js b/node_modules/@nozbe/watermelondb/Database/index.js index fa82516..aa88fe9 100644 --- a/node_modules/@nozbe/watermelondb/Database/index.js +++ b/node_modules/@nozbe/watermelondb/Database/index.js @@ -91,7 +91,9 @@ var Database = /*#__PURE__*/function () { var preparedState = record._preparedState; if (!preparedState) { (0, _common.invariant)('disposable' !== record._raw._status, "Cannot batch a disposable record"); - throw new Error("Cannot batch a record that doesn't have a prepared create/update/delete"); + // throw new Error("Cannot batch a record that doesn't have a prepared create/update/delete"); + console.debug('Trying to batch a record with no prepared state on table', record.constructor.table); + return; } var raw = record._raw; var { @@ -122,6 +124,10 @@ var Database = /*#__PURE__*/function () { // subsequent changes to the record don't trip up the invariant // TODO: What if this fails? record._preparedState = null; + + if ('update' === preparedState) { + record.__original = null; + } } if (!changeNotifications[table]) { changeNotifications[table] = []; @@ -379,6 +385,59 @@ var Database = /*#__PURE__*/function () { } }.bind(this)); }; + + _proto.unsafeVacuum = function unsafeVacuum() { + return new Promise(function ($return, $error) { + var $Try_3_Finally = function ($Try_3_Exit) { + return function ($Try_3_Value) { + try { + // this._isBeingReset = false; + return $Try_3_Exit && $Try_3_Exit.call(this, $Try_3_Value); + } catch ($boundEx) { + return $error($boundEx); + } + }.bind(this); + }.bind(this); + var adapter, ErrorAdapter; + var $Try_3_Post = function () { + try { + return $return(); + } catch ($boundEx) { + return $error($boundEx); + } + }; + var $Try_3_Catch = function ($exception_4) { + try { + throw $exception_4; + } catch ($boundEx) { + return $Try_3_Finally($error)($boundEx); + } + }; + try { + // Kill ability to call adapter methods during reset (to catch bugs if someone does this) + ({ + adapter: adapter + } = this); + ErrorAdapter = require('../adapters/error').default; + this.adapter = new ErrorAdapter(); + + // Vacuum the database + return Promise.resolve(adapter.unsafeVacuum()).then(function () { + try { + // Restore working Database + this._resetCount += 1; + this.adapter = adapter; + return $Try_3_Finally($Try_3_Post)(); + } catch ($boundEx) { + return $Try_3_Catch($boundEx); + } + }.bind(this), $Try_3_Catch); + } catch ($exception_4) { + $Try_3_Catch($exception_4) + } + }.bind(this)); + }; + _proto._ensureInWriter = function _ensureInWriter(diagnosticMethodName) { (0, _common.invariant)(this._workQueue.isWriterRunning, "".concat(diagnosticMethodName, " can only be called from inside of a Writer. See docs for more details.")); } diff --git a/node_modules/@nozbe/watermelondb/Model/index.d.ts b/node_modules/@nozbe/watermelondb/Model/index.d.ts index 96114ec..ecfe3c1 100644 --- a/node_modules/@nozbe/watermelondb/Model/index.d.ts +++ b/node_modules/@nozbe/watermelondb/Model/index.d.ts @@ -61,6 +61,8 @@ export default class Model { // database.batch() prepareUpdate(recordUpdater?: (_: this) => void): this + cancelPrepareUpdate(): void + prepareMarkAsDeleted(): this prepareDestroyPermanently(): this diff --git a/node_modules/@nozbe/watermelondb/Model/index.js b/node_modules/@nozbe/watermelondb/Model/index.js index 10ee0a5..3d0bf77 100644 --- a/node_modules/@nozbe/watermelondb/Model/index.js +++ b/node_modules/@nozbe/watermelondb/Model/index.js @@ -80,7 +80,17 @@ var Model = /*#__PURE__*/function () { */; _proto.prepareUpdate = function prepareUpdate(recordUpdater = _noop.default) { var _this = this; - (0, _invariant.default)(!this._preparedState, "Cannot update a record with pending changes"); + if ('deleted' === this._raw._status) { + console.debug("Updating a deleted record in table " + _this.table); + return this; + } + + // (0, _invariant.default)(!this._preparedState, "Cannot update a record with pending changes"); + if (this._preparedState) { + console.debug("Updating a record with pending changes in table " + _this.table) + } else { + this.__original = Object.assign({}, this._raw); + } this.__ensureNotDisposable("Model.prepareUpdate()"); this._isEditing = true; @@ -125,6 +135,21 @@ var Model = /*#__PURE__*/function () { }.bind(this)); } + _proto.cancelPrepareUpdate = function cancelPrepareUpdate() { + var _this = this; + + if ('test' !== process.env.NODE_ENV && 'undefined' !== typeof process && process) { + (0, _invariant.default)('update' === _this._preparedState, "Cannot cancel an update on a model that has not been prepared in table " + _this.table); + } + + this.__changes = null; + this._preparedState = null; + if (this.__original) { + this._raw = this.__original; + } + this.__original = undefined; + } + /** * Prepares record to be marked as deleted * @@ -173,7 +198,10 @@ var Model = /*#__PURE__*/function () { * @see {Database#batch} */; _proto.prepareDestroyPermanently = function prepareDestroyPermanently() { - (0, _invariant.default)(!this._preparedState, "Cannot destroy permanently a record with pending changes"); + // (0, _invariant.default)(!this._preparedState, "Cannot destroy permanently a record with pending changes"); + if (this._preparedState) { + console.debug("Deleting a record with pending changes in table " + this.table); + } this.__ensureNotDisposable("Model.prepareDestroyPermanently()"); this._raw._status = 'deleted'; this._preparedState = 'destroyPermanently'; diff --git a/node_modules/@nozbe/watermelondb/adapters/compat.d.ts b/node_modules/@nozbe/watermelondb/adapters/compat.d.ts index 51879c7..435ad72 100644 --- a/node_modules/@nozbe/watermelondb/adapters/compat.d.ts +++ b/node_modules/@nozbe/watermelondb/adapters/compat.d.ts @@ -44,6 +44,8 @@ export default class DatabaseAdapterCompat { unsafeResetDatabase(): Promise + unsafeVacuum(): Promise + unsafeExecute(work: UnsafeExecuteOperations): Promise getLocal(key: string): Promise diff --git a/node_modules/@nozbe/watermelondb/adapters/compat.js b/node_modules/@nozbe/watermelondb/adapters/compat.js index 4a0b5ef..0980828 100644 --- a/node_modules/@nozbe/watermelondb/adapters/compat.js +++ b/node_modules/@nozbe/watermelondb/adapters/compat.js @@ -76,6 +76,12 @@ var DatabaseAdapterCompat = /*#__PURE__*/function () { return _this11.underlyingAdapter.unsafeResetDatabase(callback); }); }; + _proto.unsafeVacuum = function unsafeVacuum() { + var _this16 = this; + return (0, _Result.toPromise)(function (callback) { + return _this16.underlyingAdapter.unsafeVacuum(callback); + }); + } _proto.unsafeExecute = function unsafeExecute(work) { var _this12 = this; return (0, _Result.toPromise)(function (callback) { diff --git a/node_modules/@nozbe/watermelondb/adapters/error.js b/node_modules/@nozbe/watermelondb/adapters/error.js index 70a4c11..453dca2 100644 --- a/node_modules/@nozbe/watermelondb/adapters/error.js +++ b/node_modules/@nozbe/watermelondb/adapters/error.js @@ -13,7 +13,7 @@ var throwError = function (name) { var ErrorAdapter = /*#__PURE__*/function () { function ErrorAdapter() { var _this = this; - ['find', 'query', 'queryIds', 'count', 'batch', 'getDeletedRecords', 'destroyDeletedRecords', 'unsafeResetDatabase', 'getLocal', 'setLocal', 'removeLocal', 'testClone'].forEach(function (name) { + ['find', 'query', 'queryIds', 'count', 'batch', 'getDeletedRecords', 'destroyDeletedRecords', 'unsafeResetDatabase', 'unsafeVacuum', 'getLocal', 'setLocal', 'removeLocal', 'testClone'].forEach(function (name) { // $FlowFixMe _this[name] = function () { return throwError(name); diff --git a/node_modules/@nozbe/watermelondb/adapters/lokijs/common.d.ts b/node_modules/@nozbe/watermelondb/adapters/lokijs/common.d.ts index 5528dc1..0408198 100644 --- a/node_modules/@nozbe/watermelondb/adapters/lokijs/common.d.ts +++ b/node_modules/@nozbe/watermelondb/adapters/lokijs/common.d.ts @@ -13,6 +13,7 @@ export type WorkerExecutorType = | 'batch' | 'getDeletedRecords' | 'unsafeResetDatabase' + | 'unsafeVacuum' | 'unsafeExecute' | 'getLocal' | 'setLocal' diff --git a/node_modules/@nozbe/watermelondb/adapters/lokijs/index.d.ts b/node_modules/@nozbe/watermelondb/adapters/lokijs/index.d.ts index e09fb14..51d42d9 100644 --- a/node_modules/@nozbe/watermelondb/adapters/lokijs/index.d.ts +++ b/node_modules/@nozbe/watermelondb/adapters/lokijs/index.d.ts @@ -114,6 +114,8 @@ export default class LokiJSAdapter implements DatabaseAdapter { unsafeResetDatabase(callback: ResultCallback): void + unsafeVacuum(callback: ResultCallback): void + unsafeExecute(operations: UnsafeExecuteOperations, callback: ResultCallback): void getLocal(key: string, callback: ResultCallback): void diff --git a/node_modules/@nozbe/watermelondb/adapters/lokijs/index.js b/node_modules/@nozbe/watermelondb/adapters/lokijs/index.js index 9d5ea84..aa8397d 100644 --- a/node_modules/@nozbe/watermelondb/adapters/lokijs/index.js +++ b/node_modules/@nozbe/watermelondb/adapters/lokijs/index.js @@ -105,6 +105,9 @@ var LokiJSAdapter = /*#__PURE__*/function () { _proto.unsafeResetDatabase = function unsafeResetDatabase(callback) { this._dispatcher.call('unsafeResetDatabase', [], callback); }; + _proto.unsafeVacuum = function unssafeVacuum(callback) { + this._dispatcher.call('unsafeVacuum', [], callback); + }; _proto.unsafeExecute = function unsafeExecute(operations, callback) { this._dispatcher.call('unsafeExecute', [operations], callback); }; diff --git a/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseBridge.js b/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseBridge.js index ca35a79..7079572 100644 --- a/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseBridge.js +++ b/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseBridge.js @@ -33,7 +33,7 @@ var DatabaseBridge = /*#__PURE__*/function () { type: type, payload: payload } = action; - if ('setUp' === type || 'unsafeResetDatabase' === type) { + if ('setUp' === type || 'unsafeResetDatabase' === type || 'unsafeVacuum' === type) { this.processActionAsync(action); } else { var response = this._driverAction(type).apply(void 0, (0, _toConsumableArray2.default)(payload)); diff --git a/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseDriver.js b/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseDriver.js index ee89cff..9c1f6ce 100644 --- a/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseDriver.js +++ b/node_modules/@nozbe/watermelondb/adapters/lokijs/worker/DatabaseDriver.js @@ -215,6 +215,11 @@ var DatabaseDriver = /*#__PURE__*/function () { }.bind(this), $error); }.bind(this)); } + _proto.unsafeVacuum = function unsafeVacuum() { + return new Promise(function ($return, $error) { + return Promise.resolve(0); + }.bind(this)); + } // *** LocalStorage *** ; diff --git a/node_modules/@nozbe/watermelondb/adapters/sqlite/index.d.ts b/node_modules/@nozbe/watermelondb/adapters/sqlite/index.d.ts index 88c43a0..cef8232 100644 --- a/node_modules/@nozbe/watermelondb/adapters/sqlite/index.d.ts +++ b/node_modules/@nozbe/watermelondb/adapters/sqlite/index.d.ts @@ -85,6 +85,8 @@ export default class SQLiteAdapter implements DatabaseAdapter { unsafeResetDatabase(callback: ResultCallback): void + unsafeVacuum(callback: ResultCallback): void + unsafeExecute(operations: UnsafeExecuteOperations, callback: ResultCallback): void getLocal(key: string, callback: ResultCallback): void diff --git a/node_modules/@nozbe/watermelondb/adapters/sqlite/index.js b/node_modules/@nozbe/watermelondb/adapters/sqlite/index.js index bb80355..897f4d5 100644 --- a/node_modules/@nozbe/watermelondb/adapters/sqlite/index.js +++ b/node_modules/@nozbe/watermelondb/adapters/sqlite/index.js @@ -238,6 +238,14 @@ var SQLiteAdapter = /*#__PURE__*/function () { callback(result); }); }; + _proto.unsafeVacuum = function unsafeVacuum(callback) { + this._dispatcher.call('unsafeVacuum', [], function (result) { + if (result.value) { + _common.logger.log('[SQLite] Database is now vacuumed'); + } + callback(result); + }); + }; _proto.unsafeExecute = function unsafeExecute(operations, callback) { if ('production' !== process.env.NODE_ENV) { (0, _common.invariant)(operations && 'object' === typeof operations && 1 === Object.keys(operations).length && (Array.isArray(operations.sqls) || 'string' === typeof operations.sqlString), "unsafeExecute expects an { sqls: [ [sql, [args..]], ... ] } or { sqlString: 'foo; bar' } object"); diff --git a/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseBridge.js b/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseBridge.js index a93b0a7..df63896 100644 --- a/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseBridge.js +++ b/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseBridge.js @@ -113,6 +113,14 @@ var DatabaseBridge = /*#__PURE__*/function () { }); }); }; + _proto.unsafeVacuum = function unsafeVacuum(tag, schema, schemaVersion, resolve, reject) { + this.withDriver(tag, resolve, reject, 'unsafeVacuum', function (driver) { + return driver.unsafeVacuum({ + version: schemaVersion, + sql: schema + }); + }); + }; _proto.getLocal = function getLocal(tag, key, resolve, reject) { this.withDriver(tag, resolve, reject, 'getLocal', function (driver) { return driver.getLocal(key); diff --git a/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseDriver.js b/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseDriver.js index 483884c..c325299 100644 --- a/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseDriver.js +++ b/node_modules/@nozbe/watermelondb/adapters/sqlite/sqlite-node/DatabaseDriver.js @@ -198,6 +198,10 @@ var DatabaseDriver = /*#__PURE__*/function () { _this5.database.userVersion = schema.version; }); }; + _proto.unsafeVacuum = function unsafeVacuum() { + var _this5 = this; + this.database.unsafeVacuum(); + }; _proto.migrate = function migrate(migrations) { var _this6 = this; var databaseVersion = this.database.userVersion; diff --git a/node_modules/@nozbe/watermelondb/adapters/sqlite/type.d.ts b/node_modules/@nozbe/watermelondb/adapters/sqlite/type.d.ts index 8131fa1..6053628 100644 --- a/node_modules/@nozbe/watermelondb/adapters/sqlite/type.d.ts +++ b/node_modules/@nozbe/watermelondb/adapters/sqlite/type.d.ts @@ -49,6 +49,7 @@ export type SqliteDispatcherMethod = | 'unsafeLoadFromSync' | 'provideSyncJson' | 'unsafeResetDatabase' + | 'unsafeVacuum' | 'getLocal' | 'unsafeExecuteMultiple' diff --git a/node_modules/@nozbe/watermelondb/adapters/type.d.ts b/node_modules/@nozbe/watermelondb/adapters/type.d.ts index 027c366..35b5a29 100644 --- a/node_modules/@nozbe/watermelondb/adapters/type.d.ts +++ b/node_modules/@nozbe/watermelondb/adapters/type.d.ts @@ -66,6 +66,9 @@ export interface DatabaseAdapter { // Destroys the whole database, its schema, indexes, everything. unsafeResetDatabase(callback: ResultCallback): void + // Vacuum the whole database. + unsafeVacuum(callback: ResultCallback): void + // Performs work on the underlying database - see concrete DatabaseAdapter implementation for more details unsafeExecute(work: UnsafeExecuteOperations, callback: ResultCallback): void diff --git a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/Database.kt b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/Database.kt index b7f750d..052f5a4 100644 --- a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/Database.kt +++ b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/Database.kt @@ -19,8 +19,7 @@ class Database private constructor(private val db: SQLiteDatabase) { fun getInstance( name: String, context: Context, - openFlags: Int = SQLiteDatabase.CREATE_IF_NECESSARY or - SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING + openFlags: Int = SQLiteDatabase.CREATE_IF_NECESSARY ): Database = synchronized(this) { if (INSTANCES[name]?.isOpen != true) { @@ -37,8 +36,7 @@ class Database private constructor(private val db: SQLiteDatabase) { fun buildDatabase( name: String, context: Context, - openFlags: Int = SQLiteDatabase.CREATE_IF_NECESSARY or - SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING + openFlags: Int = SQLiteDatabase.CREATE_IF_NECESSARY ) = Database(createSQLiteDatabase(name, context, openFlags)) private fun createSQLiteDatabase( @@ -50,6 +48,21 @@ class Database private constructor(private val db: SQLiteDatabase) { if (name == ":memory:" || name.contains("mode=memory")) { context.cacheDir.delete() File(context.cacheDir, name).path + } else if (name.contains("/") || name.contains("file")) { + // Extracts the database name from the path + val dbName = name.substringAfterLast("/") + + // Extracts the real path where the *.db file will be created + val truePath = name.substringAfterLast("file://").substringBeforeLast("/") + + // Creates the directory + if (!truePath.contains("databases")) { + val fileObj = File(truePath, "databases") + fileObj.mkdir() + File("${truePath}/databases", dbName).path + } else { + File(truePath, dbName).path + } } else { // On some systems there is some kind of lock on `/databases` folder ¯\_(ツ)_/¯ context.getDatabasePath("$name.db").path.replace("/databases", "") @@ -131,6 +144,10 @@ class Database private constructor(private val db: SQLiteDatabase) { execute("pragma writable_schema=0") } + fun unsafeVacuum() { + execute("vacuum") + } + private fun getAllTables(): ArrayList { val allTables: ArrayList = arrayListOf() rawQuery(Queries.select_tables).use { diff --git a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseBridge.kt b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseBridge.kt index 62d9d92..2415956 100644 --- a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseBridge.kt +++ b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseBridge.kt @@ -153,6 +153,9 @@ class DatabaseBridge(private val reactContext: ReactApplicationContext) : promise: Promise ) = withDriver(tag, promise) { it.unsafeResetDatabase(Schema(schemaVersion, schema)) } + @ReactMethod + fun unsafeVacuum(tag: ConnectionTag, promise: Promise) = withDriver(tag, promise) { it.unsafeVacuum() } + @ReactMethod fun getLocal(tag: ConnectionTag, key: String, promise: Promise) = withDriver(tag, promise) { it.getLocal(key) } diff --git a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseDriver.kt b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseDriver.kt index 36909ae..eeacc6e 100644 --- a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseDriver.kt +++ b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/DatabaseDriver.kt @@ -177,6 +177,10 @@ class DatabaseDriver(context: Context, dbName: String, unsafeNativeReuse: Boolea } } + fun unsafeVacuum() { + database.unsafeVacuum() + } + fun close() = database.close() private fun markAsCached(table: TableName, id: RecordID) { diff --git a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabase.java b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabase.java index 2f170e0..bac0352 100644 --- a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabase.java +++ b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabase.java @@ -11,6 +11,8 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import kotlin.text.StringsKt; + public class WMDatabase { private final SQLiteDatabase db; @@ -21,7 +23,7 @@ public class WMDatabase { public static Map INSTANCES = new HashMap<>(); public static WMDatabase getInstance(String name, Context context) { - return getInstance(name, context, SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING); + return getInstance(name, context, SQLiteDatabase.CREATE_IF_NECESSARY); } public static WMDatabase getInstance(String name, Context context, int openFlags) { @@ -47,6 +49,22 @@ public class WMDatabase { if (name.equals(":memory:") || name.contains("mode=memory")) { context.getCacheDir().delete(); path = new File(context.getCacheDir(), name).getPath(); + } else if (name.contains("/") || name.contains("file")) { + // Extracts the database name from the path + String dbName = StringsKt.substringAfterLast(name, "/", ""); + + // Extracts the real path where the *.db file will be created + String truePath = StringsKt.substringAfterLast(name, "file://", ""); + truePath = StringsKt.substringBeforeLast(truePath, "/", ""); + + // Creates the directory + if (!truePath.contains("databases")) { + File fileObj = new File(truePath, "databases"); + fileObj.mkdir(); + path = new File("" + truePath + "/databases", dbName).getPath(); + } else { + path = new File(truePath, dbName).getPath(); + } } else { // On some systems there is some kind of lock on `/databases` folder ¯\_(ツ)_/¯ path = context.getDatabasePath("" + name + ".db").getPath().replace("/databases", ""); @@ -172,6 +190,10 @@ public class WMDatabase { }); } + public void unsafeVacuum() { + execute("vacuum"); + } + interface TransactionFunction { void applyTransactionFunction(); } diff --git a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseBridge.java b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseBridge.java index be92429..847746d 100644 --- a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseBridge.java +++ b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseBridge.java @@ -122,6 +122,14 @@ public class WMDatabaseBridge extends ReactContextBaseJavaModule { }, "unsafeResetDatabase"); } + @ReactMethod + public void unsafeVacuum(int tag, Promise promise) { + withDriver(tag, promise, (driver) -> { + driver.unsafeVacuum(); + return null; + }, "unsafeVacuum"); + } + @ReactMethod public void getLocal(int tag, String key, Promise promise) { withDriver(tag, promise, (driver) -> driver.getLocal(key), "getLocal"); diff --git a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseDriver.java b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseDriver.java index 1534830..5c5313c 100644 --- a/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseDriver.java +++ b/node_modules/@nozbe/watermelondb/native/android/src/main/java/com/nozbe/watermelondb/WMDatabaseDriver.java @@ -55,11 +55,9 @@ public class WMDatabaseDriver { public WMDatabaseDriver(Context context, String dbName, boolean unsafeNativeReuse) { this.database = unsafeNativeReuse ? WMDatabase.getInstance(dbName, context, - SQLiteDatabase.CREATE_IF_NECESSARY | - SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) : + SQLiteDatabase.CREATE_IF_NECESSARY) : WMDatabase.buildDatabase(dbName, context, - SQLiteDatabase.CREATE_IF_NECESSARY | - SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING); + SQLiteDatabase.CREATE_IF_NECESSARY); if (BuildConfig.DEBUG) { this.log = Logger.getLogger("DB_Driver"); } else { @@ -230,6 +228,13 @@ public class WMDatabaseDriver { }); } + public void unsafeVacuum() { + if (log != null) { + log.info("Unsafe database vacuum"); + } + database.unsafeVacuum(); + } + private static class SchemaCompatibility { static class Compatible extends SchemaCompatibility { } diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.h b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.h index f199fdc..01ba3b7 100644 --- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.h +++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.h @@ -23,6 +23,7 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL) inTransaction:(BOOL (^)(NSError**))transactionBlock error:(NSError**)errorPtr; - (BOOL) unsafeDestroyEverything:(NSError**)errorPtr; +- (BOOL) unsafeVacuum:(NSError**)errorPtr; @end diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.m b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.m index 04fc2e5..e6d1a5b 100644 --- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.m +++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabase.m @@ -125,6 +125,15 @@ } } +- (BOOL) unsafeVacuum:(NSError**)errorPtr +{ + if (![self executeStatements:@"vacuum" error:errorPtr]) { + return NO; + } + + return YES; +} + - (BOOL) unsafeDestroyEverything:(NSError**)errorPtr { // NOTE: Deleting files by default because it seems simpler, more reliable diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseBridge.m b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseBridge.m index 8a3ab35..9e0550f 100644 --- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseBridge.m +++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseBridge.m @@ -183,6 +183,16 @@ BRIDGE_METHOD(unsafeResetDatabase, }) } +RCT_EXPORT_METHOD(unsafeVacuum:(nonnull NSNumber *)tag + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject +) { + WITH_DRIVER({ + [driver unsafeVacuum:errorPtr]; + return @YES; + }) +} + BRIDGE_METHOD(getLocal, key:(nonnull NSString *)key ) diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseDriver.h b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseDriver.h index 0224cc3..085a10c 100644 --- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseDriver.h +++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/objc/WMDatabaseDriver.h @@ -34,6 +34,7 @@ typedef NS_ENUM(NSInteger, WMDatabaseCompatibility) { - (BOOL) batch:(NSArray *)operations error:(NSError **)errorPtr; - (NSString *) getLocal:(NSString *)key error:(NSError **)errorPtr; - (BOOL) unsafeResetDatabaseWithSchema:(NSString *)sql schemaVersion:(long)version error:(NSError **)errorPtr; +- (BOOL) unsafeVacuum:(NSError **)errorPtr; @end diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/Database.swift b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/Database.swift index b4d7151..6a71821 100644 --- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/Database.swift +++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/Database.swift @@ -136,6 +136,10 @@ class Database { } } + func unsafeVacuum() throws { + try executeStatements("vacuum") + } + private var isInMemoryDatabase: Bool { return path == ":memory:" || path == "file::memory:" || path.contains("?mode=memory") } diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.m b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.m index eb039db..6837d3a 100644 --- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.m +++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.m @@ -63,6 +63,8 @@ WMELON_BRIDGE_METHOD(unsafeResetDatabase, schemaVersion:(nonnull NSNumber *)version ) +WMELON_BRIDGE_METHOD(unsafeVacuum, null) + WMELON_BRIDGE_METHOD(getLocal, key:(nonnull NSString *)key ) diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.swift b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.swift index bca17d8..634818e 100644 --- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.swift +++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseBridge.swift @@ -184,6 +184,15 @@ extension DatabaseBridge { } } + @objc(unsafeVacuum:resolve:reject:) + func unsafeVacuum(tag: ConnectionTag, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock) { + withDriver(tag, resolve, reject) { + try $0.database.unsafeVacuum() + } + } + @objc(getLocal:key:resolve:reject:) func getLocal(tag: ConnectionTag, key: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { diff --git a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseDriver.swift b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseDriver.swift index fd8ba18..6f2b3fe 100644 --- a/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseDriver.swift +++ b/node_modules/@nozbe/watermelondb/native/ios/WatermelonDB/swift/DatabaseDriver.swift @@ -207,6 +207,10 @@ class DatabaseDriver { } } + func unsafeVaccum() throws { + try database.unsafeVacuum() + } + private func migrate(with migrations: MigrationSet) throws { precondition( database.userVersion == migrations.from, diff --git a/node_modules/@nozbe/watermelondb/native/shared/Database.cpp b/node_modules/@nozbe/watermelondb/native/shared/Database.cpp index 8a4e9b4..dc567c6 100644 --- a/node_modules/@nozbe/watermelondb/native/shared/Database.cpp +++ b/node_modules/@nozbe/watermelondb/native/shared/Database.cpp @@ -20,7 +20,7 @@ Database::Database(jsi::Runtime *runtime, std::string path, bool usesExclusiveLo initSql += "pragma temp_store = memory;"; #endif - initSql += "pragma journal_mode = WAL;"; + // initSql += "pragma journal_mode = WAL;"; // set timeout before SQLITE_BUSY error is returned initSql += "pragma busy_timeout = 5000;"; @@ -102,6 +102,10 @@ void Database::unsafeResetDatabase(jsi::String &schema, int schemaVersion) { } } +void Database::unsafeVacuum() { + executeMultiple("vacuum"); +} + void Database::migrate(jsi::String &migrationSql, int fromVersion, int toVersion) { auto &rt = getRt(); const std::lock_guard lock(mutex_); diff --git a/node_modules/@nozbe/watermelondb/native/shared/Database.h b/node_modules/@nozbe/watermelondb/native/shared/Database.h index cf53686..97f5a4b 100644 --- a/node_modules/@nozbe/watermelondb/native/shared/Database.h +++ b/node_modules/@nozbe/watermelondb/native/shared/Database.h @@ -38,6 +38,7 @@ public: void batchJSON(jsi::String &&operationsJson); jsi::Value unsafeLoadFromSync(int jsonId, jsi::Object &schema, std::string preamble, std::string postamble); void unsafeResetDatabase(jsi::String &schema, int schemaVersion); + void unsafeVacuum(); jsi::Value getLocal(jsi::String &key); void executeMultiple(std::string sql); diff --git a/node_modules/@nozbe/watermelondb/native/shared/DatabaseBridge.cpp b/node_modules/@nozbe/watermelondb/native/shared/DatabaseBridge.cpp index ef0f23b..cb49ecd 100644 --- a/node_modules/@nozbe/watermelondb/native/shared/DatabaseBridge.cpp +++ b/node_modules/@nozbe/watermelondb/native/shared/DatabaseBridge.cpp @@ -213,6 +213,18 @@ void Database::install(jsi::Runtime *runtime) { std::abort(); } }); + createMethod(rt, adapter, "unsafeVacuum", 0, [database](jsi::Runtime &rt, const jsi::Value *args) { + assert(database->initialized_); + + try { + database->unsafeVacuum(); + return jsi::Value::undefined(); + } catch (const std::exception &ex) { + consoleError("Failed to vacuum database correctly - " + std::string(ex.what())); + // Partially reset database is likely corrupted, so it's probably less bad to crash + std::abort(); + } + }); createMethod(rt, adapter, "unsafeClose", 0, [database](jsi::Runtime &rt, const jsi::Value *args) { assert(database->initialized_); database->destroy(); diff --git a/node_modules/@nozbe/watermelondb/native/shared/Sqlite.cpp b/node_modules/@nozbe/watermelondb/native/shared/Sqlite.cpp index e08153b..2fca075 100644 --- a/node_modules/@nozbe/watermelondb/native/shared/Sqlite.cpp +++ b/node_modules/@nozbe/watermelondb/native/shared/Sqlite.cpp @@ -72,6 +72,10 @@ void SqliteDb::destroy() { consoleLog("Database closed."); } +void SqliteDb::markAsDestroyed() { + isDestroyed_ = true; +} + SqliteDb::~SqliteDb() { destroy(); } diff --git a/node_modules/@nozbe/watermelondb/native/shared/Sqlite.h b/node_modules/@nozbe/watermelondb/native/shared/Sqlite.h index 22cffa7..4b74a7f 100644 --- a/node_modules/@nozbe/watermelondb/native/shared/Sqlite.h +++ b/node_modules/@nozbe/watermelondb/native/shared/Sqlite.h @@ -11,6 +11,7 @@ public: SqliteDb(std::string path); ~SqliteDb(); void destroy(); + void markAsDestroyed(); sqlite3 *sqlite; diff --git a/node_modules/@nozbe/watermelondb/src/Database/index.js b/node_modules/@nozbe/watermelondb/src/Database/index.js index d4ca4e7..75c1f8e 100644 --- a/node_modules/@nozbe/watermelondb/src/Database/index.js +++ b/node_modules/@nozbe/watermelondb/src/Database/index.js @@ -361,6 +361,24 @@ export default class Database { } } + async unsafeVacuum(): Promise { + try { + // Kill ability to call adapter methods during reset (to catch bugs if someone does this) + const { adapter } = this + const ErrorAdapter = require('../adapters/error').default + this.adapter = (new ErrorAdapter(): any) + + // Vacuum the database + await adapter.unsafeVacuum() + + // Restore working Database + this._resetCount += 1 + this.adapter = adapter + } finally { + this._isBeingReset = false + } + } + _ensureInWriter(diagnosticMethodName: string): void { invariant( this._workQueue.isWriterRunning, diff --git a/node_modules/@nozbe/watermelondb/src/adapters/compat.js b/node_modules/@nozbe/watermelondb/src/adapters/compat.js index c5175c0..c8c0c17 100644 --- a/node_modules/@nozbe/watermelondb/src/adapters/compat.js +++ b/node_modules/@nozbe/watermelondb/src/adapters/compat.js @@ -81,6 +81,10 @@ export default class DatabaseAdapterCompat { return toPromise((callback) => this.underlyingAdapter.unsafeResetDatabase(callback)) } + unsafeVacuum(): Promise { + return toPromise((callback) => this.underlyingAdapter.unsafeVacuum(callback)) + } + unsafeExecute(work: UnsafeExecuteOperations): Promise { return toPromise((callback) => this.underlyingAdapter.unsafeExecute(work, callback)) } diff --git a/node_modules/@nozbe/watermelondb/src/adapters/error.js b/node_modules/@nozbe/watermelondb/src/adapters/error.js index 9f1e847..cdd3af4 100644 --- a/node_modules/@nozbe/watermelondb/src/adapters/error.js +++ b/node_modules/@nozbe/watermelondb/src/adapters/error.js @@ -19,6 +19,7 @@ export default class ErrorAdapter { 'getDeletedRecords', 'destroyDeletedRecords', 'unsafeResetDatabase', + 'unsafeVacuum', 'getLocal', 'setLocal', 'removeLocal', diff --git a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/index.js b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/index.js index 0111c87..2f9456a 100644 --- a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/index.js +++ b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/index.js @@ -340,6 +340,20 @@ export default class SQLiteAdapter implements DatabaseAdapter { ) } + unsafeVacuum(callback: ResultCallback { + if (result.value) { + logger.log('[SQLite] Database is vacuumed') + } + callback(result) + }, + ) + } + unsafeExecute(operations: UnsafeExecuteOperations, callback: ResultCallback): void { if (process.env.NODE_ENV !== 'production') { invariant( diff --git a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseBridge.js b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseBridge.js index 480ff4d..ee3935b 100644 --- a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseBridge.js +++ b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseBridge.js @@ -160,6 +160,16 @@ class DatabaseBridge { ) } + unsafeVacuum( + tag: number, + resolve: (any) => void, + reject: (string) => void, + ): void { + this.withDriver(tag, resolve, reject, 'unsafeVacuum', (driver) => + driver.unsafeVacuum(), + ) + } + getLocal(tag: number, key: string, resolve: (any) => void, reject: (string) => void): void { this.withDriver(tag, resolve, reject, 'getLocal', (driver) => driver.getLocal(key)) } diff --git a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseDriver.js b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseDriver.js index fc1351a..5cb7e68 100644 --- a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseDriver.js +++ b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/sqlite-node/DatabaseDriver.js @@ -222,6 +222,10 @@ class DatabaseDriver { }) } + unsafeVacuum(): void { + this.database.unsafeVacuum(); + } + migrate(migrations: Migrations): void { const databaseVersion = this.database.userVersion diff --git a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/type.js b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/type.js index 8cb606b..4ed59bd 100644 --- a/node_modules/@nozbe/watermelondb/src/adapters/sqlite/type.js +++ b/node_modules/@nozbe/watermelondb/src/adapters/sqlite/type.js @@ -85,6 +85,7 @@ export type SqliteDispatcherMethod = | 'unsafeLoadFromSync' | 'provideSyncJson' | 'unsafeResetDatabase' + | 'unsafeVacuum' | 'getLocal' | 'unsafeExecuteMultiple' diff --git a/node_modules/@nozbe/watermelondb/src/adapters/type.js b/node_modules/@nozbe/watermelondb/src/adapters/type.js index 8929541..bc3f95d 100644 --- a/node_modules/@nozbe/watermelondb/src/adapters/type.js +++ b/node_modules/@nozbe/watermelondb/src/adapters/type.js @@ -68,6 +68,8 @@ export interface DatabaseAdapter { // Destroys the whole database, its schema, indexes, everything. unsafeResetDatabase(callback: ResultCallback): void; + unsafeVacuum(callback: ResultCallback): void; + // Performs work on the underlying database - see concrete DatabaseAdapter implementation for more details unsafeExecute(work: UnsafeExecuteOperations, callback: ResultCallback): void;