stoolap-python 0.4.0__tar.gz → 0.4.1__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 (27) hide show
  1. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/Cargo.lock +31 -3
  2. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/Cargo.toml +2 -2
  3. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/PKG-INFO +1 -15
  4. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/README.md +0 -14
  5. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/pyproject.toml +1 -1
  6. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/src/statement.rs +0 -4
  7. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/src/transaction.rs +4 -8
  8. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_named_params.py +37 -0
  9. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/.github/workflows/ci.yml +0 -0
  10. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/.github/workflows/publish.yml +0 -0
  11. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/.gitignore +0 -0
  12. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/LICENSE +0 -0
  13. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/benchmark.py +0 -0
  14. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/python/stoolap/__init__.py +0 -0
  15. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/python/stoolap/__init__.pyi +0 -0
  16. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/src/database.rs +0 -0
  17. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/src/error.rs +0 -0
  18. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/src/lib.rs +0 -0
  19. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/src/value.rs +0 -0
  20. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_async.py +0 -0
  21. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_basic.py +0 -0
  22. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_edge_cases.py +0 -0
  23. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_persistence.py +0 -0
  24. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_prepared.py +0 -0
  25. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_transactions.py +0 -0
  26. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_types.py +0 -0
  27. {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_vector.py +0 -0
@@ -210,6 +210,27 @@ dependencies = [
210
210
  "typenum",
211
211
  ]
212
212
 
213
+ [[package]]
214
+ name = "csv"
215
+ version = "1.4.0"
216
+ source = "registry+https://github.com/rust-lang/crates.io-index"
217
+ checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938"
218
+ dependencies = [
219
+ "csv-core",
220
+ "itoa",
221
+ "ryu",
222
+ "serde_core",
223
+ ]
224
+
225
+ [[package]]
226
+ name = "csv-core"
227
+ version = "0.1.13"
228
+ source = "registry+https://github.com/rust-lang/crates.io-index"
229
+ checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782"
230
+ dependencies = [
231
+ "memchr",
232
+ ]
233
+
213
234
  [[package]]
214
235
  name = "dashmap"
215
236
  version = "6.1.0"
@@ -735,6 +756,12 @@ version = "1.0.22"
735
756
  source = "registry+https://github.com/rust-lang/crates.io-index"
736
757
  checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
737
758
 
759
+ [[package]]
760
+ name = "ryu"
761
+ version = "1.0.23"
762
+ source = "registry+https://github.com/rust-lang/crates.io-index"
763
+ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
764
+
738
765
  [[package]]
739
766
  name = "scopeguard"
740
767
  version = "1.2.0"
@@ -829,9 +856,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
829
856
 
830
857
  [[package]]
831
858
  name = "stoolap"
832
- version = "0.4.0"
859
+ version = "0.4.1"
833
860
  source = "registry+https://github.com/rust-lang/crates.io-index"
834
- checksum = "420d8bd65519e56f55a9f13a891a373b1cda74ebc49a65a35dc4f2efe4441b40"
861
+ checksum = "a7ebe4615116d74154a6b36d1699793ae43af7c7a7a78739458f99f678456f9e"
835
862
  dependencies = [
836
863
  "ahash",
837
864
  "anyhow",
@@ -839,6 +866,7 @@ dependencies = [
839
866
  "chrono",
840
867
  "crc32fast",
841
868
  "crossbeam",
869
+ "csv",
842
870
  "dashmap",
843
871
  "getrandom 0.2.17",
844
872
  "getrandom 0.3.4",
@@ -874,7 +902,7 @@ dependencies = [
874
902
 
875
903
  [[package]]
876
904
  name = "stoolap-python"
877
- version = "0.4.0"
905
+ version = "0.4.1"
878
906
  dependencies = [
879
907
  "chrono",
880
908
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "stoolap-python"
3
- version = "0.4.0"
3
+ version = "0.4.1"
4
4
  edition = "2021"
5
5
  authors = ["Stoolap Contributors"]
6
6
  license = "Apache-2.0"
@@ -16,7 +16,7 @@ name = "_stoolap"
16
16
  crate-type = ["cdylib"]
17
17
 
18
18
  [dependencies]
19
- stoolap = { version = "0.4.0", default-features = false, features = ["parallel"] }
19
+ stoolap = { version = "0.4.1", default-features = false, features = ["parallel"] }
20
20
  pyo3 = { version = "0.23", features = ["extension-module"] }
21
21
  chrono = "0.4"
22
22
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stoolap-python
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: Apache Software License
@@ -31,20 +31,6 @@ Project-URL: Repository, https://github.com/stoolap/stoolap-python
31
31
 
32
32
  High-performance Python driver for [Stoolap](https://stoolap.io) embedded SQL database. Built with [PyO3](https://pyo3.rs) for native Rust performance with both sync and async APIs.
33
33
 
34
- ## Performance
35
-
36
- **53 out of 53 benchmark wins** against Python's built-in `sqlite3` on 10,000 rows:
37
-
38
- | Category | Highlights |
39
- |----------|-----------|
40
- | **Point Queries** | SELECT by ID: 1.5x, SELECT by index: 1.4-2.0x |
41
- | **Complex Queries** | SELECT complex: 4.8x, Scalar subquery: 19.5x |
42
- | **Aggregations** | GROUP BY: 24.8x, COUNT DISTINCT: 207x |
43
- | **Joins** | INNER JOIN: 1.1x, LEFT JOIN: 1.6x, Self JOIN: 1.3x |
44
- | **Subqueries** | IN subquery: 12.7x, NOT EXISTS: 42.8x, Nested 3-level: 16.3x |
45
- | **Window Functions** | ROW_NUMBER: 5.5x, PARTITION BY: 4.2x, ROWS frame: 2.7x |
46
- | **Write Operations** | DELETE complex: 133x, UPDATE complex: 6.7x |
47
-
48
34
  Run the benchmark yourself: `python benchmark.py`
49
35
 
50
36
  ## Installation
@@ -2,20 +2,6 @@
2
2
 
3
3
  High-performance Python driver for [Stoolap](https://stoolap.io) embedded SQL database. Built with [PyO3](https://pyo3.rs) for native Rust performance with both sync and async APIs.
4
4
 
5
- ## Performance
6
-
7
- **53 out of 53 benchmark wins** against Python's built-in `sqlite3` on 10,000 rows:
8
-
9
- | Category | Highlights |
10
- |----------|-----------|
11
- | **Point Queries** | SELECT by ID: 1.5x, SELECT by index: 1.4-2.0x |
12
- | **Complex Queries** | SELECT complex: 4.8x, Scalar subquery: 19.5x |
13
- | **Aggregations** | GROUP BY: 24.8x, COUNT DISTINCT: 207x |
14
- | **Joins** | INNER JOIN: 1.1x, LEFT JOIN: 1.6x, Self JOIN: 1.3x |
15
- | **Subqueries** | IN subquery: 12.7x, NOT EXISTS: 42.8x, Nested 3-level: 16.3x |
16
- | **Window Functions** | ROW_NUMBER: 5.5x, PARTITION BY: 4.2x, ROWS frame: 2.7x |
17
- | **Write Operations** | DELETE complex: 133x, UPDATE complex: 6.7x |
18
-
19
5
  Run the benchmark yourself: `python benchmark.py`
20
6
 
21
7
  ## Installation
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "stoolap-python"
7
- version = "0.4.0"
7
+ version = "0.4.1"
8
8
  description = "High-performance Python driver for Stoolap embedded SQL database"
9
9
  readme = "README.md"
10
10
  license = { text = "Apache-2.0" }
@@ -46,10 +46,6 @@ impl PreparedStatement {
46
46
  pub(crate) fn plan(&self) -> &CachedPlanRef {
47
47
  &self.plan
48
48
  }
49
-
50
- pub(crate) fn sql_text(&self) -> &str {
51
- &self.sql_text
52
- }
53
49
  }
54
50
 
55
51
  #[pymethods]
@@ -192,13 +192,12 @@ impl Transaction {
192
192
  ) -> PyResult<i64> {
193
193
  let bind = parse_params(params)?;
194
194
  let plan = stmt.plan().clone();
195
- let sql = stmt.sql_text().to_string();
196
195
  py.allow_threads(|| {
197
196
  let statement = plan.statement.as_ref();
198
197
  self.with_tx(|tx| match bind {
199
198
  BindParams::Positional(p) => tx.execute_prepared(statement, p).map_err(to_py),
200
199
  BindParams::Named(named) => {
201
- tx.execute_named(&sql, to_named_params(&named)).map_err(to_py)
200
+ tx.execute_prepared_named(statement, to_named_params(&named)).map_err(to_py)
202
201
  }
203
202
  })
204
203
  })
@@ -214,13 +213,12 @@ impl Transaction {
214
213
  ) -> PyResult<PyObject> {
215
214
  let bind = parse_params(params)?;
216
215
  let plan = stmt.plan().clone();
217
- let sql = stmt.sql_text().to_string();
218
216
  let rows = py.allow_threads(|| {
219
217
  let statement = plan.statement.as_ref();
220
218
  self.with_tx(|tx| match bind {
221
219
  BindParams::Positional(p) => tx.query_prepared(statement, p).map_err(to_py),
222
220
  BindParams::Named(named) => {
223
- tx.query_named(&sql, to_named_params(&named)).map_err(to_py)
221
+ tx.query_prepared_named(statement, to_named_params(&named)).map_err(to_py)
224
222
  }
225
223
  })
226
224
  })?;
@@ -237,13 +235,12 @@ impl Transaction {
237
235
  ) -> PyResult<PyObject> {
238
236
  let bind = parse_params(params)?;
239
237
  let plan = stmt.plan().clone();
240
- let sql = stmt.sql_text().to_string();
241
238
  let rows = py.allow_threads(|| {
242
239
  let statement = plan.statement.as_ref();
243
240
  self.with_tx(|tx| match bind {
244
241
  BindParams::Positional(p) => tx.query_prepared(statement, p).map_err(to_py),
245
242
  BindParams::Named(named) => {
246
- tx.query_named(&sql, to_named_params(&named)).map_err(to_py)
243
+ tx.query_prepared_named(statement, to_named_params(&named)).map_err(to_py)
247
244
  }
248
245
  })
249
246
  })?;
@@ -260,13 +257,12 @@ impl Transaction {
260
257
  ) -> PyResult<PyObject> {
261
258
  let bind = parse_params(params)?;
262
259
  let plan = stmt.plan().clone();
263
- let sql = stmt.sql_text().to_string();
264
260
  let rows = py.allow_threads(|| {
265
261
  let statement = plan.statement.as_ref();
266
262
  self.with_tx(|tx| match bind {
267
263
  BindParams::Positional(p) => tx.query_prepared(statement, p).map_err(to_py),
268
264
  BindParams::Named(named) => {
269
- tx.query_named(&sql, to_named_params(&named)).map_err(to_py)
265
+ tx.query_prepared_named(statement, to_named_params(&named)).map_err(to_py)
270
266
  }
271
267
  })
272
268
  })?;
@@ -247,3 +247,40 @@ def test_named_params_with_prefix_colon():
247
247
  row = db.query_one("SELECT val FROM t WHERE id = :id", {":id": 1})
248
248
  assert row["val"] == "hello"
249
249
  db.close()
250
+
251
+
252
+ # --- Transaction + prepared statement named params ---
253
+
254
+
255
+ def test_tx_prepared_execute_named(db):
256
+ stmt = db.prepare("INSERT INTO users VALUES (:id, :name, :email)")
257
+ with db.begin() as tx:
258
+ n = tx.execute_prepared(stmt, {"id": 3, "name": "Charlie", "email": "c@example.com"})
259
+ assert n == 1
260
+ row = db.query_one("SELECT name FROM users WHERE id = :id", {"id": 3})
261
+ assert row["name"] == "Charlie"
262
+
263
+
264
+ def test_tx_prepared_query_named(db):
265
+ stmt = db.prepare("SELECT * FROM users WHERE id = :id")
266
+ with db.begin() as tx:
267
+ rows = tx.query_prepared(stmt, {"id": 1})
268
+ assert len(rows) == 1
269
+ assert rows[0]["name"] == "Alice"
270
+
271
+ row = tx.query_one_prepared(stmt, {"id": 2})
272
+ assert row["name"] == "Bob"
273
+ assert tx.query_one_prepared(stmt, {"id": 999}) is None
274
+
275
+ raw = tx.query_raw_prepared(stmt, {"id": 1})
276
+ assert raw["columns"] == ["id", "name", "email"]
277
+ assert raw["rows"][0][1] == "Alice"
278
+
279
+
280
+ def test_tx_prepared_named_rollback(db):
281
+ stmt = db.prepare("INSERT INTO users VALUES (:id, :name, :email)")
282
+ with pytest.raises(RuntimeError):
283
+ with db.begin() as tx:
284
+ tx.execute_prepared(stmt, {"id": 4, "name": "Dave", "email": "d@example.com"})
285
+ raise RuntimeError("abort")
286
+ assert db.query_one("SELECT * FROM users WHERE id = :id", {"id": 4}) is None
File without changes