Protect against SQLiteFullExceptions
Reviewed By: @kmagiera Differential Revision: D2512317 fb-gh-sync-id: 93fd65ebd88e42b5afc4e06c0612576101f15c97
This commit is contained in:
parent
b7b83e4f12
commit
ea8d0b6c1f
|
@ -23,6 +23,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||||
import com.facebook.react.bridge.ReactMethod;
|
import com.facebook.react.bridge.ReactMethod;
|
||||||
import com.facebook.react.bridge.ReadableArray;
|
import com.facebook.react.bridge.ReadableArray;
|
||||||
import com.facebook.react.bridge.WritableArray;
|
import com.facebook.react.bridge.WritableArray;
|
||||||
|
import com.facebook.react.bridge.WritableMap;
|
||||||
import com.facebook.react.common.ReactConstants;
|
import com.facebook.react.common.ReactConstants;
|
||||||
import com.facebook.react.common.SetBuilder;
|
import com.facebook.react.common.SetBuilder;
|
||||||
import com.facebook.react.modules.common.ModuleDataCleaner;
|
import com.facebook.react.modules.common.ModuleDataCleaner;
|
||||||
|
@ -137,8 +138,9 @@ public final class AsyncStorageModule
|
||||||
} while (cursor.moveToNext());
|
} while (cursor.moveToNext());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
FLog.w(ReactConstants.TAG, "Exception in database multiGet ", e);
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()), null);
|
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()), null);
|
||||||
|
return;
|
||||||
} finally {
|
} finally {
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
|
@ -179,19 +181,20 @@ public final class AsyncStorageModule
|
||||||
|
|
||||||
String sql = "INSERT OR REPLACE INTO " + TABLE_CATALYST + " VALUES (?, ?);";
|
String sql = "INSERT OR REPLACE INTO " + TABLE_CATALYST + " VALUES (?, ?);";
|
||||||
SQLiteStatement statement = mReactDatabaseSupplier.get().compileStatement(sql);
|
SQLiteStatement statement = mReactDatabaseSupplier.get().compileStatement(sql);
|
||||||
mReactDatabaseSupplier.get().beginTransaction();
|
WritableMap error = null;
|
||||||
try {
|
try {
|
||||||
|
mReactDatabaseSupplier.get().beginTransaction();
|
||||||
for (int idx=0; idx < keyValueArray.size(); idx++) {
|
for (int idx=0; idx < keyValueArray.size(); idx++) {
|
||||||
if (keyValueArray.getArray(idx).size() != 2) {
|
if (keyValueArray.getArray(idx).size() != 2) {
|
||||||
callback.invoke(AsyncStorageErrorUtil.getInvalidValueError(null));
|
error = AsyncStorageErrorUtil.getInvalidValueError(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (keyValueArray.getArray(idx).getString(0) == null) {
|
if (keyValueArray.getArray(idx).getString(0) == null) {
|
||||||
callback.invoke(AsyncStorageErrorUtil.getInvalidKeyError(null));
|
error = AsyncStorageErrorUtil.getInvalidKeyError(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (keyValueArray.getArray(idx).getString(1) == null) {
|
if (keyValueArray.getArray(idx).getString(1) == null) {
|
||||||
callback.invoke(AsyncStorageErrorUtil.getInvalidValueError(null));
|
error = AsyncStorageErrorUtil.getInvalidValueError(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,13 +205,24 @@ public final class AsyncStorageModule
|
||||||
}
|
}
|
||||||
mReactDatabaseSupplier.get().setTransactionSuccessful();
|
mReactDatabaseSupplier.get().setTransactionSuccessful();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
FLog.w(ReactConstants.TAG, "Exception in database multiSet ", e);
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()));
|
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
mReactDatabaseSupplier.get().endTransaction();
|
mReactDatabaseSupplier.get().endTransaction();
|
||||||
|
} catch (Exception e) {
|
||||||
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
|
if (error == null) {
|
||||||
|
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error != null) {
|
||||||
|
callback.invoke(error);
|
||||||
|
} else {
|
||||||
callback.invoke();
|
callback.invoke();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,8 +244,9 @@ public final class AsyncStorageModule
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mReactDatabaseSupplier.get().beginTransaction();
|
WritableMap error = null;
|
||||||
try {
|
try {
|
||||||
|
mReactDatabaseSupplier.get().beginTransaction();
|
||||||
for (int keyStart = 0; keyStart < keys.size(); keyStart += MAX_SQL_KEYS) {
|
for (int keyStart = 0; keyStart < keys.size(); keyStart += MAX_SQL_KEYS) {
|
||||||
int keyCount = Math.min(keys.size() - keyStart, MAX_SQL_KEYS);
|
int keyCount = Math.min(keys.size() - keyStart, MAX_SQL_KEYS);
|
||||||
mReactDatabaseSupplier.get().delete(
|
mReactDatabaseSupplier.get().delete(
|
||||||
|
@ -241,13 +256,24 @@ public final class AsyncStorageModule
|
||||||
}
|
}
|
||||||
mReactDatabaseSupplier.get().setTransactionSuccessful();
|
mReactDatabaseSupplier.get().setTransactionSuccessful();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
FLog.w(ReactConstants.TAG, "Exception in database multiRemove ", e);
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()));
|
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
mReactDatabaseSupplier.get().endTransaction();
|
mReactDatabaseSupplier.get().endTransaction();
|
||||||
|
} catch (Exception e) {
|
||||||
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
|
if (error == null) {
|
||||||
|
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error != null) {
|
||||||
|
callback.invoke(error);
|
||||||
|
} else {
|
||||||
callback.invoke();
|
callback.invoke();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,21 +290,22 @@ public final class AsyncStorageModule
|
||||||
callback.invoke(AsyncStorageErrorUtil.getDBError(null));
|
callback.invoke(AsyncStorageErrorUtil.getDBError(null));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mReactDatabaseSupplier.get().beginTransaction();
|
WritableMap error = null;
|
||||||
try {
|
try {
|
||||||
|
mReactDatabaseSupplier.get().beginTransaction();
|
||||||
for (int idx = 0; idx < keyValueArray.size(); idx++) {
|
for (int idx = 0; idx < keyValueArray.size(); idx++) {
|
||||||
if (keyValueArray.getArray(idx).size() != 2) {
|
if (keyValueArray.getArray(idx).size() != 2) {
|
||||||
callback.invoke(AsyncStorageErrorUtil.getInvalidValueError(null));
|
error = AsyncStorageErrorUtil.getInvalidValueError(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyValueArray.getArray(idx).getString(0) == null) {
|
if (keyValueArray.getArray(idx).getString(0) == null) {
|
||||||
callback.invoke(AsyncStorageErrorUtil.getInvalidKeyError(null));
|
error = AsyncStorageErrorUtil.getInvalidKeyError(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyValueArray.getArray(idx).getString(1) == null) {
|
if (keyValueArray.getArray(idx).getString(1) == null) {
|
||||||
callback.invoke(AsyncStorageErrorUtil.getInvalidValueError(null));
|
error = AsyncStorageErrorUtil.getInvalidValueError(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,19 +313,30 @@ public final class AsyncStorageModule
|
||||||
mReactDatabaseSupplier.get(),
|
mReactDatabaseSupplier.get(),
|
||||||
keyValueArray.getArray(idx).getString(0),
|
keyValueArray.getArray(idx).getString(0),
|
||||||
keyValueArray.getArray(idx).getString(1))) {
|
keyValueArray.getArray(idx).getString(1))) {
|
||||||
callback.invoke(AsyncStorageErrorUtil.getDBError(null));
|
error = AsyncStorageErrorUtil.getDBError(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mReactDatabaseSupplier.get().setTransactionSuccessful();
|
mReactDatabaseSupplier.get().setTransactionSuccessful();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()));
|
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
mReactDatabaseSupplier.get().endTransaction();
|
mReactDatabaseSupplier.get().endTransaction();
|
||||||
|
} catch (Exception e) {
|
||||||
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
|
if (error == null) {
|
||||||
|
error = AsyncStorageErrorUtil.getError(null, e.getMessage());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error != null) {
|
||||||
|
callback.invoke(error);
|
||||||
|
} else {
|
||||||
callback.invoke();
|
callback.invoke();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,11 +354,11 @@ public final class AsyncStorageModule
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
mReactDatabaseSupplier.get().delete(TABLE_CATALYST, null, null);
|
mReactDatabaseSupplier.get().delete(TABLE_CATALYST, null, null);
|
||||||
|
callback.invoke();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
FLog.w(ReactConstants.TAG, "Exception in database clear ", e);
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()));
|
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()));
|
||||||
}
|
}
|
||||||
callback.invoke();
|
|
||||||
}
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
}
|
}
|
||||||
|
@ -348,8 +386,9 @@ public final class AsyncStorageModule
|
||||||
} while (cursor.moveToNext());
|
} while (cursor.moveToNext());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
FLog.w(ReactConstants.TAG, "Exception in database getAllKeys ", e);
|
FLog.w(ReactConstants.TAG, e.getMessage(), e);
|
||||||
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()), null);
|
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()), null);
|
||||||
|
return;
|
||||||
} finally {
|
} finally {
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,10 @@ public class ReactDatabaseSupplier extends SQLiteOpenHelper {
|
||||||
|
|
||||||
// VisibleForTesting
|
// VisibleForTesting
|
||||||
public static final String DATABASE_NAME = "RKStorage";
|
public static final String DATABASE_NAME = "RKStorage";
|
||||||
static final int DATABASE_VERSION = 1;
|
|
||||||
|
private static final int DATABASE_VERSION = 1;
|
||||||
private static final int SLEEP_TIME_MS = 30;
|
private static final int SLEEP_TIME_MS = 30;
|
||||||
|
private static final long DEFAULT_MAX_DB_SIZE = 6L * 1024L * 1024L; // 6 MB in bytes
|
||||||
|
|
||||||
static final String TABLE_CATALYST = "catalystLocalStorage";
|
static final String TABLE_CATALYST = "catalystLocalStorage";
|
||||||
static final String KEY_COLUMN = "key";
|
static final String KEY_COLUMN = "key";
|
||||||
|
@ -85,6 +87,10 @@ public class ReactDatabaseSupplier extends SQLiteOpenHelper {
|
||||||
if (mDb == null) {
|
if (mDb == null) {
|
||||||
throw lastSQLiteException;
|
throw lastSQLiteException;
|
||||||
}
|
}
|
||||||
|
// This is a sane limit to protect the user from the app storing too much data in the database.
|
||||||
|
// This also protects the database from filling up the disk cache and becoming malformed
|
||||||
|
// (endTransaction() calls will throw an exception, not rollback, and leave the db malformed).
|
||||||
|
mDb.setMaximumSize(DEFAULT_MAX_DB_SIZE);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue