Add getTableInfo API to process incoming TableInfo queries

Summary: Getting the "definition" of the database was originally on the GetTableStructure endpoint. This diff moves it to a new GetTableInfo endpoint

Reviewed By: jknoxville

Differential Revision: D15902619

fbshipit-source-id: ac136d24ee577711366636801b5d74d83fbc523f
This commit is contained in:
Arnaud Frugier
2019-06-20 08:25:11 -07:00
committed by Facebook Github Bot
parent a2663ea970
commit f40ac0617c
5 changed files with 142 additions and 27 deletions

View File

@@ -45,6 +45,9 @@ public abstract class DatabaseDriver<DESCRIPTOR extends DatabaseDescriptor> {
public abstract DatabaseGetTableStructureResponse getTableStructure( public abstract DatabaseGetTableStructureResponse getTableStructure(
DESCRIPTOR databaseDescriptor, String table); DESCRIPTOR databaseDescriptor, String table);
public abstract DatabaseGetTableInfoResponse getTableInfo(
DESCRIPTOR databaseDescriptor, String table);
public abstract DatabaseExecuteSqlResponse executeSQL( public abstract DatabaseExecuteSqlResponse executeSQL(
DESCRIPTOR databaseDescriptor, String query); DESCRIPTOR databaseDescriptor, String query);
@@ -76,18 +79,24 @@ public abstract class DatabaseDriver<DESCRIPTOR extends DatabaseDescriptor> {
public final List<List<Object>> structureValues; public final List<List<Object>> structureValues;
public final List<String> indexesColumns; public final List<String> indexesColumns;
public final List<List<Object>> indexesValues; public final List<List<Object>> indexesValues;
public final String definition;
public DatabaseGetTableStructureResponse( public DatabaseGetTableStructureResponse(
final List<String> structureColumns, final List<String> structureColumns,
final List<List<Object>> structureValues, final List<List<Object>> structureValues,
final List<String> indexesColumns, final List<String> indexesColumns,
final List<List<Object>> indexesValues, final List<List<Object>> indexesValues) {
String definition) {
this.structureColumns = structureColumns; this.structureColumns = structureColumns;
this.structureValues = structureValues; this.structureValues = structureValues;
this.indexesColumns = indexesColumns; this.indexesColumns = indexesColumns;
this.indexesValues = indexesValues; this.indexesValues = indexesValues;
}
}
public static class DatabaseGetTableInfoResponse {
public final String definition;
public DatabaseGetTableInfoResponse(String definition) {
this.definition = definition; this.definition = definition;
} }
} }

View File

