Follow-up CL resolving b/174478034. Adding functionality for handling bind arguments provided for queries.

Test: Tests have been added QueryLoggerTest.kt

Bug: 174478034
Change-Id: Ida02dcdaff6ef22011c3f67adeb9c949fb37fff7
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
index 4db8a26..a23ce6b 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/QueryInterceptorTest.kt
@@ -26,6 +26,7 @@
 import androidx.room.Room
 import androidx.room.RoomDatabase
 import androidx.room.Update
+import androidx.sqlite.db.SimpleSQLiteQuery
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -104,7 +105,7 @@
         assertQueryLogged(
             "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
                 "VALUES (?,?)",
-            emptyList()
+            listOf("Insert", "Inserted a placeholder query")
         )
         assertTransactionQueries()
     }
@@ -112,8 +113,10 @@
     @Test
     fun testDelete() {
         mDatabase.queryInterceptorDao().delete("Insert")
-
-        assertQueryLogged("DELETE FROM queryInterceptorTestDatabase WHERE id=?", emptyList())
+        assertQueryLogged(
+            "DELETE FROM queryInterceptorTestDatabase WHERE id=?",
+            listOf("Insert")
+        )
         assertTransactionQueries()
     }
 
@@ -130,7 +133,7 @@
             "UPDATE OR ABORT `queryInterceptorTestDatabase` SET `id` " +
                 "= ?,`description` = ? " +
                 "WHERE `id` = ?",
-            emptyList()
+            listOf("Insert", "Updated the placeholder query", "Insert")
         )
         assertTransactionQueries()
     }
@@ -147,17 +150,47 @@
         assertQueryLogged("DELETE FROM queryInterceptorTestDatabase WHERE id=?", emptyList())
     }
 
+    @Test
+    fun testLoggingSupportSQLiteQuery() {
+        mDatabase.openHelper.writableDatabase.query(
+            SimpleSQLiteQuery(
+                "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
+                    "VALUES (?,?)",
+                arrayOf<Any>("3", "Description")
+            )
+        )
+        assertQueryLogged(
+            "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
+                "VALUES (?,?)",
+            listOf("3", "Description")
+        )
+    }
+
+    @Test
+    fun testNullBindArgument() {
+        mDatabase.openHelper.writableDatabase.query(
+            SimpleSQLiteQuery(
+                "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`,`description`) " +
+                    "VALUES (?,?)",
+                arrayOf("ID", null)
+            )
+        )
+        assertQueryLogged(
+            "INSERT OR ABORT INTO `queryInterceptorTestDatabase` (`id`," +
+                "`description`) VALUES (?,?)",
+            listOf("ID", null)
+        )
+    }
+
     private fun assertQueryLogged(
         query: String,
-        expectedArgs: List<Any>
+        expectedArgs: List<String?>
     ) {
         val filteredQueries = queryAndArgs.filter {
             it.first == query
         }
         assertThat(filteredQueries).hasSize(1)
-
-        for (index in expectedArgs.indices)
-            assertThat(expectedArgs).containsExactly(filteredQueries[0].second[index])
+        assertThat(expectedArgs).containsExactlyElementsIn(filteredQueries[0].second)
     }
 
     private fun assertTransactionQueries() {
diff --git a/room/runtime/src/main/java/androidx/room/QueryInterceptorDatabase.java b/room/runtime/src/main/java/androidx/room/QueryInterceptorDatabase.java
index 23dacc0..c5ef6bc 100644
--- a/room/runtime/src/main/java/androidx/room/QueryInterceptorDatabase.java
+++ b/room/runtime/src/main/java/androidx/room/QueryInterceptorDatabase.java
@@ -31,6 +31,7 @@
 import androidx.sqlite.db.SupportSQLiteStatement;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
@@ -167,17 +168,20 @@
     @NonNull
     @Override
     public Cursor query(@NonNull String query, @NonNull Object[] bindArgs) {
+        List<Object> inputArguments = new ArrayList<>();
+        inputArguments.addAll(Arrays.asList(bindArgs));
         mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(query,
-                Arrays.asList(bindArgs)));
+                inputArguments));
         return mDelegate.query(query, bindArgs);
     }
 
     @NonNull
     @Override
     public Cursor query(@NonNull SupportSQLiteQuery query) {
-        // TODO: (b/174478034)
+        QueryInterceptorProgram queryInterceptorProgram = new QueryInterceptorProgram();
+        query.bindTo(queryInterceptorProgram);
         mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(query.getSql(),
-                Collections.emptyList()));
+                queryInterceptorProgram.getBindArgs()));
         return mDelegate.query(query);
     }
 
