stoolap-python 0.4.0__tar.gz → 0.4.2__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.
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/Cargo.lock +43 -6
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/Cargo.toml +2 -2
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/PKG-INFO +1 -15
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/README.md +0 -14
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/pyproject.toml +1 -1
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/src/statement.rs +0 -4
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/src/transaction.rs +4 -8
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/tests/test_named_params.py +37 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/.github/workflows/ci.yml +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/.github/workflows/publish.yml +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/.gitignore +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/LICENSE +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/benchmark.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/python/stoolap/__init__.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/python/stoolap/__init__.pyi +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/src/database.rs +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/src/error.rs +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/src/lib.rs +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/src/value.rs +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/tests/test_async.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/tests/test_basic.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/tests/test_edge_cases.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/tests/test_persistence.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/tests/test_prepared.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/tests/test_transactions.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/tests/test_types.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.2}/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"
|
|
@@ -312,6 +333,15 @@ dependencies = [
|
|
|
312
333
|
"foldhash",
|
|
313
334
|
]
|
|
314
335
|
|
|
336
|
+
[[package]]
|
|
337
|
+
name = "hashbrown"
|
|
338
|
+
version = "0.17.1"
|
|
339
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
340
|
+
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
|
341
|
+
dependencies = [
|
|
342
|
+
"foldhash",
|
|
343
|
+
]
|
|
344
|
+
|
|
315
345
|
[[package]]
|
|
316
346
|
name = "heck"
|
|
317
347
|
version = "0.5.0"
|
|
@@ -406,11 +436,11 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
|
|
406
436
|
|
|
407
437
|
[[package]]
|
|
408
438
|
name = "lru"
|
|
409
|
-
version = "0.
|
|
439
|
+
version = "0.18.5"
|
|
410
440
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
411
|
-
checksum = "
|
|
441
|
+
checksum = "ef9ac18847474e638e3702b76c65d4eb93428471a74778ef0f1be711717f89b5"
|
|
412
442
|
dependencies = [
|
|
413
|
-
"hashbrown 0.
|
|
443
|
+
"hashbrown 0.17.1",
|
|
414
444
|
]
|
|
415
445
|
|
|
416
446
|
[[package]]
|
|
@@ -735,6 +765,12 @@ version = "1.0.22"
|
|
|
735
765
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
736
766
|
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
|
737
767
|
|
|
768
|
+
[[package]]
|
|
769
|
+
name = "ryu"
|
|
770
|
+
version = "1.0.23"
|
|
771
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
772
|
+
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
|
|
773
|
+
|
|
738
774
|
[[package]]
|
|
739
775
|
name = "scopeguard"
|
|
740
776
|
version = "1.2.0"
|
|
@@ -829,9 +865,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
|
|
829
865
|
|
|
830
866
|
[[package]]
|
|
831
867
|
name = "stoolap"
|
|
832
|
-
version = "0.4.
|
|
868
|
+
version = "0.4.2"
|
|
833
869
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
834
|
-
checksum = "
|
|
870
|
+
checksum = "88932817e3bb9551486e091b0c6400323086409cca8c35a37c3a68afe089e083"
|
|
835
871
|
dependencies = [
|
|
836
872
|
"ahash",
|
|
837
873
|
"anyhow",
|
|
@@ -839,6 +875,7 @@ dependencies = [
|
|
|
839
875
|
"chrono",
|
|
840
876
|
"crc32fast",
|
|
841
877
|
"crossbeam",
|
|
878
|
+
"csv",
|
|
842
879
|
"dashmap",
|
|
843
880
|
"getrandom 0.2.17",
|
|
844
881
|
"getrandom 0.3.4",
|
|
@@ -874,7 +911,7 @@ dependencies = [
|
|
|
874
911
|
|
|
875
912
|
[[package]]
|
|
876
913
|
name = "stoolap-python"
|
|
877
|
-
version = "0.4.
|
|
914
|
+
version = "0.4.2"
|
|
878
915
|
dependencies = [
|
|
879
916
|
"chrono",
|
|
880
917
|
"pyo3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "stoolap-python"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.2"
|
|
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.
|
|
19
|
+
stoolap = { version = "0.4.2", 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.
|
|
3
|
+
Version: 0.4.2
|
|
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
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|