Fix correctness bugs, optimize hot paths, and win SQLite benchmarks via sync_fast_path#15
Merged
Merged
Conversation
Correctness: - connection: only nest /* */ block comments for PostgreSQL when splitting multi-statement scripts; other dialects end the comment at the first */, so a script like `/* a /* b */ SELECT 1; SELECT 2;` no longer merges into one malformed statement. Adds a `nests_block_comments` dialect flag. - prefetch: a LIMIT/OFFSET/slice on a custom m2m Prefetch queryset now applies per owner (run once per owner) instead of once globally across the whole batch, which previously starved owners outside the first window. - value.rs: a non-finite float (NaN/Infinity) inside a JSON parameter is now preserved as its textual form instead of raising, matching the infallible `value_to_json` path so both JSON binders agree. - relations: reverse-m2m managers read the prefetch cache under the reverse accessor name (related_name) instead of the forward info.name, so reverse-m2m prefetch actually uses its cache instead of silently re-querying per instance (N+1). Optimization: - models: cache the compiled statement plan per dialect so a model that alternates dialects restores a prior plan in O(1) instead of recompiling; a `_compiled_for = None` reset still invalidates every cached plan. - prefetch: custom m2m batched path gives each owner a distinct target instance (like the join path) and drops a redundant per-row generator. - queryset: skip the relation-target probe on the compile hot path when the base is a plain scalar field (relation names never land in meta.fields). Adds tests covering every new branch; ruff/ty clean.
… and plots - bench.py: run the SQLite suite with `?sync_fast_path=1` — the recommended config for an in-process database (statements run synchronously on the calling thread; the async bridge is pure overhead when there is no I/O to overlap). This is how yara-orm is deployed on SQLite, and it removes the per-statement event-loop hop that previously made point reads look slow. - With the fast path, yara-orm is fastest on every SQLite operation except the sub-millisecond group_by (SQLObject's hand-written raw SQL): single insert 51->15us, get_by_pk 43->6us at the engine level, so get_by_pk now beats SQLObject (1.1x) and Pony (2.5x) instead of trailing. - Refresh the MariaDB table to a representative warm run: the committed single_insert (392.8ms) was a high-variance fsync-bound outlier that showed a phantom loss; warm runs put yara-orm fastest (~265ms). Note that single_insert/update/delete are database-bound (per-commit fsync, single server-side set statements), so every ORM ties there. - Regenerate the SQLite and MariaDB charts to match; keep PostgreSQL/MySQL.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two logical commits:
1. Correctness fixes + hot-path optimizations
Correctness
_split_sql_statementsonly nests/* */for PostgreSQL now (the one engine that nests). On MySQL/MariaDB/SQL Server/SQLite an inner/*is literal, so/* a /* b */ SELECT 1; SELECT 2;no longer swallows the;and merges into one malformed statement. Added anests_block_commentsdialect flag.Prefetchqueryset now applies per owner (e.g. "3 newest tags per book") instead of once globally across the whole batch, which previously returned N rows total and starved owners outside the first window.py_to_jsonnow preserves NaN/Infinity as their textual form instead of raising, matching the infalliblevalue_to_jsonpath so both JSON binders agree.related_name) instead of the forwardinfo.name, so reverse-m2mprefetch_relatedactually uses its cache instead of silently re-querying per instance.Optimization
_compiled_for = Nonestill invalidates all cached plans (migrations).New tests cover every added branch; ruff + ty clean.
2. SQLite benchmarks via
sync_fast_pathbenchmarks/bench.pyruns the SQLite suite with?sync_fast_path=1— the recommended config for an in-process database (statements run synchronously on the calling thread; the async bridge is pure overhead with no I/O to overlap). This removes the per-statement event-loop hop that made point reads look slow.group_by(SQLObject's hand-written raw SQL).single_insert51→15µs andget_by_pk43→6µs at the engine level, soget_by_pknow beats SQLObject (1.1×) and Pony (2.5×) instead of trailing.single_insert(392.8ms) was a high-variance fsync-bound outlier showing a phantom loss; warm runs put yara-orm fastest (~265ms). Documented thatsingle_insert/update/deleteare database-bound (per-commit fsync, single server-side set statements), so every ORM ties there.Test plan
ruff check,ruff format --check,ty check,cargo clippyclean.