@@ -14,6 +14,7 @@ import com.facebook.flipper.core.FlipperReceiver;
import com.facebook.flipper.core.FlipperResponder; import com.facebook.flipper.core.FlipperResponder;
import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseExecuteSqlResponse; import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseExecuteSqlResponse;
import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseGetTableDataResponse; import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseGetTableDataResponse;
import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseGetTableInfoResponse;
import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseGetTableStructureResponse; import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseGetTableStructureResponse;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
@@ -26,6 +27,7 @@ public class DatabasesManager {
private static final String DATABASE_LIST_COMMAND = "databaseList"; private static final String DATABASE_LIST_COMMAND = "databaseList";
private static final String GET_TABLE_DATA_COMMAND = "getTableData"; private static final String GET_TABLE_DATA_COMMAND = "getTableData";
private static final String GET_TABLE_STRUCTURE_COMMAND = "getTableStructure"; private static final String GET_TABLE_STRUCTURE_COMMAND = "getTableStructure";
private static final String GET_TABLE_INFO_COMMAND = "getTableInfo";
private static final String EXECUTE_COMMAND = "execute"; private static final String EXECUTE_COMMAND = "execute";
private final List<DatabaseDriver> mDatabaseDriverList; private final List<DatabaseDriver> mDatabaseDriverList;
@@ -152,7 +154,7 @@ public class DatabasesManager {
databaseDescriptorHolder.databaseDescriptor, databaseDescriptorHolder.databaseDescriptor,
getTableStructureRequest.table); getTableStructureRequest.table);
responder.success( responder.success(
ObjectMapper.databaseGetTableStructureReponseToFlipperObject( ObjectMapper.databaseGetTableStructureResponseToFlipperObject(
databaseGetTableStructureResponse)); databaseGetTableStructureResponse));
} catch (Exception e) { } catch (Exception e) {
responder.error( responder.error(
@@ -163,6 +165,43 @@ public class DatabasesManager {
} }
} }
}); });
connection.receive(
GET_TABLE_INFO_COMMAND,
new FlipperReceiver() {
@Override
public void onReceive(FlipperObject params, FlipperResponder responder) {
GetTableInfoRequest getTableInfoRequest =
ObjectMapper.flipperObjectToGetTableInfoRequest(params);
if (getTableInfoRequest == null) {
responder.error(
ObjectMapper.toErrorFlipperObject(
DatabasesErrorCodes.ERROR_INVALID_REQUEST,
DatabasesErrorCodes.ERROR_INVALID_REQUEST_MESSAGE));
} else {
DatabaseDescriptorHolder databaseDescriptorHolder =
mDatabaseDescriptorHolderSparseArray.get(getTableInfoRequest.databaseId);
if (databaseDescriptorHolder == null) {
responder.error(
ObjectMapper.toErrorFlipperObject(
DatabasesErrorCodes.ERROR_DATABASE_INVALID,
DatabasesErrorCodes.ERROR_DATABASE_INVALID_MESSAGE));
} else {
try {
DatabaseGetTableInfoResponse databaseGetTableInfoResponse =
databaseDescriptorHolder.databaseDriver.getTableInfo(
databaseDescriptorHolder.databaseDescriptor, getTableInfoRequest.table);
responder.success(
ObjectMapper.databaseGetTableInfoResponseToFlipperObject(
databaseGetTableInfoResponse));
} catch (Exception e) {
responder.error(
ObjectMapper.toErrorFlipperObject(
DatabasesErrorCodes.ERROR_SQL_EXECUTION_EXCEPTION, e.getMessage()));
}
}
}
}
});
connection.receive( connection.receive(
EXECUTE_COMMAND, EXECUTE_COMMAND,
new FlipperReceiver() { new FlipperReceiver() {
@@ -257,4 +296,15 @@ public class DatabasesManager {
this.table = table; this.table = table;
} }
} }
static class GetTableInfoRequest {
public final int databaseId;
public final String table;
GetTableInfoRequest(int databaseId, String table) {
this.databaseId = databaseId;
this.table = table;
}
}
} }

View File

