diff --git a/CHANGELOG-Unreleased.md b/CHANGELOG-Unreleased.md index dbbbd9d5b..097c7585a 100644 --- a/CHANGELOG-Unreleased.md +++ b/CHANGELOG-Unreleased.md @@ -19,6 +19,8 @@ - [LokiJS] When accessing or opening IndexedDB throws synchronously (e.g. `SecurityError` when storage access is denied), the adapter now falls back to an in-memory database like for other IndexedDB open failures (or fails setup with `allowInMemoryFallback: false`), instead of always failing setup - [Android] Added linker flag for building with 16kB page alignment - [TS] make catchError visible to typescript +- [Observation] Fixed `canEncodeMatcher()` reporting queries containing `Q.unsafeSqlExpr`, + `Q.unsafeLokiExpr`, or a `Q.on` nested inside `Q.and`/`Q.or` as encodable. - [LokiJS] Fixed `unsafeResetDatabase()` failing with "another operation is already in progress" when it's called while an autosave is in progress - [iOS] Fixed compilation on React Native 0.88, which removed `RCTCxxBridge`; JSI is installed through the bridge or the bridgeless proxy alike - [Android] Generate `BuildConfig` explicitly (AGP 9 no longer does for libraries) and stop applying the unused Kotlin plugin, which collides with AGP 9's built-in Kotlin support diff --git a/src/observation/encodeMatcher/canEncode.js b/src/observation/encodeMatcher/canEncode.js index dd6ab8d08..007851915 100644 --- a/src/observation/encodeMatcher/canEncode.js +++ b/src/observation/encodeMatcher/canEncode.js @@ -1,11 +1,23 @@ // @flow -import type { QueryDescription } from '../../QueryDescription' +import type { QueryDescription, Where } from '../../QueryDescription' -export const forbiddenError = `Queries with joins, sortBy, take, skip, lokiTransform can't be encoded into a matcher` +export const forbiddenError = `Queries with joins, sortBy, take, skip, lokiTransform, Q.unsafeSqlExpr, Q.unsafeLokiExpr can't be encoded into a matcher` + +const isEncodableWhere = (where: Where): boolean => { + switch (where.type) { + case 'where': + return true + case 'and': + case 'or': + return where.conditions.every(isEncodableWhere) + default: + return false + } +} export default function canEncodeMatcher(query: QueryDescription): boolean { - const { joinTables, nestedJoinTables, sortBy, take, skip, lokiTransform, sql } = query + const { joinTables, nestedJoinTables, sortBy, take, skip, lokiTransform, sql, where } = query return ( !joinTables.length && @@ -14,6 +26,7 @@ export default function canEncodeMatcher(query: QueryDescription): boolean { !take && !skip && !lokiTransform && - !sql + !sql && + where.every(isEncodableWhere) ) } diff --git a/src/observation/encodeMatcher/test.js b/src/observation/encodeMatcher/test.js index 5be8bfd77..f69dd5a99 100644 --- a/src/observation/encodeMatcher/test.js +++ b/src/observation/encodeMatcher/test.js @@ -23,6 +23,11 @@ const unencodableQueries = [ [Q.take(100)], [Q.unsafeLokiTransform(() => {})], [Q.unsafeSqlQuery('select * from tasks')], + [Q.unsafeSqlExpr('')], + [Q.unsafeLokiExpr({})], + [Q.or(Q.on('projects', 'team_id', 'abcdef'))], + [Q.or(Q.unsafeSqlExpr(''))], + [Q.or(Q.unsafeLokiExpr({}))], ] describe('SQLite encodeMatcher', () => { @@ -61,9 +66,6 @@ describe('SQLite encodeMatcher', () => { // console.log(query) expect(() => makeMatcher(query)).toThrow(`can't be encoded into a matcher`) }) - expect(() => makeMatcher([Q.or(Q.on('projects', 'team_id', 'abcdef'))])).toThrow('Illegal Q.on') - expect(() => makeMatcher([Q.or(Q.unsafeSqlExpr(''))])).toThrow('Illegal') - expect(() => makeMatcher([Q.or(Q.unsafeLokiExpr({}))])).toThrow('Illegal') }) }) diff --git a/src/observation/test.js b/src/observation/test.js index f4d6eadfe..1104fe6b1 100644 --- a/src/observation/test.js +++ b/src/observation/test.js @@ -96,4 +96,17 @@ describe('common observation tests', () => { subscribeToQueryWithColumns(mockDb.tasks.query(...complexQuery), ['position'], observer), ) }) + const unencodableQuery = Q.unsafeLokiExpr({ is_completed: true }) + it(`updates list before model - test with an unencodable where clause`, async () => { + const mockDb = mockDatabase() + await updatesListBeforeModelTest(mockDb, (observer) => + subscribeToQuery(mockDb.tasks.query(unencodableQuery), observer), + ) + }) + it(`updates list before model - test with an unencodable where clause and columns`, async () => { + const mockDb = mockDatabase() + await updatesListBeforeModelTest(mockDb, (observer) => + subscribeToQueryWithColumns(mockDb.tasks.query(unencodableQuery), ['position'], observer), + ) + }) })