Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ package io.sentry.android.sqlite

import android.database.CrossProcessCursor
import android.database.CursorWindow
import android.database.CursorWrapper

/*
* SQLiteCursor executes the query lazily, when one of getCount() and onMove() is called.
* Also, by docs, fillWindow() can be used to fill the cursor with data.
* So we wrap these methods to create a span.
* SQLiteCursor is never used directly in the code, but only the Cursor interface.
* This means we can use CrossProcessCursor - that extends Cursor - as wrapper, since
* CrossProcessCursor is an interface and we can use Kotlin delegation.
* Ordinary Cursor methods are delegated through CursorWrapper to avoid adding Sentry frames to
* app database exceptions that the wrapper did not instrument.
*/
internal class SentryCrossProcessCursor(
private val delegate: CrossProcessCursor,
private val spans: OpenHelperSpans,
private val sql: String,
) : CrossProcessCursor by delegate {
) : CursorWrapper(delegate), CrossProcessCursor {
// We have to start the span only the first time, regardless of how many times its methods get
// called.
private var isSpanStarted = false
Expand All @@ -36,6 +36,8 @@ internal class SentryCrossProcessCursor(
return spans.performSql(sql) { delegate.onMove(oldPosition, newPosition) }
}

override fun getWindow(): CursorWindow? = delegate.window

override fun fillWindow(position: Int, window: CursorWindow?) {
if (isSpanStarted) {
return delegate.fillWindow(position, window)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.sentry.android.sqlite

import android.database.CrossProcessCursor
import android.database.CursorWrapper
import io.sentry.IScopes
import io.sentry.ISpan
import io.sentry.SentryOptions
Expand Down Expand Up @@ -52,13 +53,14 @@ class SentryCrossProcessCursorTest {

cursor.fillWindow(0, mock())
verify(fixture.mockCursor).fillWindow(eq(0), any())
}

// Let's verify other methods are delegated, even if not explicitly
cursor.close()
verify(fixture.mockCursor).close()
@Test
fun `ordinary cursor methods are delegated by Android CursorWrapper`() {
val getStringMethod =
SentryCrossProcessCursor::class.java.getMethod("getString", Int::class.javaPrimitiveType!!)

cursor.getString(1)
verify(fixture.mockCursor).getString(eq(1))
assertEquals(CursorWrapper::class.java, getStringMethod.declaringClass)
}

@Test
Expand Down
Loading