Fix SQL errors caused by huge operations

Differential Revision: D2475717

committer: Service User <svcscm@fb.com>
This commit is contained in:
Andrei Coman 2015-09-24 05:18:47 -07:00 committed by facebook-github-bot-9
parent eee11eaeed
commit c1e3f9100e
2 changed files with 57 additions and 43 deletions

View File

@ -49,10 +49,10 @@ import static com.facebook.react.modules.storage.ReactDatabaseSupplier.VALUE_COL
* {a, b, c} * {a, b, c}
* to be used in the SQL select statement: WHERE key in (?, ?, ?) * to be used in the SQL select statement: WHERE key in (?, ?, ?)
*/ */
/* package */ static String[] buildKeySelectionArgs(ReadableArray keys) { /* package */ static String[] buildKeySelectionArgs(ReadableArray keys, int start, int count) {
String[] selectionArgs = new String[keys.size()]; String[] selectionArgs = new String[count];
for (int keyIndex = 0; keyIndex < keys.size(); keyIndex++) { for (int keyIndex = 0; keyIndex < count; keyIndex++) {
selectionArgs[keyIndex] = keys.getString(keyIndex); selectionArgs[keyIndex] = keys.getString(start + keyIndex);
} }
return selectionArgs; return selectionArgs;
} }

View File

@ -34,6 +34,10 @@ import static com.facebook.react.modules.storage.ReactDatabaseSupplier.VALUE_COL
public final class AsyncStorageModule public final class AsyncStorageModule
extends ReactContextBaseJavaModule implements ModuleDataCleaner.Cleanable { extends ReactContextBaseJavaModule implements ModuleDataCleaner.Cleanable {
// SQL variable number limit, defined by SQLITE_LIMIT_VARIABLE_NUMBER:
// https://raw.githubusercontent.com/android/platform_external_sqlite/master/dist/sqlite3.c
private static final int MAX_SQL_KEYS = 999;
private ReactDatabaseSupplier mReactDatabaseSupplier; private ReactDatabaseSupplier mReactDatabaseSupplier;
private boolean mShuttingDown = false; private boolean mShuttingDown = false;
@ -104,47 +108,50 @@ public final class AsyncStorageModule
String[] columns = {KEY_COLUMN, VALUE_COLUMN}; String[] columns = {KEY_COLUMN, VALUE_COLUMN};
HashSet<String> keysRemaining = SetBuilder.newHashSet(); HashSet<String> keysRemaining = SetBuilder.newHashSet();
WritableArray data = Arguments.createArray(); WritableArray data = Arguments.createArray();
Cursor cursor = mReactDatabaseSupplier.get().query( for (int keyStart = 0; keyStart < keys.size(); keyStart += MAX_SQL_KEYS) {
TABLE_CATALYST, int keyCount = Math.min(keys.size() - keyStart, MAX_SQL_KEYS);
columns, Cursor cursor = mReactDatabaseSupplier.get().query(
AsyncLocalStorageUtil.buildKeySelection(keys.size()), TABLE_CATALYST,
AsyncLocalStorageUtil.buildKeySelectionArgs(keys), columns,
null, AsyncLocalStorageUtil.buildKeySelection(keyCount),
null, AsyncLocalStorageUtil.buildKeySelectionArgs(keys, keyStart, keyCount),
null); null,
null,
try { null);
if (cursor.getCount() != keys.size()) { keysRemaining.clear();
// some keys have not been found - insert them with null into the final array try {
for (int keyIndex = 0; keyIndex < keys.size(); keyIndex++) { if (cursor.getCount() != keys.size()) {
keysRemaining.add(keys.getString(keyIndex)); // some keys have not been found - insert them with null into the final array
for (int keyIndex = keyStart; keyIndex < keyStart + keyCount; keyIndex++) {
keysRemaining.add(keys.getString(keyIndex));
}
} }
if (cursor.moveToFirst()) {
do {
WritableArray row = Arguments.createArray();
row.pushString(cursor.getString(0));
row.pushString(cursor.getString(1));
data.pushArray(row);
keysRemaining.remove(cursor.getString(0));
} while (cursor.moveToNext());
}
} catch (Exception e) {
FLog.w(ReactConstants.TAG, "Exception in database multiGet ", e);
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()), null);
} finally {
cursor.close();
} }
if (cursor.moveToFirst()) { for (String key : keysRemaining) {
do { WritableArray row = Arguments.createArray();
WritableArray row = Arguments.createArray(); row.pushString(key);
row.pushString(cursor.getString(0)); row.pushNull();
row.pushString(cursor.getString(1)); data.pushArray(row);
data.pushArray(row);
keysRemaining.remove(cursor.getString(0));
} while (cursor.moveToNext());
} }
} catch (Exception e) { keysRemaining.clear();
FLog.w(ReactConstants.TAG, "Exception in database multiGet ", e);
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()), null);
} finally {
cursor.close();
} }
for (String key : keysRemaining) {
WritableArray row = Arguments.createArray();
row.pushString(key);
row.pushNull();
data.pushArray(row);
}
keysRemaining.clear();
callback.invoke(null, data); callback.invoke(null, data);
} }
}.execute(); }.execute();
@ -223,14 +230,21 @@ public final class AsyncStorageModule
return; return;
} }
mReactDatabaseSupplier.get().beginTransaction();
try { try {
mReactDatabaseSupplier.get().delete( for (int keyStart = 0; keyStart < keys.size(); keyStart += MAX_SQL_KEYS) {
TABLE_CATALYST, int keyCount = Math.min(keys.size() - keyStart, MAX_SQL_KEYS);
AsyncLocalStorageUtil.buildKeySelection(keys.size()), mReactDatabaseSupplier.get().delete(
AsyncLocalStorageUtil.buildKeySelectionArgs(keys)); TABLE_CATALYST,
AsyncLocalStorageUtil.buildKeySelection(keyCount),
AsyncLocalStorageUtil.buildKeySelectionArgs(keys, keyStart, keyCount));
}
mReactDatabaseSupplier.get().setTransactionSuccessful();
} catch (Exception e) { } catch (Exception e) {
FLog.w(ReactConstants.TAG, "Exception in database multiRemove ", e); FLog.w(ReactConstants.TAG, "Exception in database multiRemove ", e);
callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage())); callback.invoke(AsyncStorageErrorUtil.getError(null, e.getMessage()));
} finally {
mReactDatabaseSupplier.get().endTransaction();
} }
callback.invoke(); callback.invoke();
} }