diff --git a/stetho/src/main/java/com/facebook/stetho/Stetho.java b/stetho/src/main/java/com/facebook/stetho/Stetho.java index 93b91e4d..e9873aed 100644 --- a/stetho/src/main/java/com/facebook/stetho/Stetho.java +++ b/stetho/src/main/java/com/facebook/stetho/Stetho.java @@ -1,3 +1,5 @@ +// Copyright 2015-present Facebook. All Rights Reserved. + package com.facebook.stetho; import com.facebook.stetho.inspector.database.DefaultDatabaseFilesProvider; @@ -26,6 +28,7 @@ import com.facebook.stetho.inspector.protocol.module.DOM; import com.facebook.stetho.inspector.protocol.module.DOMStorage; import com.facebook.stetho.inspector.protocol.module.Database; +import com.facebook.stetho.inspector.protocol.module.DatabaseConstants; import com.facebook.stetho.inspector.protocol.module.Debugger; import com.facebook.stetho.inspector.protocol.module.HeapProfiler; import com.facebook.stetho.inspector.protocol.module.Inspector; @@ -115,8 +118,8 @@ public Iterable get() { modules.add(new Profiler()); modules.add(new Runtime()); modules.add(new Worker()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - modules.add(new Database(context, new DefaultDatabaseFilesProvider(context))); + if (Build.VERSION.SDK_INT >= DatabaseConstants.MIN_API_LEVEL) { + modules.add(new Database(context)); } return modules; } diff --git a/stetho/src/main/java/com/facebook/stetho/inspector/database/DatabasePeerManager.java b/stetho/src/main/java/com/facebook/stetho/inspector/database/DatabasePeerManager.java index 3075c64b..56afb10c 100644 --- a/stetho/src/main/java/com/facebook/stetho/inspector/database/DatabasePeerManager.java +++ b/stetho/src/main/java/com/facebook/stetho/inspector/database/DatabasePeerManager.java @@ -11,16 +11,19 @@ import java.util.ArrayList; import java.util.List; +import android.annotation.TargetApi; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; +import android.database.sqlite.SQLiteStatement; import com.facebook.stetho.common.Util; import com.facebook.stetho.inspector.helper.ChromePeerManager; import com.facebook.stetho.inspector.helper.PeerRegistrationListener; import com.facebook.stetho.inspector.jsonrpc.JsonRpcPeer; import com.facebook.stetho.inspector.protocol.module.Database; +import com.facebook.stetho.inspector.protocol.module.DatabaseConstants; @ThreadSafe public class DatabasePeerManager extends ChromePeerManager { @@ -129,17 +132,67 @@ public T executeSQL(String databaseName, String query, ExecuteResultHandler< Util.throwIfNull(handler); SQLiteDatabase database = openDatabase(databaseName); try { - Cursor cursor = database.rawQuery(query, null); - try { - return handler.handleResult(cursor); - } finally { - cursor.close(); + String firstWord = getFirstWord(query); + + if (firstWord.equalsIgnoreCase("UPDATE") || firstWord.equalsIgnoreCase("DELETE")) { + return executeUpdateDelete(database, query, handler); + } else if (firstWord.equalsIgnoreCase("INSERT")) { + return executeInsert(database, query, handler); + } else if (firstWord.equalsIgnoreCase("SELECT")) { + return executeSelect(database, query, handler); + } else { + return executeRawQuery(database, query, handler); } + } finally { database.close(); } } + private static String getFirstWord(String s) { + s.trim(); + int firstSpace = s.indexOf(' '); + return firstSpace >= 0 ? s.substring(0, firstSpace) : s; + } + + @TargetApi(DatabaseConstants.MIN_API_LEVEL) + private T executeUpdateDelete( + SQLiteDatabase database, + String query, + ExecuteResultHandler handler) { + SQLiteStatement statement = database.compileStatement(query); + int count = statement.executeUpdateDelete(); + return handler.handleUpdateDelete(count); + } + + private T executeInsert( + SQLiteDatabase database, + String query, + ExecuteResultHandler handler) { + SQLiteStatement statement = database.compileStatement(query); + long count = statement.executeInsert(); + return handler.handleInsert(count); + } + + private T executeSelect( + SQLiteDatabase database, + String query, + ExecuteResultHandler handler) { + Cursor cursor = database.rawQuery(query, null); + try { + return handler.handleSelect(cursor); + } finally { + cursor.close(); + } + } + + private T executeRawQuery( + SQLiteDatabase database, + String query, + ExecuteResultHandler handler) { + database.execSQL(query); + return handler.handleRawQuery(); + } private SQLiteDatabase openDatabase(String databaseName) throws SQLiteException { Util.throwIfNull(databaseName); File databaseFile = mContext.getDatabasePath(databaseName); @@ -151,7 +204,13 @@ private SQLiteDatabase openDatabase(String databaseName) throws SQLiteException } public interface ExecuteResultHandler { - public T handleResult(Cursor result) throws SQLiteException; + public T handleRawQuery() throws SQLiteException; + + public T handleSelect(Cursor result) throws SQLiteException; + + public T handleInsert(long insertedId) throws SQLiteException; + + public T handleUpdateDelete(int count) throws SQLiteException; } private final PeerRegistrationListener mPeerRegistrationListener = diff --git a/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/Database.java b/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/Database.java index 00e26ae4..a8f060e3 100644 --- a/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/Database.java +++ b/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/Database.java @@ -5,6 +5,7 @@ import com.facebook.stetho.inspector.database.DatabaseFilesProvider; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import android.annotation.TargetApi; @@ -85,12 +86,38 @@ public JsonRpcResult executeSQL(JsonRpcPeer peer, JSONObject params) { return mDatabasePeerManager.executeSQL(request.databaseId, request.query, new DatabasePeerManager.ExecuteResultHandler() { @Override - public ExecuteSQLResponse handleResult(Cursor result) throws SQLiteException { + public ExecuteSQLResponse handleRawQuery() throws SQLiteException { + ExecuteSQLResponse response = new ExecuteSQLResponse(); + // This is done because the inspector UI likes to delete rows if you give them no + // name/value list + response.columnNames = Arrays.asList("success"); + response.values = Arrays.asList((Object) "true"); + return response; + } + + @Override + public ExecuteSQLResponse handleSelect(Cursor result) throws SQLiteException { ExecuteSQLResponse response = new ExecuteSQLResponse(); response.columnNames = Arrays.asList(result.getColumnNames()); response.values = flattenRows(result, MAX_EXECUTE_RESULTS); return response; } + + @Override + public ExecuteSQLResponse handleInsert(long insertedId) throws SQLiteException { + ExecuteSQLResponse response = new ExecuteSQLResponse(); + response.columnNames = Arrays.asList("ID of last inserted row"); + response.values = Arrays.asList((Object) insertedId); + return response; + } + + @Override + public ExecuteSQLResponse handleUpdateDelete(int count) throws SQLiteException { + ExecuteSQLResponse response = new ExecuteSQLResponse(); + response.columnNames = Arrays.asList("Modified rows"); + response.values = Arrays.asList((Object) count); + return response; + } }); } catch (SQLiteException e) { Error error = new Error(); diff --git a/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/DatabaseConstants.java b/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/DatabaseConstants.java new file mode 100644 index 00000000..b65bc0d1 --- /dev/null +++ b/stetho/src/main/java/com/facebook/stetho/inspector/protocol/module/DatabaseConstants.java @@ -0,0 +1,13 @@ +// Copyright 2015-present Facebook. All Rights Reserved. + +package com.facebook.stetho.inspector.protocol.module; + +import android.os.Build; + +public interface DatabaseConstants { + + /** + * Minimum API version required to use the {@link Database}. + */ + public static final int MIN_API_LEVEL = Build.VERSION_CODES.HONEYCOMB; +}