@@ -12,10 +12,12 @@ import com.facebook.flipper.core.FlipperArray.Builder;
import com.facebook.flipper.core.FlipperObject; import com.facebook.flipper.core.FlipperObject;
import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseExecuteSqlResponse; import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseExecuteSqlResponse;
import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseGetTableDataResponse; import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseGetTableDataResponse;
import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseGetTableInfoResponse;
import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseGetTableStructureResponse; import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseGetTableStructureResponse;
import com.facebook.flipper.plugins.databases.DatabasesManager.DatabaseDescriptorHolder; import com.facebook.flipper.plugins.databases.DatabasesManager.DatabaseDescriptorHolder;
import com.facebook.flipper.plugins.databases.DatabasesManager.ExecuteSqlRequest; import com.facebook.flipper.plugins.databases.DatabasesManager.ExecuteSqlRequest;
import com.facebook.flipper.plugins.databases.DatabasesManager.GetTableDataRequest; import com.facebook.flipper.plugins.databases.DatabasesManager.GetTableDataRequest;
import com.facebook.flipper.plugins.databases.DatabasesManager.GetTableInfoRequest;
import com.facebook.flipper.plugins.databases.DatabasesManager.GetTableStructureRequest; import com.facebook.flipper.plugins.databases.DatabasesManager.GetTableStructureRequest;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.Collection; import java.util.Collection;
@@ -76,6 +78,15 @@ public class ObjectMapper {
return new GetTableStructureRequest(databaseId, table); return new GetTableStructureRequest(databaseId, table);
} }
public static GetTableInfoRequest flipperObjectToGetTableInfoRequest(FlipperObject params) {
int databaseId = params.getInt("databaseId");
String table = params.getString("table");
if (databaseId <= 0 || TextUtils.isEmpty(table)) {
return null;
}
return new GetTableInfoRequest(databaseId, table);
}
public static ExecuteSqlRequest flipperObjectToExecuteSqlRequest(FlipperObject params) { public static ExecuteSqlRequest flipperObjectToExecuteSqlRequest(FlipperObject params) {
int databaseId = params.getInt("databaseId"); int databaseId = params.getInt("databaseId");
String value = params.getString("value"); String value = params.getString("value");
@@ -111,7 +122,7 @@ public class ObjectMapper {
.build(); .build();
} }
public static FlipperObject databaseGetTableStructureReponseToFlipperObject( public static FlipperObject databaseGetTableStructureResponseToFlipperObject(
DatabaseGetTableStructureResponse databaseGetTableStructureResponse) { DatabaseGetTableStructureResponse databaseGetTableStructureResponse) {
FlipperArray.Builder structureColumnBuilder = new FlipperArray.Builder(); FlipperArray.Builder structureColumnBuilder = new FlipperArray.Builder();
@@ -147,7 +158,14 @@ public class ObjectMapper {
.put("structureValues", structureValuesBuilder.build()) .put("structureValues", structureValuesBuilder.build())
.put("indexesColumns", indexesColumnBuilder.build()) .put("indexesColumns", indexesColumnBuilder.build())
.put("indexesValues", indexesValuesBuilder.build()) .put("indexesValues", indexesValuesBuilder.build())
.put("definition", databaseGetTableStructureResponse.definition) .build();
}
public static FlipperObject databaseGetTableInfoResponseToFlipperObject(
DatabaseGetTableInfoResponse databaseGetTableInfoResponse) {
return new FlipperObject.Builder()
.put("definition", databaseGetTableInfoResponse.definition)
.build(); .build();
} }

View File

@@ -171,9 +171,7 @@ public class SqliteDatabaseDriver extends DatabaseDriver<SqliteDatabaseDescripto
Cursor structureCursor = database.rawQuery("PRAGMA table_info(" + table + ")", null); Cursor structureCursor = database.rawQuery("PRAGMA table_info(" + table + ")", null);
Cursor foreignKeysCursor = database.rawQuery("PRAGMA foreign_key_list(" + table + ")", null); Cursor foreignKeysCursor = database.rawQuery("PRAGMA foreign_key_list(" + table + ")", null);
Cursor indexesCursor = database.rawQuery("PRAGMA index_list(" + table + ")", null); Cursor indexesCursor = database.rawQuery("PRAGMA index_list(" + table + ")", null);
Cursor definitionCursor =
database.rawQuery(
"SELECT sql FROM " + SCHEMA_TABLE + " WHERE name=?", new String[] {table});
try { try {
// Structure & foreign keys // Structure & foreign keys
@@ -234,13 +232,9 @@ public class SqliteDatabaseDriver extends DatabaseDriver<SqliteDatabaseDescripto
} }
} }
// Definition
definitionCursor.moveToFirst();
String definition = definitionCursor.getString(definitionCursor.getColumnIndex("sql"));
return new DatabaseGetTableStructureResponse( return new DatabaseGetTableStructureResponse(
structureColumns, structureValues, indexesColumns, indexesValues, definition); structureColumns, structureValues, indexesColumns, indexesValues);
} finally { } finally {
structureCursor.close(); structureCursor.close();
foreignKeysCursor.close(); foreignKeysCursor.close();
@@ -251,6 +245,31 @@ public class SqliteDatabaseDriver extends DatabaseDriver<SqliteDatabaseDescripto
} }
} }
@Override
public DatabaseGetTableInfoResponse getTableInfo(
SqliteDatabaseDescriptor databaseDescriptor, String table) {
SQLiteDatabase database =
sqliteDatabaseConnectionProvider.openDatabase(databaseDescriptor.file);
try {
Cursor definitionCursor =
database.rawQuery(
"SELECT sql FROM " + SCHEMA_TABLE + " WHERE name=?", new String[] {table});
try {
// Definition
definitionCursor.moveToFirst();
String definition = definitionCursor.getString(definitionCursor.getColumnIndex("sql"));
return new DatabaseGetTableInfoResponse(definition);
} finally {
definitionCursor.close();
}
} finally {
database.close();
}
}
private static List<File> tidyDatabaseList(List<File> databaseFiles) { private static List<File> tidyDatabaseList(List<File> databaseFiles) {
Set<File> originalAsSet = new HashSet<>(databaseFiles); Set<File> originalAsSet = new HashSet<>(databaseFiles);
List<File> tidiedList = new ArrayList<>(); List<File> tidiedList = new ArrayList<>();

View File

@@ -12,7 +12,6 @@ import static org.junit.Assert.assertThat;
import android.content.Context; import android.content.Context;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import androidx.test.core.app.ApplicationProvider;
import com.facebook.flipper.core.FlipperArray; import com.facebook.flipper.core.FlipperArray;
import com.facebook.flipper.core.FlipperObject; import com.facebook.flipper.core.FlipperObject;
import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseExecuteSqlResponse; import com.facebook.flipper.plugins.databases.DatabaseDriver.DatabaseExecuteSqlResponse;
@@ -29,6 +28,7 @@ import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Ignore @Ignore
@@ -45,28 +45,26 @@ public class DatabasesFlipperPluginTest {
connectionMock = new FlipperConnectionMock(); connectionMock = new FlipperConnectionMock();
responderMock = new FlipperResponderMock(); responderMock = new FlipperResponderMock();
databaseHelper1 = databaseHelper1 = new DatabaseHelper(RuntimeEnvironment.application, "database1.db");
new DatabaseHelper(ApplicationProvider.getApplicationContext(), "database1.db");
databaseHelper1 databaseHelper1
.getWritableDatabase() .getWritableDatabase()
.execSQL("INSERT INTO first_table (column1, column2) VALUES('a','b')"); .execSQL("INSERT INTO first_table (column1, column2) VALUES('a','b')");
databaseHelper2 = databaseHelper2 = new DatabaseHelper(RuntimeEnvironment.application, "database2.db");
new DatabaseHelper(ApplicationProvider.getApplicationContext(), "database2.db");
databaseHelper2 databaseHelper2
.getWritableDatabase() .getWritableDatabase()
.execSQL("INSERT INTO first_table (column1, column2) VALUES('a','b')"); .execSQL("INSERT INTO first_table (column1, column2) VALUES('a','b')");
plugin = plugin =
new DatabasesFlipperPlugin( new DatabasesFlipperPlugin(
new SqliteDatabaseDriver( new SqliteDatabaseDriver(
ApplicationProvider.getApplicationContext(), RuntimeEnvironment.application,
new SqliteDatabaseProvider() { new SqliteDatabaseProvider() {
@Override @Override
public List<File> getDatabaseFiles() { public List<File> getDatabaseFiles() {
return Arrays.asList( return Arrays.asList(
ApplicationProvider.getApplicationContext() RuntimeEnvironment.application.getDatabasePath(
.getDatabasePath(databaseHelper1.getDatabaseName()), databaseHelper1.getDatabaseName()),
ApplicationProvider.getApplicationContext() RuntimeEnvironment.application.getDatabasePath(
.getDatabasePath(databaseHelper2.getDatabaseName())); databaseHelper2.getDatabaseName()));
} }
})); }));
@@ -76,8 +74,8 @@ public class DatabasesFlipperPluginTest {
@After @After
public void tearDown() { public void tearDown() {
databaseHelper1.close(); databaseHelper1.close();
ApplicationProvider.getApplicationContext().deleteDatabase(databaseHelper1.getDatabaseName()); RuntimeEnvironment.application.deleteDatabase(databaseHelper1.getDatabaseName());
ApplicationProvider.getApplicationContext().deleteDatabase(databaseHelper2.getDatabaseName()); RuntimeEnvironment.application.deleteDatabase(databaseHelper2.getDatabaseName());
} }
@Test @Test
@@ -361,6 +359,27 @@ public class DatabasesFlipperPluginTest {
.put("value", "column1,column2")) .put("value", "column1,column2"))
.build()) .build())
.build()) .build())
.build()));
}
@Test
public void testCommandGetTableInfo() throws Exception {
// Arrange
connectionMock.receivers.get("databaseList").onReceive(null, responderMock); // Load data
// Act
connectionMock
.receivers
.get("getTableInfo")
.onReceive(
new FlipperObject.Builder().put("databaseId", 1).put("table", "first_table").build(),
responderMock);
// Assert
assertThat(
responderMock.successes,
hasItem(
new FlipperObject.Builder()
.put( .put(
"definition", "definition",
"CREATE TABLE first_table (_id INTEGER PRIMARY KEY AUTOINCREMENT,column1 TEXT,column2 TEXT)") "CREATE TABLE first_table (_id INTEGER PRIMARY KEY AUTOINCREMENT,column1 TEXT,column2 TEXT)")