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.
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/Cargo.lock +31 -3
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/Cargo.toml +2 -2
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/PKG-INFO +1 -15
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/README.md +0 -14
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/pyproject.toml +1 -1
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/src/statement.rs +0 -4
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/src/transaction.rs +4 -8
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_named_params.py +37 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/.github/workflows/ci.yml +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/.github/workflows/publish.yml +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/.gitignore +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/LICENSE +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/benchmark.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/python/stoolap/__init__.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/python/stoolap/__init__.pyi +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/src/database.rs +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/src/error.rs +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/src/lib.rs +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/src/value.rs +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_async.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_basic.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_edge_cases.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_persistence.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_prepared.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_transactions.py +0 -0
- {stoolap_python-0.4.0 → stoolap_python-0.4.1}/tests/test_types.py +0 -0
- {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.
|
|
859
|
+
version = "0.4.1"
|
|
833
860
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
834
|
-
checksum = "
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
@@ -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
|