diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9289b03..2d9cef6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,23 +20,32 @@ maturin develop # compile Rust + install in dev mode ``` ### Run Tests - + ```bash # Rust unit tests (no DB needed) cargo test - + # Python unit tests (no DB needed) python test.py - + # Integration tests (SQLite) python test.py --integration - + # All tests python test.py --all ``` - + +### Run Benchmarks + +To measure the performance of the query compiler: + +```bash +cd ryx-query && cargo bench +``` + ### Type Check + ```bash mypy ryx/ ``` diff --git a/README.md b/README.md index 25722f0..20e5791 100644 --- a/README.md +++ b/README.md @@ -75,10 +75,20 @@ async with ryx.transaction(): | **Backends** | All | All | **PG · MySQL · SQLite** | | **Migrations** | Built-in | Alembic | **Built-in** | -## Performance +## Architecture + +

+ Ryx Architecture +

+ +Your Python queries are compiled to SQL in Rust, executed by sqlx, and decoded back — all without blocking the Python event loop. +Since v0.1.3, the query engine has been extracted into a standalone crate `ryx-query`. This decouples the SQL compilation logic from the PyO3 bindings, enabling extreme performance and independent testing. + +## Performance + Benchmark of 1 000 rows on SQLite (lower is better): - + | Operation | Ryx ORM | SQLAlchemy ORM | SQLAlchemy Core | Ryx raw | |-----------|--------:|---------------:|----------------:|--------:| | **bulk_create** | 0.0074 s | 0.1696 s | 0.0022 s | 0.0011 s | @@ -86,59 +96,13 @@ Benchmark of 1 000 rows on SQLite (lower is better): | **bulk_delete** | 0.0005 s | 0.0012 s | 0.0009 s | 0.0004 s | | **filter + order + limit** | 0.0009 s | 0.0019 s | 0.0008 s | 0.0004 s | | **aggregate** | 0.0002 s | 0.0015 s | 0.0005 s | 0.0001 s | - + Ryx ORM is **16× faster** than SQLAlchemy ORM on bulk inserts and **2× faster** on deletes — while keeping the same Django-style API. The raw SQL layer (`raw_execute` / `raw_fetch`) gives you near-C speed when you need it. +**Internal Compilation Speed**: Our query compiler is blindingly fast, with simple lookups compiled in **~248ns** and complex query trees in **~1µs**. + Run the benchmark yourself: -```bash -uv add sqlalchemy[asyncio] aiosqlite -uv run python examples/13_benchmark_sqlalchemy.py -``` - -## Quick Start - -```bash -pip install maturin -maturin develop # compile Rust + install -``` - -```python -import asyncio, ryx -from ryx import Model, CharField - -class Article(Model): - title = CharField(max_length=200) - -async def main(): - await ryx.setup("sqlite:///app.db") - await ryx.migrate([Article]) - await Article.objects.create(title="Hello Ryx") - print(await Article.objects.all()) - -asyncio.run(main()) -``` - -## Key Features - -- **30+ field types** — from `AutoField` to `JSONField`, with validation built in -- **Q objects** — complex `AND` / `OR` / `NOT` expressions with nesting -- **Aggregations** — `Count`, `Sum`, `Avg`, `Min`, `Max` with `GROUP BY` and `HAVING` -- **Relationships** — `ForeignKey`, `OneToOneField`, `ManyToManyField` with `select_related` / `prefetch_related` -- **Transactions** — async context managers with nested savepoints -- **Signals** — `pre_save`, `post_save`, `pre_delete`, `post_delete` and more -- **Migrations** — autodetect schema changes, generate and apply -- **Validation** — field-level + model-level, collects all errors before raising -- **Sync/async bridge** — use from sync or async code seamlessly -- **CLI** — `python -m ryx migrate`, `makemigrations`, `shell`, `inspectdb` - -## Architecture - -

- Ryx Architecture -

