ferrum-orm 0.1.2__tar.gz → 0.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/Cargo.lock +26 -4
  2. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/Cargo.toml +4 -1
  3. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/PKG-INFO +116 -33
  4. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/README.md +97 -27
  5. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/crates/ferrum-core/benches/hydrate.rs +10 -0
  6. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/crates/ferrum-core/src/compile/mod.rs +443 -29
  7. ferrum_orm-0.1.4/crates/ferrum-core/src/error.rs +111 -0
  8. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/crates/ferrum-core/src/hydrate/mod.rs +8 -0
  9. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/crates/ferrum-core/src/ir/metadata.rs +55 -1
  10. ferrum_orm-0.1.4/crates/ferrum-core/src/ir/mod.rs +302 -0
  11. ferrum_orm-0.1.4/crates/ferrum-core/src/migrate/mod.rs +91 -0
  12. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/crates/ferrum-pyo3/Cargo.toml +1 -0
  13. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/crates/ferrum-pyo3/src/lib.rs +168 -30
  14. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/crates/ferrum-sql/Cargo.toml +3 -0
  15. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/crates/ferrum-sql/benches/compile.rs +15 -0
  16. ferrum_orm-0.1.4/crates/ferrum-sql/src/dialect.rs +143 -0
  17. ferrum_orm-0.1.4/crates/ferrum-sql/src/emit.rs +1801 -0
  18. ferrum_orm-0.1.4/crates/ferrum-sql/src/fts/mod.rs +318 -0
  19. ferrum_orm-0.1.4/crates/ferrum-sql/src/fts/mssql.rs +60 -0
  20. ferrum_orm-0.1.4/crates/ferrum-sql/src/fts/mysql.rs +62 -0
  21. ferrum_orm-0.1.4/crates/ferrum-sql/src/fts/postgres.rs +58 -0
  22. ferrum_orm-0.1.4/crates/ferrum-sql/src/fts/sqlite.rs +51 -0
  23. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/crates/ferrum-sql/src/lib.rs +1 -0
  24. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/pyproject.toml +34 -8
  25. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/__init__.py +51 -3
  26. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/_native.pyi +34 -1
  27. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/cli/app.py +20 -0
  28. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/cli/bootstrap.py +3 -2
  29. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/cli/init.py +3 -0
  30. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/cli/makemigrations_cmd.py +13 -0
  31. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/cli/migrate_cmd.py +24 -0
  32. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/cli/migrations_cmd.py +14 -4
  33. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/cli/revert_cmd.py +5 -0
  34. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/cli/showmigrations_cmd.py +6 -3
  35. ferrum_orm-0.1.4/python/ferrum/cli/sqlmigrate_cmd.py +53 -0
  36. ferrum_orm-0.1.4/python/ferrum/config.py +243 -0
  37. ferrum_orm-0.1.4/python/ferrum/connection.py +567 -0
  38. ferrum_orm-0.1.4/python/ferrum/contrib/fastapi.py +75 -0
  39. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/drivers/__init__.py +10 -1
  40. ferrum_orm-0.1.4/python/ferrum/drivers/mssql.py +200 -0
  41. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/drivers/mysql.py +11 -1
  42. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/drivers/postgres.py +33 -8
  43. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/drivers/protocol.py +17 -3
  44. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/drivers/sqlite.py +14 -3
  45. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/errors.py +43 -0
  46. ferrum_orm-0.1.4/python/ferrum/expressions.py +82 -0
  47. ferrum_orm-0.1.4/python/ferrum/ext/fts.py +95 -0
  48. ferrum_orm-0.1.4/python/ferrum/ext/pgvector.py +238 -0
  49. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/migrations/__init__.py +24 -0
  50. ferrum_orm-0.1.4/python/ferrum/migrations/fts/__init__.py +57 -0
  51. ferrum_orm-0.1.4/python/ferrum/migrations/fts/mssql.py +31 -0
  52. ferrum_orm-0.1.4/python/ferrum/migrations/fts/mysql.py +22 -0
  53. ferrum_orm-0.1.4/python/ferrum/migrations/fts/postgres.py +27 -0
  54. ferrum_orm-0.1.4/python/ferrum/migrations/fts/sqlite.py +54 -0
  55. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/migrations/introspect.py +81 -1
  56. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/migrations/ledger.py +48 -3
  57. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/migrations/loader.py +7 -0
  58. ferrum_orm-0.1.4/python/ferrum/migrations/operations.py +739 -0
  59. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/migrations/orchestrator.py +331 -15
  60. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/models.py +278 -30
  61. ferrum_orm-0.1.4/python/ferrum/observability.py +159 -0
  62. ferrum_orm-0.1.4/python/ferrum/queryset.py +2369 -0
  63. ferrum_orm-0.1.4/python/ferrum/registry.py +55 -0
  64. ferrum_orm-0.1.4/python/ferrum/relations.py +359 -0
  65. ferrum_orm-0.1.4/python/ferrum/runtime.py +188 -0
  66. ferrum_orm-0.1.4/python/ferrum/session.py +171 -0
  67. ferrum_orm-0.1.2/crates/ferrum-core/src/error.rs +0 -73
  68. ferrum_orm-0.1.2/crates/ferrum-core/src/ir/mod.rs +0 -152
  69. ferrum_orm-0.1.2/crates/ferrum-core/src/migrate/mod.rs +0 -72
  70. ferrum_orm-0.1.2/crates/ferrum-sql/src/dialect.rs +0 -94
  71. ferrum_orm-0.1.2/crates/ferrum-sql/src/emit.rs +0 -864
  72. ferrum_orm-0.1.2/python/ferrum/config.py +0 -104
  73. ferrum_orm-0.1.2/python/ferrum/connection.py +0 -292
  74. ferrum_orm-0.1.2/python/ferrum/contrib/fastapi.py +0 -40
  75. ferrum_orm-0.1.2/python/ferrum/ext/pgvector.py +0 -49
  76. ferrum_orm-0.1.2/python/ferrum/migrations/operations.py +0 -334
  77. ferrum_orm-0.1.2/python/ferrum/queryset.py +0 -1078
  78. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/LICENSE +0 -0
  79. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/crates/ferrum-core/Cargo.toml +0 -0
  80. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/crates/ferrum-core/src/lib.rs +0 -0
  81. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/cli/__init__.py +0 -0
  82. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/cli/inspectdb_cmd.py +0 -0
  83. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/cli/resetdb_cmd.py +0 -0
  84. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/contrib/__init__.py +0 -0
  85. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/ext/__init__.py +0 -0
  86. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/hooks.py +0 -0
  87. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/migrations/base.py +0 -0
  88. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/migrations/drift.py +0 -0
  89. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/migrations/gates.py +0 -0
  90. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/migrations/tokens.py +0 -0
  91. {ferrum_orm-0.1.2 → ferrum_orm-0.1.4}/python/ferrum/py.typed +0 -0