@@ -185,9 +189,10 @@
     @Override
     public Cursor query(@NonNull SupportSQLiteQuery query,
             @NonNull CancellationSignal cancellationSignal) {
-        // TODO: (b/174478034)
+        QueryInterceptorProgram queryInterceptorProgram = new QueryInterceptorProgram();
+        query.bindTo(queryInterceptorProgram);
         mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(query.getSql(),
-                Collections.emptyList()));
+                queryInterceptorProgram.getBindArgs()));
         return mDelegate.query(query);
     }
 
@@ -213,14 +218,16 @@
 
     @Override
     public void execSQL(@NonNull String sql) throws SQLException {
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(sql, Collections.emptyList()));
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(sql, new ArrayList<>(0)));
         mDelegate.execSQL(sql);
     }
 
     @Override
     public void execSQL(@NonNull String sql, @NonNull Object[] bindArgs) throws SQLException {
-        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(sql, Arrays.asList(bindArgs)));
-        mDelegate.execSQL(sql, bindArgs);
+        List<Object> inputArguments = new ArrayList<>();
+        inputArguments.addAll(Arrays.asList(bindArgs));
+        mQueryCallbackExecutor.execute(() -> mQueryCallback.onQuery(sql, inputArguments));
+        mDelegate.execSQL(sql, inputArguments.toArray());
     }
 
     @Override
diff --git a/room/runtime/src/main/java/androidx/room/QueryInterceptorProgram.java b/room/runtime/src/main/java/androidx/room/QueryInterceptorProgram.java
new file mode 100644
index 0000000..2b9c554
--- /dev/null
+++ b/room/runtime/src/main/java/androidx/room/QueryInterceptorProgram.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room;
+
+import androidx.sqlite.db.SupportSQLiteProgram;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A program implementing an {@link SupportSQLiteProgram} API to record bind arguments.
+ */
+final class QueryInterceptorProgram implements SupportSQLiteProgram {
+    private List<Object> mBindArgsCache = new ArrayList<>();
+
+    @Override
+    public void bindNull(int index) {
+        saveArgsToCache(index, null);
+    }
+
+    @Override
+    public void bindLong(int index, long value) {
+        saveArgsToCache(index, value);
+    }
+
+    @Override
+    public void bindDouble(int index, double value) {
+        saveArgsToCache(index, value);
+    }
+
+    @Override
+    public void bindString(int index, String value) {
+        saveArgsToCache(index, value);
+    }
+
+    @Override
+    public void bindBlob(int index, byte[] value) {
+        saveArgsToCache(index, value);
+    }
+
+    @Override
+    public void clearBindings() {
+        mBindArgsCache.clear();
+    }
+
+    @Override
+    public void close() { }
+
+    private void saveArgsToCache(int bindIndex, Object value) {
+        // The index into bind methods are 1...n
+        int index = bindIndex - 1;
+        if (index >= mBindArgsCache.size()) {
+            for (int i = mBindArgsCache.size(); i <= index; i++) {
+                mBindArgsCache.add(null);
+            }
+        }
+        mBindArgsCache.set(index, value);
+    }
+
+    /**
+     * Returns the list of arguments associated with the query.
+     *
+     * @return argument list.
+     */
+    List<Object> getBindArgs() {
+        return mBindArgsCache;
+    }
+}
diff --git a/room/runtime/src/main/java/androidx/room/QueryInterceptorStatement.java b/room/runtime/src/main/java/androidx/room/QueryInterceptorStatement.java
index 6a08500..8825252 100644
--- a/room/runtime/src/main/java/androidx/room/QueryInterceptorStatement.java
+++ b/room/runtime/src/main/java/androidx/room/QueryInterceptorStatement.java
@@ -115,10 +115,13 @@
         mDelegate.close();
     }
 
-    private void saveArgsToCache(int index, Object value) {
-        // Add null entries to the list until we have the desired # of indices
-        for (int i = mBindArgsCache.size(); i <= index; i++) {
-            mBindArgsCache.add(null);
+    private void saveArgsToCache(int bindIndex, Object value) {
+        int index = bindIndex - 1;
+        if (index >= mBindArgsCache.size()) {
+            // Add null entries to the list until we have the desired # of indices
+            for (int i = mBindArgsCache.size(); i <= index; i++) {
+                mBindArgsCache.add(null);
+            }
         }
         mBindArgsCache.set(index, value);
     }