- -Your Python queries are compiled to SQL in Rust, executed by sqlx, and decoded back — all without blocking the Python event loop. ## Documentation diff --git a/docs/doc/internals/architecture.mdx b/docs/doc/internals/architecture.mdx index a2d2471..0a8e9c2 100644 --- a/docs/doc/internals/architecture.mdx +++ b/docs/doc/internals/architecture.mdx @@ -7,7 +7,7 @@ sidebar_position: 2 Ryx is built in three layers, each with a clear responsibility. ## Layer Diagram - + ``` ┌──────────────────────────────────────────────────────────┐ │ Python Layer (ryx/) │ @@ -17,8 +17,11 @@ Ryx is built in three layers, each with a clear responsibility. │ PyO3 Boundary (src/lib.rs) │ │ QueryBuilder · TransactionHandle · Type Bridge · Async │ ├──────────────────────────────────────────────────────────┤ -│ Rust Core (src/) │ -│ AST · Q-Trees · SQL Compiler · Executor · Pool · Tx │ +│ Modular Query Engine (ryx-query crate) │ +│ AST · Q-Trees · SQL Compiler · Lookup Registry │ +├──────────────────────────────────────────────────────────┤ +│ Rust Core (src/) │ +│ Executor · Pool · Transaction Logic │ ├──────────────────────────────────────────────────────────┤ │ sqlx 0.8.6 + tokio 1.40 │ │ AnyPool · Async Drivers · Transactions │ @@ -27,6 +30,7 @@ Ryx is built in three layers, each with a clear responsibility. └──────────────────────────────────────────────────────────┘ ``` + ## Query Execution Flow ``` diff --git a/docs/doc/internals/query-compiler.mdx b/docs/doc/internals/query-compiler.mdx index 8905064..2c86595 100644 --- a/docs/doc/internals/query-compiler.mdx +++ b/docs/doc/internals/query-compiler.mdx @@ -3,11 +3,13 @@ sidebar_position: 4 --- # Query Compiler + +The heart of Ryx — transforms Python query expressions into optimized SQL. -The heart of Ryx — transforms Python query expressions into optimized SQL. - +Since v0.1.3, the compiler resides in the standalone `ryx-query` crate, decoupled from the Python bindings for maximum performance and testability. + ## Pipeline - + ``` Python QuerySet methods │ @@ -15,26 +17,27 @@ Python QuerySet methods QueryNode (Rust AST) │ ▼ -compiler::compile() +ryx_query::compiler::compile() │ ▼ CompiledQuery { sql: String, values: Vec } ``` - + ## AST Types - + ### QueryNode - + The root of every query: - + ```rust pub struct QueryNode { - pub operation: QueryOperation, // Select, Aggregate, Count, Delete, Update, Insert pub table: String, - pub columns: Vec, + pub backend: Backend, // DB backend for SQL generation + pub operation: QueryOperation, // Select, Aggregate, Count, Delete, Update, Insert pub filters: Vec, - pub q_tree: Option, + pub q_filter: Option, pub joins: Vec, + pub annotations: Vec, pub group_by: Vec, pub having: Vec, pub order_by: Vec, @@ -43,20 +46,20 @@ pub struct QueryNode { pub distinct: bool, } ``` - + ### QNode — Boolean Expression Tree - + ```rust pub enum QNode { Leaf { field: String, lookup: String, value: SqlValue, negated: bool }, - And { left: Box, right: Box }, - Or { left: Box, right: Box }, - Not { inner: Box }, + And(Vec), + Or(Vec), + Not(Box), } ``` - + ### SqlValue — Type-Safe Values - + ```rust pub enum SqlValue { Null, @@ -64,27 +67,25 @@ pub enum SqlValue { Int(i64), Float(f64), Text(String), - Bytes(Vec), - Date(chrono::NaiveDate), - Time(chrono::NaiveTime), - DateTime(chrono::NaiveDateTime), - Json(serde_json::Value), + List(Vec), } ``` - + ### JoinClause - + ```rust -pub enum JoinKind { Inner, LeftOuter, RightOuter, FullOuter, Cross } - +pub enum JoinKind { Inner, LeftOuter, RightOuter, FullOuter, CrossJoin } + pub struct JoinClause { - pub table: String, - pub condition: String, pub kind: JoinKind, + pub table: String, pub alias: Option, + pub on_left: String, + pub on_right: String, } ``` + ## Compilation Process 1. **SELECT clause** — `columns` or `*`