@@ -223,7 +223,7 @@ checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
223
223
 
224
224
  [[package]]
225
225
  name = "ferrum-core"
226
- version = "0.1.2"
226
+ version = "0.1.4"
227
227
  dependencies = [
228
228
  "criterion",
229
229
  "proptest",
@@ -234,7 +234,7 @@ dependencies = [
234
234
 
235
235
  [[package]]
236
236
  name = "ferrum-migrate"
237
- version = "0.1.2"
237
+ version = "0.1.4"
238
238
  dependencies = [
239
239
  "ferrum-core",
240
240
  "serde",
@@ -243,23 +243,26 @@ dependencies = [
243
243
 
244
244
  [[package]]
245
245
  name = "ferrum-pyo3"
246
- version = "0.1.2"
246
+ version = "0.1.4"
247
247
  dependencies = [
248
248
  "ferrum-core",
249
249
  "ferrum-sql",
250
250
  "pyo3",
251
+ "rmp-serde",
251
252
  "serde_json",
252
253
  "thiserror",
253
254
  ]
254
255
 
255
256
  [[package]]
256
257
  name = "ferrum-sql"
257
- version = "0.1.2"
258
+ version = "0.1.4"
258
259
  dependencies = [
259
260
  "criterion",
260
261
  "ferrum-core",
261
262
  "proptest",
263
+ "rmp-serde",
262
264
  "serde",
265
+ "serde_json",
263
266
  "thiserror",
264
267
  ]
265
268
 
@@ -726,6 +729,25 @@ version = "0.8.11"
726
729
  source = "registry+https://github.com/rust-lang/crates.io-index"
727
730
  checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
728
731
 
732
+ [[package]]
733
+ name = "rmp"
734
+ version = "0.8.15"
735
+ source = "registry+https://github.com/rust-lang/crates.io-index"
736
+ checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c"
737
+ dependencies = [
738
+ "num-traits",
739
+ ]
740
+
741
+ [[package]]
742
+ name = "rmp-serde"
743
+ version = "1.3.1"
744
+ source = "registry+https://github.com/rust-lang/crates.io-index"
745
+ checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155"
746
+ dependencies = [
747
+ "rmp",
748
+ "serde",
749
+ ]
750
+
729
751
  [[package]]
730
752
  name = "rustix"
731
753
  version = "1.1.4"
@@ -3,7 +3,7 @@ members = ["crates/ferrum-core", "crates/ferrum-sql", "crates/ferrum-pyo3"]
3
3
  resolver = "2"
4
4
 
5
5
  [workspace.package]
6
- version = "0.1.2"
6
+ version = "0.1.4"
7
7
  edition = "2021"
8
8
  authors = ["Ferrum Contributors"]
9
9
  license = "Apache-2.0"
@@ -17,6 +17,9 @@ pyo3 = { version = "0.29", features = ["abi3-py311"] }
17
17
  # Serialization
18
18
  serde = { version = "1", features = ["derive"] }
19
19
  serde_json = "1"
20
+ # MessagePack wire format for the PyO3 boundary (always compiled; the Python
21
+ # `[msgpack]` extra gates only the runtime `msgpack` package + selection).
22
+ rmp-serde = "1"
20
23
 
21
24
  # Error handling
22
25
  thiserror = "2"
@@ -1,14 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ferrum-orm
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Framework :: AsyncIO
6
6
  Classifier: Intended Audience :: Developers
7
7
  Classifier: License :: OSI Approved :: Apache Software License
8
+ Classifier: Operating System :: OS Independent
8
9
  Classifier: Programming Language :: Python :: 3
9
10
  Classifier: Programming Language :: Python :: 3.11
10
11
  Classifier: Programming Language :: Python :: 3.12
11
12
  Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
12
14
  Classifier: Programming Language :: Rust
13
15
  Classifier: Topic :: Database
14
16
  Classifier: Topic :: Software Development :: Libraries
@@ -17,6 +19,8 @@ Requires-Dist: pydantic>=2.0
17
19
  Requires-Dist: ferrum-orm[pg] ; extra == 'all'
18
20
  Requires-Dist: ferrum-orm[mysql] ; extra == 'all'
19
21
  Requires-Dist: ferrum-orm[sqlite] ; extra == 'all'
22
+ Requires-Dist: ferrum-orm[mssql] ; extra == 'all'
23
+ Requires-Dist: ferrum-orm[msgpack] ; extra == 'all'
20
24
  Requires-Dist: ferrum-orm[cli] ; extra == 'all'
21
25
  Requires-Dist: ferrum-orm[dotenv] ; extra == 'all'
22
26
  Requires-Dist: typer>=0.12 ; extra == 'cli'
@@ -32,8 +36,12 @@ Requires-Dist: ty>=0.0.49 ; extra == 'dev'
32
36
  Requires-Dist: import-linter>=2 ; extra == 'dev'
33
37
  Requires-Dist: testcontainers[postgres]>=4 ; extra == 'dev'
34
38
  Requires-Dist: pip-audit>=2 ; extra == 'dev'
39
+ Requires-Dist: fastapi>=0.110 ; extra == 'dev'
35
40
  Requires-Dist: python-dotenv>=1.0 ; extra == 'dotenv'
41
+ Requires-Dist: msgpack>=1.0 ; extra == 'msgpack'
42
+ Requires-Dist: aioodbc>=0.5 ; extra == 'mssql'
36
43
  Requires-Dist: asyncmy>=0.2 ; extra == 'mysql'
44
+ Requires-Dist: opentelemetry-api>=1.20 ; extra == 'otel'
37
45
  Requires-Dist: asyncpg>=0.29 ; extra == 'pg'
38
46
  Requires-Dist: aiosqlite>=0.19 ; extra == 'sqlite'
39
47
  Requires-Dist: uuid6>=2024.1 ; extra == 'uuid7'
@@ -42,20 +50,25 @@ Provides-Extra: cli
42
50
  Provides-Extra: dev
43
51
  Provides-Extra: dotenv
44
52
  Provides-Extra: fastapi
53
+ Provides-Extra: msgpack
54
+ Provides-Extra: mssql
45
55
  Provides-Extra: mysql
56
+ Provides-Extra: otel
46
57
  Provides-Extra: pg
47
58
  Provides-Extra: sqlite
48
59
  Provides-Extra: uuid7
49
60
  License-File: LICENSE
50
61
  Summary: Next-generation async ORM for Python with a Rust-powered core
51
- Keywords: orm,async,postgresql,mysql,sqlite,pydantic,rust
62
+ Keywords: orm,async,postgresql,mysql,sqlite,mssql,pydantic,rust,asyncpg,query-builder
63
+ Author: Ferrum Contributors
52
64
  License: Apache-2.0
53
65
  Requires-Python: >=3.11
54
66
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
55
- Project-URL: Bug Tracker, https://github.com/ferrumdb/ferrum/issues
56
- Project-URL: Documentation, https://ferrumdb.github.io/ferrum
57
- Project-URL: Homepage, https://github.com/ferrumdb/ferrum
58
- Project-URL: Repository, https://github.com/ferrumdb/ferrum
67
+ Project-URL: Changelog, https://github.com/ferrum-orm/ferrum/blob/main/CHANGELOG.md
68
+ Project-URL: Documentation, https://github.com/ferrum-orm/ferrum/tree/main/docs
69
+ Project-URL: Homepage, https://github.com/ferrum-orm/ferrum
70
+ Project-URL: Issues, https://github.com/ferrum-orm/ferrum/issues
71
+ Project-URL: Repository, https://github.com/ferrum-orm/ferrum
59
72
 
60
73
  # Ferrum
61
74
 
@@ -83,7 +96,7 @@ Ferrum aims to provide all four.
83
96
  - Pydantic-first models
84
97
  - Django-inspired ORM experience
85
98
  - Rust-powered query engine
86
- - PostgreSQL-first architecture (MySQL and SQLite via optional extras)
99
+ - PostgreSQL-first architecture (MySQL, SQLite, and SQL Server via optional extras)
87
100
  - Type-safe query construction
88
101
  - Automatic migrations
89
102
  - High-performance result hydration
@@ -172,6 +185,50 @@ Performance-critical components are implemented in Rust:
172
185
 
173
186
  This allows Ferrum to maintain a Pythonic API without sacrificing performance.
174
187
 
188
+ ### Cross-Driver Full-Text Search
189
+
190
+ Native full-text search across PostgreSQL, MySQL, SQLite FTS5, and SQL Server — one
191
+ QuerySet API, dialect-specific SQL emit and migration DDL.
192
+
193
+ **Query modes** (filter lookups and ranking):
194
+
195
+ | Mode | Lookup operator | Typical use |
196
+ | ----------- | ----------------- | ------------------------------------ |
197
+ | `plain` | `__match` | Natural-language terms |
198
+ | `phrase` | `__match_phrase` | Exact phrase |
199
+ | `websearch` | `__match_websearch` | Web-style quotes, `-` negation |
200
+ | `boolean` | `__match_boolean` | Boolean operators (`&`, `\|`, `!`) |
201
+
202
+ **Convenience methods:**
203
+
204
+ ```python
205
+ # Filter + relevance ranking in one call
206
+ hits = await Article.objects.search(
207
+ "python async orm", field="body", mode="websearch"
208
+ ).limit(10).all(conn)
209
+
210
+ # Rank without an implicit filter
211
+ ranked = await Article.objects.rank_by("body", "rust", mode="plain").all(conn)
212
+ ```
213
+
214
+ **Index declaration** — PostgreSQL uses `TSVector` columns; other drivers index base
215
+ `text` columns via `Meta.full_text_indexes`:
216
+
217
+ ```python
218
+ from ferrum.models import Field, FullTextIndex
219
+
220
+ class Article(Model):
221
+ search_vector: Annotated[TSVector, Field(fts_config="english")] | None = None
222
+ body: str = ""
223
+
224
+ class Meta:
225
+ full_text_indexes = [FullTextIndex(fields=("body",), config="english")]
226
+ ```
227
+
228
+ Query strings are always bound parameters; `fts_config` and index names come from
229
+ model-metadata allowlists only. See [Getting Started → Vector and full-text columns](docs/getting-started.md)
230
+ and [API Reference](docs/api-reference.md) for per-dialect DDL and operator mapping.
231
+
175
232
  ## Architecture
176
233
 
177
234
  ```text
@@ -199,34 +256,41 @@ This allows Ferrum to maintain a Pythonic API without sacrificing performance.
199
256
 
200
257
  ## Roadmap
201
258
 
202
- ### v0.1
203
-
204
- - [ ] PostgreSQL support
205
- - [ ] Basic CRUD operations
206
- - [ ] Async query execution
207
- - [ ] Pydantic models
208
- - [ ] Query builder
209
- - [ ] Type-safe filters
210
-
211
- ### v0.2
212
-
213
- - [ ] Relationships
214
- - [x] Transactions
215
- - [ ] Query optimization
216
- - [ ] Bulk operations
217
-
218
- ### v0.3
219
-
220
- - [ ] Migrations
221
- - [ ] Schema diff engine
222
- - [ ] CLI tools
259
+ ### v0.1 (complete)
260
+
261
+ - [x] PostgreSQL support
262
+ - [x] Basic CRUD operations
263
+ - [x] Async query execution
264
+ - [x] Pydantic models
265
+ - [x] Query builder
266
+ - [x] Type-safe filters
267
+ - [x] Transactions and savepoints
268
+ - [x] Bulk operations (`bulk_create`, `bulk_update`, `bulk_delete`)
269
+ - [x] Migrations (schema diff, apply, revert, CLI)
270
+ - [x] Relationships (ForeignKey, OneToOne, ManyToMany)
271
+ - [x] pgvector KNN search and HNSW/IVFFLAT index DDL
272
+ - [x] Full-text search (cross-dialect: PostgreSQL, MySQL, SQLite FTS5, SQL Server)
273
+ - [x] Observability hooks (Tier A/B/C)
274
+ - [x] CLI (`makemigrations`, `migrate`, `revert`, `showmigrations`, `inspectdb`, `resetdb`)
275
+
276
+ ### v0.2 (in progress)
277
+
278
+ - [x] Upsert API (`upsert`, `bulk_upsert` with conflict targets and `RETURNING`)
279
+ - [x] Composite primary keys
280
+ - [x] Array field types (`uuid[]`, `text[]`, scalar arrays)
281
+ - [x] JSONB operators (`__contains`, `__has_key`)
282
+ - [x] RLS / tenant session helpers (`set_config`, `tenant_session`)
283
+ - [x] `call_function` for allowlisted stored-procedure calls
284
+ - [x] Migration ops for extensions, RLS policies, and function DDL
285
+ - [x] pgvector similarity score projection (`vector_search` helper)
286
+ - [ ] Query optimization (deferred fields, prefetch tuning)
287
+ - [ ] Advanced relationship loading
223
288
 
224
289
  ### v1.0
225
290
 
226
291
  - [ ] Production-ready stability
227
- - [ ] Advanced relationships
228
- - [ ] Performance benchmarking
229
- - [ ] Full documentation
292
+ - [ ] Performance benchmarking suite
293
+ - [ ] Full documentation site
230
294
 
231
295
  ## Project Status
232
296
 
@@ -249,6 +313,12 @@ pip install 'ferrum-orm[mysql]'
249
313
  # SQLite + migrations CLI (testing / local dev)
250
314
  pip install 'ferrum-orm[sqlite,cli]'
251
315
 
316
+ # SQL Server (also needs a system ODBC driver, e.g. msodbcsql18)
317
+ pip install 'ferrum-orm[mssql]'
318
+
319
+ # Optional MessagePack wire format for the Python<->Rust boundary
320
+ pip install 'ferrum-orm[msgpack]'
321
+
252
322
  # Everything (all drivers + CLI + dotenv)
253
323
  pip install 'ferrum-orm[all]'
254
324
 
@@ -257,7 +327,19 @@ pip install ferrum-orm
257
327
  ```
258
328
 
259
329
  Bare `ferrum-orm` installs Pydantic and the Rust core only. Choose a driver extra
260
- (`pg`, `mysql`, or `sqlite`) before calling `ferrum.connect()`.
330
+ (`pg`, `mysql`, `sqlite`, or `mssql`) before calling `ferrum.connect()`.
331
+
332
+ MySQL, SQLite, and SQL Server are **thin-parity** backends: they support core CRUD
333
+ and migrations but not transactions, upsert, `bulk_update`, RLS, or pgvector
334
+ (PostgreSQL only). SQL Server connects via `aioodbc`/`pyodbc` and requires a system
335
+ ODBC driver such as `msodbcsql18`; DSNs use the `mssql://` or `sqlserver://` scheme.
336
+
337
+ ### Wire format (advanced)
338
+
339
+ The Python↔Rust IR/hydration boundary defaults to JSON. Installing the `msgpack`
340
+ extra lets you switch it to MessagePack, selected via the `FERRUM_WIRE_FORMAT`
341
+ environment variable (`json` | `msgpack`) or the `[ferrum] wire_format` key in
342
+ `ferrum.toml` / `pyproject.toml`. JSON remains the default; MessagePack is opt-in.
261
343
 
262
344
  From source, build the native extension with `maturin develop` (or `mise run dev`).
263
345
 
@@ -271,7 +353,8 @@ Runnable samples live under [`examples/`](examples/):
271
353
 
272
354
  ## Contributing
273
355
 
274
- Contributions are welcome.
356
+ Contributions are welcome. Start with [`CONTRIBUTING.md`](CONTRIBUTING.md) for local setup,
357
+ scoped verification, architecture rules, and pull request expectations.
275
358
 
276
359
  ## License
277
360
 
@@ -24,7 +24,7 @@ Ferrum aims to provide all four.
24
24
  - Pydantic-first models
25
25
  - Django-inspired ORM experience
26
26
  - Rust-powered query engine
27
- - PostgreSQL-first architecture (MySQL and SQLite via optional extras)
27
+ - PostgreSQL-first architecture (MySQL, SQLite, and SQL Server via optional extras)
28
28
  - Type-safe query construction
29
29
  - Automatic migrations
30
30
  - High-performance result hydration
@@ -113,6 +113,50 @@ Performance-critical components are implemented in Rust:
113
113
 
114
114
  This allows Ferrum to maintain a Pythonic API without sacrificing performance.
115
115
 
116
+ ### Cross-Driver Full-Text Search
117
+
118
+ Native full-text search across PostgreSQL, MySQL, SQLite FTS5, and SQL Server — one
119
+ QuerySet API, dialect-specific SQL emit and migration DDL.
120
+
121
+ **Query modes** (filter lookups and ranking):
122
+
123
+ | Mode | Lookup operator | Typical use |
124
+ | ----------- | ----------------- | ------------------------------------ |
125
+ | `plain` | `__match` | Natural-language terms |
126
+ | `phrase` | `__match_phrase` | Exact phrase |
127
+ | `websearch` | `__match_websearch` | Web-style quotes, `-` negation |
128
+ | `boolean` | `__match_boolean` | Boolean operators (`&`, `\|`, `!`) |
129
+
130
+ **Convenience methods:**
131
+
132
+ ```python
133
+ # Filter + relevance ranking in one call
134
+ hits = await Article.objects.search(
135
+ "python async orm", field="body", mode="websearch"
136
+ ).limit(10).all(conn)
137
+
138
+ # Rank without an implicit filter
139
+ ranked = await Article.objects.rank_by("body", "rust", mode="plain").all(conn)
140
+ ```
141
+
142
+ **Index declaration** — PostgreSQL uses `TSVector` columns; other drivers index base
143
+ `text` columns via `Meta.full_text_indexes`:
144
+
145
+ ```python
146
+ from ferrum.models import Field, FullTextIndex
147
+
148
+ class Article(Model):
149
+ search_vector: Annotated[TSVector, Field(fts_config="english")] | None = None
150
+ body: str = ""
151
+
152
+ class Meta:
153
+ full_text_indexes = [FullTextIndex(fields=("body",), config="english")]
154
+ ```
155
+
156
+ Query strings are always bound parameters; `fts_config` and index names come from
157
+ model-metadata allowlists only. See [Getting Started → Vector and full-text columns](docs/getting-started.md)
158
+ and [API Reference](docs/api-reference.md) for per-dialect DDL and operator mapping.
159
+
116
160
  ## Architecture
117
161
 
118
162
  ```text
@@ -140,34 +184,41 @@ This allows Ferrum to maintain a Pythonic API without sacrificing performance.
140
184
 
141
185
  ## Roadmap
142
186
 
143
- ### v0.1
144
-
145
- - [ ] PostgreSQL support
146
- - [ ] Basic CRUD operations
147
- - [ ] Async query execution
148
- - [ ] Pydantic models
149
- - [ ] Query builder
150
- - [ ] Type-safe filters
151
-
152
- ### v0.2
153
-
154
- - [ ] Relationships
155
- - [x] Transactions
156
- - [ ] Query optimization
157
- - [ ] Bulk operations
158
-
159
- ### v0.3
160
-
161
- - [ ] Migrations
162
- - [ ] Schema diff engine
163
- - [ ] CLI tools
187
+ ### v0.1 (complete)
188
+
189
+ - [x] PostgreSQL support
190
+ - [x] Basic CRUD operations
191
+ - [x] Async query execution
192
+ - [x] Pydantic models
193
+ - [x] Query builder
194
+ - [x] Type-safe filters
195
+ - [x] Transactions and savepoints
196
+ - [x] Bulk operations (`bulk_create`, `bulk_update`, `bulk_delete`)
197
+ - [x] Migrations (schema diff, apply, revert, CLI)
198
+ - [x] Relationships (ForeignKey, OneToOne, ManyToMany)
199
+ - [x] pgvector KNN search and HNSW/IVFFLAT index DDL
200
+ - [x] Full-text search (cross-dialect: PostgreSQL, MySQL, SQLite FTS5, SQL Server)
201
+ - [x] Observability hooks (Tier A/B/C)
202
+ - [x] CLI (`makemigrations`, `migrate`, `revert`, `showmigrations`, `inspectdb`, `resetdb`)
203
+
204
+ ### v0.2 (in progress)
205
+
206
+ - [x] Upsert API (`upsert`, `bulk_upsert` with conflict targets and `RETURNING`)
207
+ - [x] Composite primary keys
208
+ - [x] Array field types (`uuid[]`, `text[]`, scalar arrays)
209
+ - [x] JSONB operators (`__contains`, `__has_key`)
210
+ - [x] RLS / tenant session helpers (`set_config`, `tenant_session`)
211
+ - [x] `call_function` for allowlisted stored-procedure calls
212
+ - [x] Migration ops for extensions, RLS policies, and function DDL
213
+ - [x] pgvector similarity score projection (`vector_search` helper)
214
+ - [ ] Query optimization (deferred fields, prefetch tuning)
215
+ - [ ] Advanced relationship loading
164
216
 
165
217
  ### v1.0
166
218
 
167
219
  - [ ] Production-ready stability
168
- - [ ] Advanced relationships
169
- - [ ] Performance benchmarking
170
- - [ ] Full documentation
220
+ - [ ] Performance benchmarking suite
221
+ - [ ] Full documentation site
171
222
 
172
223
  ## Project Status
173
224
 
@@ -190,6 +241,12 @@ pip install 'ferrum-orm[mysql]'
190
241
  # SQLite + migrations CLI (testing / local dev)
191
242
  pip install 'ferrum-orm[sqlite,cli]'
192
243
 
244
+ # SQL Server (also needs a system ODBC driver, e.g. msodbcsql18)
245
+ pip install 'ferrum-orm[mssql]'
246
+
247
+ # Optional MessagePack wire format for the Python<->Rust boundary
248
+ pip install 'ferrum-orm[msgpack]'
249
+
193
250
  # Everything (all drivers + CLI + dotenv)
194
251
  pip install 'ferrum-orm[all]'
195
252
 
@@ -198,7 +255,19 @@ pip install ferrum-orm
198
255
  ```
199
256
 
200
257
  Bare `ferrum-orm` installs Pydantic and the Rust core only. Choose a driver extra
201
- (`pg`, `mysql`, or `sqlite`) before calling `ferrum.connect()`.
258
+ (`pg`, `mysql`, `sqlite`, or `mssql`) before calling `ferrum.connect()`.
259
+
260
+ MySQL, SQLite, and SQL Server are **thin-parity** backends: they support core CRUD
261
+ and migrations but not transactions, upsert, `bulk_update`, RLS, or pgvector
262
+ (PostgreSQL only). SQL Server connects via `aioodbc`/`pyodbc` and requires a system
263
+ ODBC driver such as `msodbcsql18`; DSNs use the `mssql://` or `sqlserver://` scheme.
264
+
265
+ ### Wire format (advanced)
266
+
267
+ The Python↔Rust IR/hydration boundary defaults to JSON. Installing the `msgpack`
268
+ extra lets you switch it to MessagePack, selected via the `FERRUM_WIRE_FORMAT`
269
+ environment variable (`json` | `msgpack`) or the `[ferrum] wire_format` key in
270
+ `ferrum.toml` / `pyproject.toml`. JSON remains the default; MessagePack is opt-in.
202
271
 
203
272
  From source, build the native extension with `maturin develop` (or `mise run dev`).
204
273
 
@@ -212,7 +281,8 @@ Runnable samples live under [`examples/`](examples/):
212
281
 
213
282
  ## Contributing
214
283
 
215
- Contributions are welcome.
284
+ Contributions are welcome. Start with [`CONTRIBUTING.md`](CONTRIBUTING.md) for local setup,
285
+ scoped verification, architecture rules, and pull request expectations.
216
286
 
217
287
  ## License
218
288
 
@@ -20,6 +20,8 @@ fn bench_metadata() -> ModelMetadata {
20
20
  allowed_operators: vec!["eq".into()],
21
21
  nullable: false,
22
22
  vector_dimensions: None,
23
+ fts_config: None,
24
+ fts_source_columns: None,
23
25
  },
24
26
  FieldMeta {
25
27
  name: "title".into(),
@@ -28,6 +30,8 @@ fn bench_metadata() -> ModelMetadata {
28
30
  allowed_operators: vec!["eq".into(), "icontains".into()],
29
31
  nullable: false,
30
32
  vector_dimensions: None,
33
+ fts_config: None,
34
+ fts_source_columns: None,
31
35
  },
32
36
  FieldMeta {
33
37
  name: "body".into(),
@@ -36,6 +40,8 @@ fn bench_metadata() -> ModelMetadata {
36
40
  allowed_operators: vec!["eq".into()],
37
41
  nullable: true,
38
42
  vector_dimensions: None,
43
+ fts_config: None,
44
+ fts_source_columns: None,
39
45
  },
40
46
  FieldMeta {
41
47
  name: "published".into(),
@@ -44,9 +50,13 @@ fn bench_metadata() -> ModelMetadata {
44
50
  allowed_operators: vec!["eq".into()],
45
51
  nullable: false,
46
52
  vector_dimensions: None,
53
+ fts_config: None,
54
+ fts_source_columns: None,
47
55
  },
48
56
  ],
49
57
  pk_index: 0,
58
+ pk_fields: vec![0],
59
+ full_text_indexes: vec![],
50
60
  }
51
61
  }
52
62