Skip to content

Add custom SQL function support#7

Merged
colemancda merged 3 commits into
masterfrom
feature/function
Jul 16, 2026
Merged

Add custom SQL function support#7
colemancda merged 3 commits into
masterfrom
feature/function

Conversation

@colemancda

Copy link
Copy Markdown
Member

Summary

Adds support for registering custom scalar SQL functions with a SQLite-backed store and invoking them from predicates and sort descriptors, plus a raw-SQL escape hatch for app-managed virtual tables (e.g. RTree). The motivating use case is efficient GPS distance filtering/sorting — an app registers a distance function and, if it wants spatial indexing, sets up its own RTree table via execute — but nothing here is geo-specific.

Changes

  • register(function:) on SQLiteDatabase and SQLiteViewContext — bridges a DatabaseFunction (from CoreModel) to Connection.createFunction, converting between AttributeValue and SQLite Binding at the boundary and invoking the registered closure inside the SQLite callback. Registration is per-connection, so the same function must be registered on both a database and its paired view context.
  • SQLiteDatabase.execute(_:_:) — runs raw SQL against the underlying connection so apps can CREATE VIRTUAL TABLE ... USING rtree(...) and keep it in sync themselves (e.g. via triggers). The library never creates, syncs, or knows about any virtual table itself.
  • Predicate.swift — compiles .function expressions to real SQL function calls (name(args...)) in WHERE clauses, with arguments compiled recursively (columns, constants, or nested calls).
  • Database.swift — the ORDER BY builder emits .function sort terms the same way, appending bindings in positional order.

Testing

New CustomFunctionTests register a Haversine distance function (defined in the test, not the library) and verify:

  • radius filtering via a .function predicate against an independently computed brute-force oracle,
  • distance sorting via a .function sort term,
  • combined filter + sort + fetchLimit,
  • the same function registered on a paired SQLiteViewContext,
  • unrelated (non-function) entities/queries are unaffected.

Full suite passes locally (56 tests).

Dependency note

This depends on the new DatabaseFunction / Expression.function / SortDescriptor.term API in CoreModel PR #22 (feature/functions branch), which is not yet in a released tag. CI will not pass until that PR merges and a new CoreModel release is tagged, at which point Package.swift should be bumped to that version.

Register custom scalar functions with a SQLite store and invoke them from
predicates and sort descriptors, plus a raw-SQL escape hatch for app-managed
virtual tables (e.g. R*Tree).

- SQLiteDatabase/SQLiteViewContext.register(function:) bridges a
  DatabaseFunction to Connection.createFunction, converting between
  AttributeValue and SQLite Binding at the boundary.
- SQLiteDatabase.execute(_:_:) runs raw SQL so apps can create and maintain
  their own virtual tables; the library never creates or syncs them itself.
- Predicate.swift compiles .function expressions to SQL function calls in
  WHERE clauses; Database.swift emits them in ORDER BY.
The custom function API landed in CoreModel 2.8.0, so depend on the release
instead of the local path override.
- matchesInMemorySwiftFilterAndSort: verifies the SQL distance function
  produces the same filtered set and sort order as filtering/sorting the
  same 200-point dataset in Swift.
- appManagedRTreePrefilter: demonstrates the app-managed R*Tree pattern —
  the app creates an rtree virtual table + sync triggers via execute(),
  uses it as a bounding-box prefilter, then applies the exact distance
  function; result matches a brute-force oracle and the prefilter is sound.

Gate the custom-function tests to Apple platforms and document the limit:
SQLite.swift's createFunction is unreliable on non-Apple platforms and
segfaults on Linux (upstream stephencelis/SQLite.swift#1071).
@colemancda
colemancda merged commit 85cf682 into master Jul 16, 2026
12 checks passed
@colemancda
colemancda deleted the feature/function branch July 16, 2026 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant