OpSQLiteDriver: executeAsync result format not handled by extractRowsFromStatementResult, causing silent data loss on SELECT queries
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 72/100
- Tipo de issue
- Error
- Claridad
- Bastante claro
- Estado de actividad
- Tranquilo
- Stack tecnológico
- react-native, typescript
- Área
- database, mobile-dev
Línea de trabajo
Localiza extractRowsFromStatementResult y resolveExecuteMethod en el paquete de persistencia React Native SQLite y, a continuación, inspecciona cómo se normalizan los resultados de los statements. Reproduce el escenario de reinicio con op-sqlite 14.1.4 y verifica que los resultados de SELECT conserven sus filas y que el inicio de la colección ya no termine en un error de restricción UNIQUE.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
- I've validated the bug against the latest version of DB packages
Describe the bug
OpSQLiteDriver in @tanstack/react-native-db-sqlite-persistence silently returns empty arrays for SELECT queries when op-sqlite's executeAsync method is used. This causes the persistence layer to think collections don't exist in collection_registry on app restart, leading to UNIQUE constraint violations.
Root cause
resolveExecuteMethod picks the first available method from [executeAsync, execute, executeRaw, execAsync]. On op-sqlite v14, executeAsync is available and gets selected.
The problem is that op-sqlite's executeAsync returns a different result format than execute:
executereturns:{ rows: Array<Record<string, Scalar>> }(object rows)executeAsyncreturns:{ rowsAffected: number, rawRows: unknown[][], columnNames: string[] }(raw columnar format)
extractRowsFromStatementResult only handles the { rows } and { resultRows } shapes. When it receives an executeAsync result:
toRowArray(value.rows)->value.rowsis undefined -> returns nulltoRowArray(value.resultRows)->value.resultRowsis undefined -> returns nullhasWriteResultMarker(value)->"rowsAffected" in value-> true -> returns[]
The SELECT result is silently treated as a write result with zero rows, even though the data is present in rawRows.
Impact
This causes a cascade of failures on app restart when the database already has data:
ensureCollectionReadyInternalSELECTs fromcollection_registry-> gets[]instead of the existing row- Code takes the INSERT branch -> fails with
UNIQUE constraint failed: collection_registry.tombstone_table_name getStreamPositionrejects -> the persistence runtime'sensureStartupMetadataLoadedfails- The Electric sync function is never called -> the collection stays in
loadingstate forever with no data
The ALTER TABLE ADD COLUMN errors in ensureInitialized are a separate but related symptom — executeAsync is used for those DDL statements too, and the existing error handling (isDuplicateColumnAddError) works but still logs errors.
To Reproduce
- Create a collection with
persistedCollectionOptionsandcreateReactNativeSQLitePersistenceusing op-sqlite v14+ - Let it sync data from an Electric shape
- Kill the app (full process kill, not hot reload)
- Relaunch the app
- The persistence layer crashes on startup — the collection never reaches
readystatus
Expected behavior
extractRowsFromStatementResult should handle the { rawRows, columnNames } format returned by executeAsync, converting it into the expected Array<Record<string, unknown>> shape. Alternatively, resolveExecuteMethod should prefer execute over executeAsync.
Smartphone (please complete the following information):
- Device: iOS Simulator & physical Android device
- OS: iOS 18, Android 14
- Version: N/A (React Native app, not browser)
Additional context
Package versions:
@tanstack/db-sqlite-persistence-core: 0.1.9@tanstack/react-native-db-sqlite-persistence: 0.1.9@op-engineering/op-sqlite: 14.1.4- React Native (Expo)
Current workaround:
Remove executeAsync from the database handle before passing it to createReactNativeSQLitePersistence, forcing the driver to fall back to execute:
const database = open({ name: 'my-db.sqlite', location: 'default' });
delete (database as any).executeAsync;
const persistence = createReactNativeSQLitePersistence({ database });
Suggested fix:
Either:
- Add
rawRows+columnNameshandling toextractRowsFromStatementResult:
function extractRowsFromStatementResult(value) {
// Handle op-sqlite executeAsync format: { rawRows, columnNames }
if (Array.isArray(value.rawRows) && Array.isArray(value.columnNames)) {
return value.rawRows.map((row) =>
Object.fromEntries(value.columnNames.map((col, i) => [col, row[i]]))
);
}
// ... existing logic
}
- Or change
resolveExecuteMethodto preferexecuteoverexecuteAsync, sinceexecutereturns the{ rows }format the driver already handles.
- Lenguaje dominante
- TypeScript
- Estrellas
- 3.9k
- Forks
- 268
- Merge medio
- 1 d 1 h
- PR fusionados (30 d)
- 212
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de TanStack/db
-
Dificultad 4/5 3-5 días Aptitud para principiantes 45/100
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 1-2 días Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día
-
electric-db-collection: on-demand + persistence fails after relaunch with "Snapshot requests are not supported in full mode"Posiblemente ocupada @KyleAMathews la tomó hace 2 días. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 68/100
TanStack/db#2056 · 1 reacción ·
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
TanStack/db#1972 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 5/5 Más de una semana Aptitud para principiantes 45/100
Los mantenedores suelen responder en 1 día
Todos los issues de TanStack/db
Issues similares
-
Add: CanalPlusActionEurope.nlAbiertocheck:passed streams:add
Dificultad 1/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 2 días
-
beta technical-medium ui
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
walletbeat/walletbeat#1625 ·
Los mantenedores suelen responder en 1 día
-
Good First Issue hacktoberfest
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
hiero-ledger/hiero-sdk-js#4489 ·
Los mantenedores suelen responder en 1 día
-
[Bug] The clients language filter cannot select the rows the page labels as unknownPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
apache/rocketmq-dashboard#6103 ·
Los mantenedores suelen responder en 4 días
-
Bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
payloadcms/payload#18652 ·
Los mantenedores suelen responder en 1 día