sqlengine-lite 2.1.1__tar.gz → 2.2.0__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.
- {sqlengine_lite-2.1.1 → sqlengine_lite-2.2.0}/PKG-INFO +53 -9
- {sqlengine_lite-2.1.1 → sqlengine_lite-2.2.0}/README.md +52 -8
- {sqlengine_lite-2.1.1 → sqlengine_lite-2.2.0}/pyproject.toml +1 -1
- sqlengine_lite-2.2.0/src/sqlengine/__init__.py +7 -0
- sqlengine_lite-2.2.0/src/sqlengine/core/__init__.py +4 -0
- sqlengine_lite-2.2.0/src/sqlengine/core/connection.py +255 -0
- {sqlengine_lite-2.1.1/src/sqlengine/utils → sqlengine_lite-2.2.0/src/sqlengine/core}/repr.py +1 -30
- {sqlengine_lite-2.1.1/src/sqlengine/utils → sqlengine_lite-2.2.0/src/sqlengine/core}/sqlgen.py +2 -4
- {sqlengine_lite-2.1.1/src/sqlengine/utils → sqlengine_lite-2.2.0/src/sqlengine/core}/statements.py +36 -36
- {sqlengine_lite-2.1.1/src/sqlengine/utils → sqlengine_lite-2.2.0/src/sqlengine/core}/types.py +47 -8
- {sqlengine_lite-2.1.1 → sqlengine_lite-2.2.0}/src/sqlengine/schema.py +1 -1
- {sqlengine_lite-2.1.1 → sqlengine_lite-2.2.0}/src/sqlengine/sqltable.py +162 -276
- sqlengine_lite-2.2.0/src/sqlengine/utils/__init__.py +4 -0
- {sqlengine_lite-2.1.1 → sqlengine_lite-2.2.0}/src/sqlengine/utils/connection.py +19 -13
- sqlengine_lite-2.2.0/src/sqlengine/utils/convert.py +24 -0
- {sqlengine_lite-2.1.1 → sqlengine_lite-2.2.0}/src/sqlengine_lite.egg-info/PKG-INFO +53 -9
- {sqlengine_lite-2.1.1 → sqlengine_lite-2.2.0}/src/sqlengine_lite.egg-info/SOURCES.txt +7 -4
- {sqlengine_lite-2.1.1 → sqlengine_lite-2.2.0}/tests/test.py +133 -32
- sqlengine_lite-2.1.1/src/sqlengine/__init__.py +0 -7
- sqlengine_lite-2.1.1/src/sqlengine/utils/__init__.py +0 -5
- {sqlengine_lite-2.1.1 → sqlengine_lite-2.2.0}/LICENSE +0 -0
- {sqlengine_lite-2.1.1 → sqlengine_lite-2.2.0}/setup.cfg +0 -0
- {sqlengine_lite-2.1.1 → sqlengine_lite-2.2.0}/src/sqlengine_lite.egg-info/dependency_links.txt +0 -0
- {sqlengine_lite-2.1.1 → sqlengine_lite-2.2.0}/src/sqlengine_lite.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlengine-lite
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Cute sqlite3 wrapper for sql tables
|
|
5
5
|
Project-URL: Homepage, https://github.com/suffermuffin/SQL-Engine
|
|
6
6
|
Project-URL: Repository, https://github.com/suffermuffin/SQL-Engine.git
|
|
@@ -25,6 +25,7 @@ Dynamic: license-file
|
|
|
25
25
|
- [Delete Query](#delete-query)
|
|
26
26
|
- [Transaction](#transaction)
|
|
27
27
|
- [Get Item](#get-item)
|
|
28
|
+
- [Custom Types](#custom-types)
|
|
28
29
|
- [Csv Converter](#csv-converter)
|
|
29
30
|
- [Full Documentation](#full-documentation)
|
|
30
31
|
|
|
@@ -44,17 +45,21 @@ Sql-Engine implements Jupyter integration and dynamic schema building. You can e
|
|
|
44
45
|
```py
|
|
45
46
|
from sqlengine import schema
|
|
46
47
|
|
|
47
|
-
table = schema.table_from_database("temp/chinook.db", "
|
|
48
|
+
table = schema.table_from_database("temp/chinook.db", "Album")
|
|
48
49
|
table
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
<table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">
|
|
52
|
+
<table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">Album</caption><thead><tr><td style="border: 1px solid #555; text-align: center;">AlbumId</td><td style="border: 1px solid #555; text-align: center;">Title</td><td style="border: 1px solid #555; text-align: center;">ArtistId</td></tr></thead><tbody><tr><td style="border: 1px solid #000; text-align: center;">1</td><td style="border: 1px solid #000; text-align: center;">For Those About To Rock We Salute You</td><td style="border: 1px solid #000; text-align: center;">1</td></tr><tr><td style="border: 1px solid #000; text-align: center;">2</td><td style="border: 1px solid #000; text-align: center;">Balls to the Wall</td><td style="border: 1px solid #000; text-align: center;">2</td></tr><tr><td style="border: 1px solid #000; text-align: center;">3</td><td style="border: 1px solid #000; text-align: center;">Restless and Wild</td><td style="border: 1px solid #000; text-align: center;">2</td></tr><tr><td style="border: 1px solid #000; text-align: center;">4</td><td style="border: 1px solid #000; text-align: center;">Let There Be Rock</td><td style="border: 1px solid #000; text-align: center;">1</td></tr><tr><td style="border: 1px solid #000; text-align: center;">5</td><td style="border: 1px solid #000; text-align: center;">Big Ones</td><td style="border: 1px solid #000; text-align: center;">3</td></tr><tr><td style="border: 1px solid #000; text-align: center;">6</td><td style="border: 1px solid #000; text-align: center;">Jagged Little Pill</td><td style="border: 1px solid #000; text-align: center;">4</td></tr><tr><td style="border: 1px solid #000; text-align: center;">7</td><td style="border: 1px solid #000; text-align: center;">Facelift</td><td style="border: 1px solid #000; text-align: center;">5</td></tr><tr><td style="border: 1px solid #000; text-align: center;">8</td><td style="border: 1px solid #000; text-align: center;">Warner 25 Anos</td><td style="border: 1px solid #000; text-align: center;">6</td></tr><tr><td style="border: 1px solid #000; text-align: center;">9</td><td style="border: 1px solid #000; text-align: center;">Plays Metallica By Four Cellos</td><td style="border: 1px solid #000; text-align: center;">7</td></tr><tr><td style="border: 1px solid #000; text-align: center;">10</td><td style="border: 1px solid #000; text-align: center;">Audioslave</td><td style="border: 1px solid #000; text-align: center;">8</td></tr><tr><td colspan="3" style="text-align:center;color:#888;font-style:italic;padding:8px;">... more rows ...</td></tr></tbody></table>
|
|
53
|
+
|
|
54
|
+
Note that GitHub's html rendering is simplified.
|
|
52
55
|
|
|
53
56
|
---
|
|
54
57
|
|
|
55
58
|
You can preview select statements before fetching data to your variables.
|
|
56
59
|
|
|
57
60
|
```py
|
|
61
|
+
table = schema.table_from_database("temp/chinook.db", "Invoice")
|
|
62
|
+
|
|
58
63
|
table.select("InvoiceId", "CustomerId", "BillingAddress", "BillingCountry", "Total")\
|
|
59
64
|
.where\
|
|
60
65
|
.gte("Total", 2.0)\
|
|
@@ -68,7 +73,7 @@ table.select("InvoiceId", "CustomerId", "BillingAddress", "BillingCountry", "Tot
|
|
|
68
73
|
|
|
69
74
|
## Purpose
|
|
70
75
|
|
|
71
|
-
It's a tiny little modern ORM that lets you prototype your databases locally with great flexibility. Also, it can be used in production apps to store and retrieve data, because all select, update, delete queries are parametrized. But it does not restrict you from using your own queries which might not be paramerized with methods like `select.custom()` and `where.custom()`.
|
|
76
|
+
It's a tiny little modern ORM-like that lets you prototype your databases locally with great flexibility. Also, it can be used in production apps to store and retrieve data, because all select, update, delete queries are parametrized. But it does not restrict you from using your own queries which might not be paramerized with methods like `select.custom()` and `where.custom()`.
|
|
72
77
|
|
|
73
78
|
And last (but not least) is data inspection. If you need to quickly inspect existing .db file but don't want to install yet another heavy ORM with a lot of unused dependencies, you might look into Sql-Engine, as it uses only native python modules.
|
|
74
79
|
|
|
@@ -119,7 +124,7 @@ __primary__ : list[str]
|
|
|
119
124
|
More details at [Declaration](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#table-declaration).
|
|
120
125
|
|
|
121
126
|
```py
|
|
122
|
-
from sqlengine import SqlTableMixin
|
|
127
|
+
from sqlengine import SqlTableMixin, Primary
|
|
123
128
|
|
|
124
129
|
# Helper constants for column names
|
|
125
130
|
ID = "ID"
|
|
@@ -130,9 +135,10 @@ Salary = "Salary"
|
|
|
130
135
|
|
|
131
136
|
class Employees(SqlTableMixin):
|
|
132
137
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
138
|
+
ID : Primary[int]
|
|
139
|
+
Name : str
|
|
140
|
+
Occupation : str
|
|
141
|
+
Salary : float
|
|
136
142
|
|
|
137
143
|
# You may overwrite your insert methods for type consistency
|
|
138
144
|
def insert(self, id : int, name : str, occupation : str, salary : float) -> None:
|
|
@@ -149,8 +155,9 @@ More details at [Instantiation](https://github.com/suffermuffin/SQL-Engine/blob/
|
|
|
149
155
|
```py
|
|
150
156
|
# Create an instance of the table class
|
|
151
157
|
# with provided path to create or connect to
|
|
158
|
+
# `force_drop=True` to overwrite existing table if exists
|
|
152
159
|
|
|
153
|
-
table = Employees("temp/data.db")
|
|
160
|
+
table = Employees("temp/data.db", force_drop=True)
|
|
154
161
|
```
|
|
155
162
|
|
|
156
163
|
## Row insertion
|
|
@@ -267,6 +274,43 @@ table[4:10:2]
|
|
|
267
274
|
# (8, 'Frank White', 'Quality Assurance', 53000.0)]
|
|
268
275
|
```
|
|
269
276
|
|
|
277
|
+
## Custom Types
|
|
278
|
+
|
|
279
|
+
More details at [Custom Types](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/custom_types.md).
|
|
280
|
+
|
|
281
|
+
```py
|
|
282
|
+
from datetime import datetime
|
|
283
|
+
from sqlengine import SqlTableMixin, Primary
|
|
284
|
+
|
|
285
|
+
class DateTime(datetime):
|
|
286
|
+
|
|
287
|
+
def to_sql(self) -> str:
|
|
288
|
+
return self.strftime("%Y-%m-%d %H:%M")
|
|
289
|
+
|
|
290
|
+
@classmethod
|
|
291
|
+
def from_sql(cls, sql : bytes):
|
|
292
|
+
return cls.fromisoformat(sql.decode('utf-8'))
|
|
293
|
+
|
|
294
|
+
def __repr__(self):
|
|
295
|
+
return f"DateTime({self.time})"
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class ReservationIndex(SqlTableMixin):
|
|
299
|
+
|
|
300
|
+
user_id : Primary[int]
|
|
301
|
+
room_id : Primary[str]
|
|
302
|
+
time_at : Primary[DateTime]
|
|
303
|
+
user_name : str | None
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
table = ReservationIndex("temp/data.db")
|
|
307
|
+
|
|
308
|
+
table.insert(1, "loft_1", DateTime.now(), None)
|
|
309
|
+
table
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
<table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">ReservationIndex</caption><thead><tr><td style="border: 1px solid #555; text-align: center;">user_id</td><td style="border: 1px solid #555; text-align: center;">room_id</td><td style="border: 1px solid #555; text-align: center;">time_at</td><td style="border: 1px solid #555; text-align: center;">user_name</td></tr></thead><tbody><tr><td style="border: 1px solid #000; text-align: center;">1</td><td style="border: 1px solid #000; text-align: center;">loft_1</td><td style="border: 1px solid #000; text-align: center;">2026-05-29 21:29:00</td><td style="border: 1px solid #000; text-align: center;">None</td></tr></tbody></table>
|
|
313
|
+
|
|
270
314
|
## Csv Converter
|
|
271
315
|
|
|
272
316
|
```py
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
- [Delete Query](#delete-query)
|
|
14
14
|
- [Transaction](#transaction)
|
|
15
15
|
- [Get Item](#get-item)
|
|
16
|
+
- [Custom Types](#custom-types)
|
|
16
17
|
- [Csv Converter](#csv-converter)
|
|
17
18
|
- [Full Documentation](#full-documentation)
|
|
18
19
|
|
|
@@ -32,17 +33,21 @@ Sql-Engine implements Jupyter integration and dynamic schema building. You can e
|
|
|
32
33
|
```py
|
|
33
34
|
from sqlengine import schema
|
|
34
35
|
|
|
35
|
-
table = schema.table_from_database("temp/chinook.db", "
|
|
36
|
+
table = schema.table_from_database("temp/chinook.db", "Album")
|
|
36
37
|
table
|
|
37
38
|
```
|
|
38
39
|
|
|
39
|
-
<table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">
|
|
40
|
+
<table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">Album</caption><thead><tr><td style="border: 1px solid #555; text-align: center;">AlbumId</td><td style="border: 1px solid #555; text-align: center;">Title</td><td style="border: 1px solid #555; text-align: center;">ArtistId</td></tr></thead><tbody><tr><td style="border: 1px solid #000; text-align: center;">1</td><td style="border: 1px solid #000; text-align: center;">For Those About To Rock We Salute You</td><td style="border: 1px solid #000; text-align: center;">1</td></tr><tr><td style="border: 1px solid #000; text-align: center;">2</td><td style="border: 1px solid #000; text-align: center;">Balls to the Wall</td><td style="border: 1px solid #000; text-align: center;">2</td></tr><tr><td style="border: 1px solid #000; text-align: center;">3</td><td style="border: 1px solid #000; text-align: center;">Restless and Wild</td><td style="border: 1px solid #000; text-align: center;">2</td></tr><tr><td style="border: 1px solid #000; text-align: center;">4</td><td style="border: 1px solid #000; text-align: center;">Let There Be Rock</td><td style="border: 1px solid #000; text-align: center;">1</td></tr><tr><td style="border: 1px solid #000; text-align: center;">5</td><td style="border: 1px solid #000; text-align: center;">Big Ones</td><td style="border: 1px solid #000; text-align: center;">3</td></tr><tr><td style="border: 1px solid #000; text-align: center;">6</td><td style="border: 1px solid #000; text-align: center;">Jagged Little Pill</td><td style="border: 1px solid #000; text-align: center;">4</td></tr><tr><td style="border: 1px solid #000; text-align: center;">7</td><td style="border: 1px solid #000; text-align: center;">Facelift</td><td style="border: 1px solid #000; text-align: center;">5</td></tr><tr><td style="border: 1px solid #000; text-align: center;">8</td><td style="border: 1px solid #000; text-align: center;">Warner 25 Anos</td><td style="border: 1px solid #000; text-align: center;">6</td></tr><tr><td style="border: 1px solid #000; text-align: center;">9</td><td style="border: 1px solid #000; text-align: center;">Plays Metallica By Four Cellos</td><td style="border: 1px solid #000; text-align: center;">7</td></tr><tr><td style="border: 1px solid #000; text-align: center;">10</td><td style="border: 1px solid #000; text-align: center;">Audioslave</td><td style="border: 1px solid #000; text-align: center;">8</td></tr><tr><td colspan="3" style="text-align:center;color:#888;font-style:italic;padding:8px;">... more rows ...</td></tr></tbody></table>
|
|
41
|
+
|
|
42
|
+
Note that GitHub's html rendering is simplified.
|
|
40
43
|
|
|
41
44
|
---
|
|
42
45
|
|
|
43
46
|
You can preview select statements before fetching data to your variables.
|
|
44
47
|
|
|
45
48
|
```py
|
|
49
|
+
table = schema.table_from_database("temp/chinook.db", "Invoice")
|
|
50
|
+
|
|
46
51
|
table.select("InvoiceId", "CustomerId", "BillingAddress", "BillingCountry", "Total")\
|
|
47
52
|
.where\
|
|
48
53
|
.gte("Total", 2.0)\
|
|
@@ -56,7 +61,7 @@ table.select("InvoiceId", "CustomerId", "BillingAddress", "BillingCountry", "Tot
|
|
|
56
61
|
|
|
57
62
|
## Purpose
|
|
58
63
|
|
|
59
|
-
It's a tiny little modern ORM that lets you prototype your databases locally with great flexibility. Also, it can be used in production apps to store and retrieve data, because all select, update, delete queries are parametrized. But it does not restrict you from using your own queries which might not be paramerized with methods like `select.custom()` and `where.custom()`.
|
|
64
|
+
It's a tiny little modern ORM-like that lets you prototype your databases locally with great flexibility. Also, it can be used in production apps to store and retrieve data, because all select, update, delete queries are parametrized. But it does not restrict you from using your own queries which might not be paramerized with methods like `select.custom()` and `where.custom()`.
|
|
60
65
|
|
|
61
66
|
And last (but not least) is data inspection. If you need to quickly inspect existing .db file but don't want to install yet another heavy ORM with a lot of unused dependencies, you might look into Sql-Engine, as it uses only native python modules.
|
|
62
67
|
|
|
@@ -107,7 +112,7 @@ __primary__ : list[str]
|
|
|
107
112
|
More details at [Declaration](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#table-declaration).
|
|
108
113
|
|
|
109
114
|
```py
|
|
110
|
-
from sqlengine import SqlTableMixin
|
|
115
|
+
from sqlengine import SqlTableMixin, Primary
|
|
111
116
|
|
|
112
117
|
# Helper constants for column names
|
|
113
118
|
ID = "ID"
|
|
@@ -118,9 +123,10 @@ Salary = "Salary"
|
|
|
118
123
|
|
|
119
124
|
class Employees(SqlTableMixin):
|
|
120
125
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
126
|
+
ID : Primary[int]
|
|
127
|
+
Name : str
|
|
128
|
+
Occupation : str
|
|
129
|
+
Salary : float
|
|
124
130
|
|
|
125
131
|
# You may overwrite your insert methods for type consistency
|
|
126
132
|
def insert(self, id : int, name : str, occupation : str, salary : float) -> None:
|
|
@@ -137,8 +143,9 @@ More details at [Instantiation](https://github.com/suffermuffin/SQL-Engine/blob/
|
|
|
137
143
|
```py
|
|
138
144
|
# Create an instance of the table class
|
|
139
145
|
# with provided path to create or connect to
|
|
146
|
+
# `force_drop=True` to overwrite existing table if exists
|
|
140
147
|
|
|
141
|
-
table = Employees("temp/data.db")
|
|
148
|
+
table = Employees("temp/data.db", force_drop=True)
|
|
142
149
|
```
|
|
143
150
|
|
|
144
151
|
## Row insertion
|
|
@@ -255,6 +262,43 @@ table[4:10:2]
|
|
|
255
262
|
# (8, 'Frank White', 'Quality Assurance', 53000.0)]
|
|
256
263
|
```
|
|
257
264
|
|
|
265
|
+
## Custom Types
|
|
266
|
+
|
|
267
|
+
More details at [Custom Types](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/custom_types.md).
|
|
268
|
+
|
|
269
|
+
```py
|
|
270
|
+
from datetime import datetime
|
|
271
|
+
from sqlengine import SqlTableMixin, Primary
|
|
272
|
+
|
|
273
|
+
class DateTime(datetime):
|
|
274
|
+
|
|
275
|
+
def to_sql(self) -> str:
|
|
276
|
+
return self.strftime("%Y-%m-%d %H:%M")
|
|
277
|
+
|
|
278
|
+
@classmethod
|
|
279
|
+
def from_sql(cls, sql : bytes):
|
|
280
|
+
return cls.fromisoformat(sql.decode('utf-8'))
|
|
281
|
+
|
|
282
|
+
def __repr__(self):
|
|
283
|
+
return f"DateTime({self.time})"
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class ReservationIndex(SqlTableMixin):
|
|
287
|
+
|
|
288
|
+
user_id : Primary[int]
|
|
289
|
+
room_id : Primary[str]
|
|
290
|
+
time_at : Primary[DateTime]
|
|
291
|
+
user_name : str | None
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
table = ReservationIndex("temp/data.db")
|
|
295
|
+
|
|
296
|
+
table.insert(1, "loft_1", DateTime.now(), None)
|
|
297
|
+
table
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
<table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">ReservationIndex</caption><thead><tr><td style="border: 1px solid #555; text-align: center;">user_id</td><td style="border: 1px solid #555; text-align: center;">room_id</td><td style="border: 1px solid #555; text-align: center;">time_at</td><td style="border: 1px solid #555; text-align: center;">user_name</td></tr></thead><tbody><tr><td style="border: 1px solid #000; text-align: center;">1</td><td style="border: 1px solid #000; text-align: center;">loft_1</td><td style="border: 1px solid #000; text-align: center;">2026-05-29 21:29:00</td><td style="border: 1px solid #000; text-align: center;">None</td></tr></tbody></table>
|
|
301
|
+
|
|
258
302
|
## Csv Converter
|
|
259
303
|
|
|
260
304
|
```py
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from typing import overload, Literal, Sequence
|
|
6
|
+
from contextlib import contextmanager
|
|
7
|
+
|
|
8
|
+
from .types import SqlValue, SqlRow
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger("sqlengine")
|
|
12
|
+
logger.setLevel(os.getenv("SQL_ENGINE_LOG_LEVEL", "WARNING").upper())
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ConnectionManager:
|
|
16
|
+
|
|
17
|
+
_trans : sqlite3.Connection
|
|
18
|
+
_trans_cursor : sqlite3.Cursor
|
|
19
|
+
|
|
20
|
+
def __init__(self, database : str, **connection_params) -> None:
|
|
21
|
+
|
|
22
|
+
self.database = database
|
|
23
|
+
self.connection_params = connection_params
|
|
24
|
+
|
|
25
|
+
self._is_managed_transaction = False
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@overload
|
|
29
|
+
def _fetch(self, query : str, args : SqlRow,
|
|
30
|
+
method : Literal["fetchone"]) -> SqlRow: ...
|
|
31
|
+
@overload
|
|
32
|
+
def _fetch(self, query : str, args : SqlRow,
|
|
33
|
+
method : Literal["fetchall"]) -> list[SqlRow]: ...
|
|
34
|
+
|
|
35
|
+
def _fetch(
|
|
36
|
+
self,
|
|
37
|
+
query : str,
|
|
38
|
+
args : SqlRow = (),
|
|
39
|
+
method : Literal["fetchone", "fetchall"] = "fetchall"
|
|
40
|
+
) -> SqlRow | list[SqlRow]:
|
|
41
|
+
|
|
42
|
+
logger.debug(f"{self.database}: {query} {args}")
|
|
43
|
+
|
|
44
|
+
if self.in_transaction():
|
|
45
|
+
self._trans_cursor.execute(query, args)
|
|
46
|
+
return getattr(self._trans_cursor, method)()
|
|
47
|
+
|
|
48
|
+
with self.connect() as conn:
|
|
49
|
+
cursor = conn.cursor()
|
|
50
|
+
cursor.execute(query, args)
|
|
51
|
+
return getattr(cursor, method)()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@overload
|
|
55
|
+
def _execute(self, query : str, args : tuple[SqlValue, ...], method : Literal["execute"]) -> None: ...
|
|
56
|
+
@overload
|
|
57
|
+
def _execute(self, query : str, args : Sequence[SqlRow], method : Literal["executemany"]) -> None: ...
|
|
58
|
+
|
|
59
|
+
def _execute(
|
|
60
|
+
self,
|
|
61
|
+
query : str,
|
|
62
|
+
args : tuple[SqlValue, ...] | Sequence[SqlRow] = (),
|
|
63
|
+
method : Literal["execute", "executemany"] = "execute"
|
|
64
|
+
) -> None:
|
|
65
|
+
"""
|
|
66
|
+
Shortcut to connect() -> execute[<many>]() -> commit() for single operations.
|
|
67
|
+
Can be used in transaction using `transaction()` manager.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
query (str): SQL query to execute on SQLite3 DB
|
|
71
|
+
*args (Any): Arguments to the execution
|
|
72
|
+
method (str): "execute" or "executemany"
|
|
73
|
+
"""
|
|
74
|
+
logger.debug(f"{self.database}: {query} {args}")
|
|
75
|
+
|
|
76
|
+
if self.in_transaction():
|
|
77
|
+
getattr(self._trans_cursor, method)(query, args)
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
with self.connect() as conn:
|
|
81
|
+
cursor = conn.cursor()
|
|
82
|
+
getattr(cursor, method)(query, args)
|
|
83
|
+
conn.commit()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def execute(self, query : str, *args : SqlValue) -> None:
|
|
87
|
+
"""
|
|
88
|
+
Shortcut to connect() -> execute() -> commit() for single operations.
|
|
89
|
+
Can be used in transaction using `transaction()` manager.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
query (str): SQL query to execute on SQLite3 DB
|
|
93
|
+
*args (tuple[SqlValue, ...]): Arguments to the execution
|
|
94
|
+
"""
|
|
95
|
+
return self._execute(query, args, method="execute")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def executemany(self, query : str, args : Sequence[SqlRow]) -> None:
|
|
99
|
+
"""
|
|
100
|
+
Shortcut to connect() -> executemany() -> commit() for single operations.
|
|
101
|
+
Can be used in transaction using `transaction()` manager.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
query (str): SQL query to execute on SQLite3 DB
|
|
105
|
+
args (list[tuple[SqlValue, ...]]): Arguments to the execution
|
|
106
|
+
"""
|
|
107
|
+
return self._execute(query, args, method="executemany")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def fetchone(self, query : str, *args : SqlValue) -> SqlRow:
|
|
111
|
+
"""
|
|
112
|
+
Fetch first row based on `query`
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
query (str): SQL query
|
|
116
|
+
*args (tuple[SqlValue, ...]): Arguments to the execution
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
row (SqlRow): Single row
|
|
120
|
+
"""
|
|
121
|
+
return self._fetch(query, args, method="fetchone")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def fetchmany(self, query : str, *args : SqlValue, size : int = 1) -> list[SqlRow]:
|
|
125
|
+
"""
|
|
126
|
+
Fetch first `size` rows based on `query`
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
query (str): SQL query
|
|
130
|
+
*args (tuple[SqlValue, ...]): Arguments to the execution
|
|
131
|
+
size (str): Number of rows to return
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
rows (list[SqlRow]): list of `size` rows
|
|
135
|
+
"""
|
|
136
|
+
logger.debug(f"{self.database}: {query} {args}")
|
|
137
|
+
|
|
138
|
+
if self.in_transaction():
|
|
139
|
+
self._trans_cursor.execute(query, args)
|
|
140
|
+
return self._trans_cursor.fetchmany(size)
|
|
141
|
+
|
|
142
|
+
with self.connect() as conn:
|
|
143
|
+
cursor = conn.cursor()
|
|
144
|
+
cursor.execute(query, args)
|
|
145
|
+
return cursor.fetchmany(size)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def fetchall(self, query : str, *args : SqlValue) -> list[SqlRow]:
|
|
149
|
+
"""
|
|
150
|
+
Fetch all rows based on `query`
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
query (str): SQL query
|
|
154
|
+
*args (tuple[SqlValue, ...]): Arguments to the execution
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
rows (list[SqlRow]): list of rows
|
|
158
|
+
"""
|
|
159
|
+
return self._fetch(query, args, method="fetchall")
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def in_transaction(self) -> bool:
|
|
163
|
+
""" Returns True if instance is in transaction """
|
|
164
|
+
return hasattr(self, "_trans") and hasattr(self, "_trans_cursor")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def connect(self) -> sqlite3.Connection:
|
|
168
|
+
""" Shortcut to sqlite3 connection context manager """
|
|
169
|
+
return sqlite3.connect(self.database, **self.connection_params)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def open(self) -> None:
|
|
173
|
+
""" Opens unmanaged transaction """
|
|
174
|
+
if self.in_transaction():
|
|
175
|
+
raise RuntimeError("Can't re-open existing connection")
|
|
176
|
+
|
|
177
|
+
self._trans = self.connect()
|
|
178
|
+
self._trans_cursor = self._trans.cursor()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def close(self) -> None:
|
|
182
|
+
""" Closes unmanaged transaction """
|
|
183
|
+
if not self.in_transaction():
|
|
184
|
+
return
|
|
185
|
+
|
|
186
|
+
if self._is_managed_transaction:
|
|
187
|
+
raise RuntimeError("Can't manually close managed transaction")
|
|
188
|
+
|
|
189
|
+
self._trans_cursor.close()
|
|
190
|
+
self._trans.close()
|
|
191
|
+
del(self._trans_cursor)
|
|
192
|
+
del(self._trans)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def commit(self) -> None:
|
|
196
|
+
if not self.in_transaction():
|
|
197
|
+
raise RuntimeError("Can't commit outside transaction mode")
|
|
198
|
+
|
|
199
|
+
self._trans.commit()
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def rollback(self) -> None:
|
|
203
|
+
if not self.in_transaction():
|
|
204
|
+
raise RuntimeError("Can't rollback outside transaction mode")
|
|
205
|
+
|
|
206
|
+
self._trans.rollback()
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
@contextmanager
|
|
210
|
+
def transaction(self, autocommit : bool = True):
|
|
211
|
+
"""
|
|
212
|
+
Creates context manager to use class methods in transaction
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
autocommit (bool): If `True`, will commit changes at the end of transaction
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
self.open()
|
|
219
|
+
self._is_managed_transaction = True
|
|
220
|
+
logger.debug(f"{self.database}: Transaction started")
|
|
221
|
+
|
|
222
|
+
try:
|
|
223
|
+
yield
|
|
224
|
+
|
|
225
|
+
except Exception as e:
|
|
226
|
+
logger.error(f"{self.database}: Error while in transaction: {e}")
|
|
227
|
+
logger.debug(e, exc_info=True)
|
|
228
|
+
self._trans.rollback()
|
|
229
|
+
raise e
|
|
230
|
+
|
|
231
|
+
else:
|
|
232
|
+
if autocommit:
|
|
233
|
+
self._trans.commit()
|
|
234
|
+
|
|
235
|
+
finally:
|
|
236
|
+
self._is_managed_transaction = False
|
|
237
|
+
self.close()
|
|
238
|
+
logger.debug(f"{self.database}: Transaction finished")
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def tx_conn(self) -> sqlite3.Connection:
|
|
243
|
+
""" Gives access to connection while in transaction """
|
|
244
|
+
if not self.in_transaction():
|
|
245
|
+
raise RuntimeError("`tx_conn` is not available outside the transaction mode")
|
|
246
|
+
return self._trans
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def tx_cursor(self) -> sqlite3.Cursor:
|
|
251
|
+
""" Gives access to connection cursor while in transaction """
|
|
252
|
+
if not self.in_transaction():
|
|
253
|
+
raise RuntimeError("`tx_cursor` is not available outside the transaction mode")
|
|
254
|
+
return self._trans_cursor
|
|
255
|
+
|
{sqlengine_lite-2.1.1/src/sqlengine/utils → sqlengine_lite-2.2.0/src/sqlengine/core}/repr.py
RENAMED
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
1
|
from html import escape
|
|
3
|
-
from typing import Sequence
|
|
4
|
-
|
|
5
|
-
import csv
|
|
6
|
-
|
|
7
|
-
if TYPE_CHECKING:
|
|
8
|
-
from .statements import Select, Where
|
|
9
|
-
from ..sqltable import SqlTableMixin
|
|
2
|
+
from typing import Sequence
|
|
10
3
|
|
|
11
4
|
from .types import SqlRow
|
|
12
5
|
|
|
@@ -57,25 +50,3 @@ def to_html(tablename : str, columns : list[str], repr_rows : Sequence[SqlRow],
|
|
|
57
50
|
|
|
58
51
|
return "".join(html)
|
|
59
52
|
|
|
60
|
-
|
|
61
|
-
def to_csv(builder : Select | Where[Select] | SqlTableMixin, path : str) -> None:
|
|
62
|
-
|
|
63
|
-
from ..sqltable import SqlTableMixin
|
|
64
|
-
from .statements import Where
|
|
65
|
-
|
|
66
|
-
match builder:
|
|
67
|
-
case Where():
|
|
68
|
-
builder = builder.then
|
|
69
|
-
case SqlTableMixin():
|
|
70
|
-
builder = builder.select
|
|
71
|
-
|
|
72
|
-
if builder._aggregate:
|
|
73
|
-
raise AssertionError("Aggregated queries are not supported")
|
|
74
|
-
|
|
75
|
-
columns = builder._table.columns if len(builder._columns) == 0 or "*" in builder._columns else builder._columns
|
|
76
|
-
repr_rows = builder.fetchall()
|
|
77
|
-
|
|
78
|
-
with open(path, 'w', newline='') as file:
|
|
79
|
-
writer = csv.writer(file)
|
|
80
|
-
writer.writerow(columns)
|
|
81
|
-
writer.writerows(repr_rows)
|
{sqlengine_lite-2.1.1/src/sqlengine/utils → sqlengine_lite-2.2.0/src/sqlengine/core}/sqlgen.py
RENAMED
|
@@ -4,9 +4,7 @@ from typing import Sequence
|
|
|
4
4
|
def format_list(items : Sequence | set, brackets : bool = True) -> str:
|
|
5
5
|
""" Formats list into `(item1, item2, ...)` format """
|
|
6
6
|
items_str = ', '.join([str(i) for i in items])
|
|
7
|
-
if
|
|
8
|
-
return items_str
|
|
9
|
-
return f'({items_str})'
|
|
7
|
+
return f'({items_str})' if brackets else items_str
|
|
10
8
|
|
|
11
9
|
|
|
12
10
|
def create_table(
|
|
@@ -40,7 +38,7 @@ def values_placeholder(n_values : int) -> str:
|
|
|
40
38
|
def bulk_placeholder(n_values : int, n_rows : int) -> str:
|
|
41
39
|
""" Creates placeholders `(?, ?, ..), (?, ?, ..), ...` for each row """
|
|
42
40
|
place_holder = values_placeholder(n_values)
|
|
43
|
-
return
|
|
41
|
+
return format_list([place_holder]*n_rows, False)
|
|
44
42
|
|
|
45
43
|
|
|
46
44
|
def insert(tablename : str, columns : list[str], values : str) -> str:
|