sqlengine-lite 2.1.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 suffermuffin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,287 @@
1
+ Metadata-Version: 2.4
2
+ Name: sqlengine-lite
3
+ Version: 2.1.0
4
+ Summary: Cute sqlite3 wrapper for sql tables
5
+ Project-URL: Homepage, https://github.com/suffermuffin/SQL-Engine
6
+ Project-URL: Repository, https://github.com/suffermuffin/SQL-Engine.git
7
+ Project-URL: Documentation, https://github.com/suffermuffin/SQL-Engine/blob/main/docs/index.md
8
+ Requires-Python: >=3.12
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Dynamic: license-file
12
+
13
+ - [Sql-Engine](#sql-engine)
14
+ - [Features](#features)
15
+ - [Purpose](#purpose)
16
+ - [Installation](#installation)
17
+ - [Env](#env)
18
+ - [Quick Start](#quick-start)
19
+ - [Table Declaration](#table-declaration)
20
+ - [Instantiation](#instantiation)
21
+ - [Row insertion](#row-insertion)
22
+ - [Jupyter view](#jupyter-view)
23
+ - [Select Query](#select-query)
24
+ - [Update Query](#update-query)
25
+ - [Delete Query](#delete-query)
26
+ - [Transaction](#transaction)
27
+ - [Get Item](#get-item)
28
+ - [Csv Converter](#csv-converter)
29
+ - [Full Documentation](#full-documentation)
30
+
31
+
32
+ # Sql-Engine
33
+
34
+ My Sql-Engine is a cute little wrapper for `sqlite3` table manipulations without any third party dependencies.
35
+
36
+
37
+ ## Features
38
+
39
+ Abstracts SQL queries into tiny little methods like, `insert`, `insert_many`, `upsert`, and not so little and tiny query builders a-la `select`, `delete`, `update`, etc. Sql-Engine also provides bulk insertion and transaction methods, like `insert_many` and `select.fetchmany_iterator`. Methods can be executed in transaction mode thanks to `transaction` context manager.
40
+
41
+
42
+ Sql-Engine implements Jupyter integration and dynamic schema building. You can easily instantiate existing database table and view it in cute little html representation.
43
+
44
+ ```py
45
+ from sqlengine import schema
46
+
47
+ table = schema.table_from_database("temp/chinook.db", "Invoice")
48
+ table
49
+ ```
50
+
51
+ <table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">Invoice</caption><thead><tr><td style="border: 1px solid #555; text-align: center;">InvoiceId</td><td style="border: 1px solid #555; text-align: center;">CustomerId</td><td style="border: 1px solid #555; text-align: center;">InvoiceDate</td><td style="border: 1px solid #555; text-align: center;">BillingAddress</td><td style="border: 1px solid #555; text-align: center;">BillingCity</td><td style="border: 1px solid #555; text-align: center;">BillingState</td><td style="border: 1px solid #555; text-align: center;">BillingCountry</td><td style="border: 1px solid #555; text-align: center;">BillingPostalCode</td><td style="border: 1px solid #555; text-align: center;">Total</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;">2</td><td style="border: 1px solid #000; text-align: center;">2021-01-01 00:00:00</td><td style="border: 1px solid #000; text-align: center;">Theodor-Heuss-Straße 34</td><td style="border: 1px solid #000; text-align: center;">Stuttgart</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">Germany</td><td style="border: 1px solid #000; text-align: center;">70174</td><td style="border: 1px solid #000; text-align: center;">1.98</td></tr><tr><td style="border: 1px solid #000; text-align: center;">2</td><td style="border: 1px solid #000; text-align: center;">4</td><td style="border: 1px solid #000; text-align: center;">2021-01-02 00:00:00</td><td style="border: 1px solid #000; text-align: center;">Ullevålsveien 14</td><td style="border: 1px solid #000; text-align: center;">Oslo</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">Norway</td><td style="border: 1px solid #000; text-align: center;">0171</td><td style="border: 1px solid #000; text-align: center;">3.96</td></tr><tr><td style="border: 1px solid #000; text-align: center;">3</td><td style="border: 1px solid #000; text-align: center;">8</td><td style="border: 1px solid #000; text-align: center;">2021-01-03 00:00:00</td><td style="border: 1px solid #000; text-align: center;">Grétrystraat 63</td><td style="border: 1px solid #000; text-align: center;">Brussels</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">Belgium</td><td style="border: 1px solid #000; text-align: center;">1000</td><td style="border: 1px solid #000; text-align: center;">5.94</td></tr><tr><td style="border: 1px solid #000; text-align: center;">4</td><td style="border: 1px solid #000; text-align: center;">14</td><td style="border: 1px solid #000; text-align: center;">2021-01-06 00:00:00</td><td style="border: 1px solid #000; text-align: center;">8210 111 ST NW</td><td style="border: 1px solid #000; text-align: center;">Edmonton</td><td style="border: 1px solid #000; text-align: center;">AB</td><td style="border: 1px solid #000; text-align: center;">Canada</td><td style="border: 1px solid #000; text-align: center;">T6G 2C7</td><td style="border: 1px solid #000; text-align: center;">8.91</td></tr><tr><td style="border: 1px solid #000; text-align: center;">5</td><td style="border: 1px solid #000; text-align: center;">23</td><td style="border: 1px solid #000; text-align: center;">2021-01-11 00:00:00</td><td style="border: 1px solid #000; text-align: center;">69 Salem Street</td><td style="border: 1px solid #000; text-align: center;">Boston</td><td style="border: 1px solid #000; text-align: center;">MA</td><td style="border: 1px solid #000; text-align: center;">USA</td><td style="border: 1px solid #000; text-align: center;">2113</td><td style="border: 1px solid #000; text-align: center;">13.86</td></tr><tr><td style="border: 1px solid #000; text-align: center;">6</td><td style="border: 1px solid #000; text-align: center;">37</td><td style="border: 1px solid #000; text-align: center;">2021-01-19 00:00:00</td><td style="border: 1px solid #000; text-align: center;">Berger Straße 10</td><td style="border: 1px solid #000; text-align: center;">Frankfurt</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">Germany</td><td style="border: 1px solid #000; text-align: center;">60316</td><td style="border: 1px solid #000; text-align: center;">0.99</td></tr><tr><td style="border: 1px solid #000; text-align: center;">7</td><td style="border: 1px solid #000; text-align: center;">38</td><td style="border: 1px solid #000; text-align: center;">2021-02-01 00:00:00</td><td style="border: 1px solid #000; text-align: center;">Barbarossastraße 19</td><td style="border: 1px solid #000; text-align: center;">Berlin</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">Germany</td><td style="border: 1px solid #000; text-align: center;">10779</td><td style="border: 1px solid #000; text-align: center;">1.98</td></tr><tr><td style="border: 1px solid #000; text-align: center;">8</td><td style="border: 1px solid #000; text-align: center;">40</td><td style="border: 1px solid #000; text-align: center;">2021-02-01 00:00:00</td><td style="border: 1px solid #000; text-align: center;">8, Rue Hanovre</td><td style="border: 1px solid #000; text-align: center;">Paris</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">France</td><td style="border: 1px solid #000; text-align: center;">75002</td><td style="border: 1px solid #000; text-align: center;">1.98</td></tr><tr><td style="border: 1px solid #000; text-align: center;">9</td><td style="border: 1px solid #000; text-align: center;">42</td><td style="border: 1px solid #000; text-align: center;">2021-02-02 00:00:00</td><td style="border: 1px solid #000; text-align: center;">9, Place Louis Barthou</td><td style="border: 1px solid #000; text-align: center;">Bordeaux</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">France</td><td style="border: 1px solid #000; text-align: center;">33000</td><td style="border: 1px solid #000; text-align: center;">3.96</td></tr><tr><td style="border: 1px solid #000; text-align: center;">10</td><td style="border: 1px solid #000; text-align: center;">46</td><td style="border: 1px solid #000; text-align: center;">2021-02-03 00:00:00</td><td style="border: 1px solid #000; text-align: center;">3 Chatham Street</td><td style="border: 1px solid #000; text-align: center;">Dublin</td><td style="border: 1px solid #000; text-align: center;">Dublin</td><td style="border: 1px solid #000; text-align: center;">Ireland</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">5.94</td></tr><tr><td colspan="9" style="text-align:center;color:#888;font-style:italic;padding:8px;">... more rows ...</td></tr></tbody></table>
52
+
53
+ ---
54
+
55
+ You can preview select statements before fetching data to your variables.
56
+
57
+ ```py
58
+ table.select("InvoiceId", "CustomerId", "BillingAddress", "BillingCountry", "Total")\
59
+ .where\
60
+ .gte("Total", 2.0)\
61
+ .then\
62
+ .order_by("CustomerId")\
63
+ .limit(10)
64
+ ```
65
+
66
+ <table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">Invoice</caption><thead><tr><td style="border: 1px solid #555; text-align: center;">InvoiceId</td><td style="border: 1px solid #555; text-align: center;">CustomerId</td><td style="border: 1px solid #555; text-align: center;">BillingAddress</td><td style="border: 1px solid #555; text-align: center;">BillingCountry</td><td style="border: 1px solid #555; text-align: center;">Total</td></tr></thead><tbody><tr><td style="border: 1px solid #000; text-align: center;">98</td><td style="border: 1px solid #000; text-align: center;">1</td><td style="border: 1px solid #000; text-align: center;">Av. Brigadeiro Faria Lima, 2170</td><td style="border: 1px solid #000; text-align: center;">Brazil</td><td style="border: 1px solid #000; text-align: center;">3.98</td></tr><tr><td style="border: 1px solid #000; text-align: center;">121</td><td style="border: 1px solid #000; text-align: center;">1</td><td style="border: 1px solid #000; text-align: center;">Av. Brigadeiro Faria Lima, 2170</td><td style="border: 1px solid #000; text-align: center;">Brazil</td><td style="border: 1px solid #000; text-align: center;">3.96</td></tr><tr><td style="border: 1px solid #000; text-align: center;">143</td><td style="border: 1px solid #000; text-align: center;">1</td><td style="border: 1px solid #000; text-align: center;">Av. Brigadeiro Faria Lima, 2170</td><td style="border: 1px solid #000; text-align: center;">Brazil</td><td style="border: 1px solid #000; text-align: center;">5.94</td></tr><tr><td style="border: 1px solid #000; text-align: center;">327</td><td style="border: 1px solid #000; text-align: center;">1</td><td style="border: 1px solid #000; text-align: center;">Av. Brigadeiro Faria Lima, 2170</td><td style="border: 1px solid #000; text-align: center;">Brazil</td><td style="border: 1px solid #000; text-align: center;">13.86</td></tr><tr><td style="border: 1px solid #000; text-align: center;">382</td><td style="border: 1px solid #000; text-align: center;">1</td><td style="border: 1px solid #000; text-align: center;">Av. Brigadeiro Faria Lima, 2170</td><td style="border: 1px solid #000; text-align: center;">Brazil</td><td style="border: 1px solid #000; text-align: center;">8.91</td></tr><tr><td style="border: 1px solid #000; text-align: center;">12</td><td style="border: 1px solid #000; text-align: center;">2</td><td style="border: 1px solid #000; text-align: center;">Theodor-Heuss-Straße 34</td><td style="border: 1px solid #000; text-align: center;">Germany</td><td style="border: 1px solid #000; text-align: center;">13.86</td></tr><tr><td style="border: 1px solid #000; text-align: center;">67</td><td style="border: 1px solid #000; text-align: center;">2</td><td style="border: 1px solid #000; text-align: center;">Theodor-Heuss-Straße 34</td><td style="border: 1px solid #000; text-align: center;">Germany</td><td style="border: 1px solid #000; text-align: center;">8.91</td></tr><tr><td style="border: 1px solid #000; text-align: center;">219</td><td style="border: 1px solid #000; text-align: center;">2</td><td style="border: 1px solid #000; text-align: center;">Theodor-Heuss-Straße 34</td><td style="border: 1px solid #000; text-align: center;">Germany</td><td style="border: 1px solid #000; text-align: center;">3.96</td></tr><tr><td style="border: 1px solid #000; text-align: center;">241</td><td style="border: 1px solid #000; text-align: center;">2</td><td style="border: 1px solid #000; text-align: center;">Theodor-Heuss-Straße 34</td><td style="border: 1px solid #000; text-align: center;">Germany</td><td style="border: 1px solid #000; text-align: center;">5.94</td></tr><tr><td style="border: 1px solid #000; text-align: center;">99</td><td style="border: 1px solid #000; text-align: center;">3</td><td style="border: 1px solid #000; text-align: center;">1498 rue Bélanger</td><td style="border: 1px solid #000; text-align: center;">Canada</td><td style="border: 1px solid #000; text-align: center;">3.98</td></tr></tbody></table>
67
+
68
+
69
+ ## Purpose
70
+
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()`.
72
+
73
+ 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
+
75
+
76
+ ## Installation
77
+
78
+ To install `sqlengine`, you can use `pip`:
79
+
80
+ ```sh
81
+ pip install sqlengine-lite
82
+ ```
83
+
84
+ ## Env
85
+
86
+ You may set environment variable for logging. By default it's `WARNING`.
87
+
88
+ ```console
89
+ SQL_ENGINE_LOG_LEVEL=INFO
90
+ ```
91
+
92
+ # Quick Start
93
+
94
+ All you have to do to create your own cute little table is to [inherit](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#class-declaration) `SqlTableMixin` class or to create your own [schema](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#schema-declaration) and declare desired properties of your table's columns. They are:
95
+
96
+
97
+ _Name of the table that will be used in queries. If omitted in inherited class declaration, then it will take the class name._
98
+ ```py
99
+ __tablename__ : Optional[str]
100
+ ```
101
+
102
+ _Column names of the table_
103
+ ```py
104
+ __columns__ : list[str]
105
+ ```
106
+
107
+ _Column types of the table_
108
+ ```py
109
+ __types__ : list[SqlType | str]
110
+ ```
111
+
112
+ _List of primary keys_
113
+ ```py
114
+ __primary__ : list[str]
115
+ ```
116
+
117
+ ## Table Declaration
118
+
119
+ More details at [Declaration](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#table-declaration).
120
+
121
+ ```py
122
+ from sqlengine import SqlTableMixin
123
+
124
+ # Helper constants for column names
125
+ ID = "ID"
126
+ Name = "Name"
127
+ Occupation = "Occupation"
128
+ Salary = "Salary"
129
+
130
+
131
+ class Employees(SqlTableMixin):
132
+
133
+ __columns__ = [ID, Name, Occupation, Salary]
134
+ __types__ = [int, str, str, float]
135
+ __primary__ = [ID]
136
+
137
+ # You may overwrite your insert methods for type consistency
138
+ def insert(self, id : int, name : str, occupation : str, salary : float) -> None:
139
+ return super().insert(id, name, occupation, salary)
140
+
141
+ def upsert(self, id : int, name : str, occupation : str, salary : float) -> None:
142
+ return super().upsert(id, name, occupation, salary)
143
+ ```
144
+
145
+ ## Instantiation
146
+
147
+ More details at [Instantiation](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#table-instantiation).
148
+
149
+ ```py
150
+ # Create an instance of the table class
151
+ # with provided path to create or connect to
152
+
153
+ table = Employees("temp/data.db")
154
+ ```
155
+
156
+ ## Row insertion
157
+
158
+ ```py
159
+ # Insert one row
160
+
161
+ table.insert(1, "John Doe", "Software Engineer", 75000.0)
162
+ ```
163
+
164
+ ```py
165
+ # Bulk insert multiple rows
166
+
167
+ employees_data = [
168
+ (2, "Jane Smith", "Data Scientist", 80000.0),
169
+ (3, "Alice Johnson", "Product Manager", 90000.0),
170
+ (4, "Bob Brown", "Project Manager", 78000.0),
171
+ (5, "Charlie Davis", "UI/UX Designer", 65000.0),
172
+ (6, "David Wilson", "DevOps Engineer", 82000.0),
173
+ (7, "Eve Taylor", "Customer Support", 45000.0),
174
+ (8, "Frank White", "Quality Assurance", 53000.0),
175
+ (9, "Grace Hall", "Marketing Manager", 68000.0),
176
+ (10, "Henry Lee", "Technical Writer", 52000.0)
177
+ ]
178
+
179
+ table.insert_many(employees_data)
180
+ ```
181
+
182
+
183
+ ```py
184
+ # Upsert one row
185
+
186
+ table.upsert(1, "Jane Doe", "Data Scientist", 80000.0)
187
+ ```
188
+
189
+ ## Jupyter view
190
+
191
+ ```py
192
+ # Inspect tables in Jupyter Notebook
193
+
194
+ table
195
+ ```
196
+
197
+ <table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">Employees</caption><thead><tr><td style="border: 1px solid #555; text-align: center;">ID</td><td style="border: 1px solid #555; text-align: center;">Name</td><td style="border: 1px solid #555; text-align: center;">Occupation</td><td style="border: 1px solid #555; text-align: center;">Salary</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;">Jane Doe</td><td style="border: 1px solid #000; text-align: center;">Data Scientist</td><td style="border: 1px solid #000; text-align: center;">80000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">2</td><td style="border: 1px solid #000; text-align: center;">Jane Smith</td><td style="border: 1px solid #000; text-align: center;">Data Scientist</td><td style="border: 1px solid #000; text-align: center;">80000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">3</td><td style="border: 1px solid #000; text-align: center;">Alice Johnson</td><td style="border: 1px solid #000; text-align: center;">Product Manager</td><td style="border: 1px solid #000; text-align: center;">90000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">4</td><td style="border: 1px solid #000; text-align: center;">Bob Brown</td><td style="border: 1px solid #000; text-align: center;">Project Manager</td><td style="border: 1px solid #000; text-align: center;">78000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">5</td><td style="border: 1px solid #000; text-align: center;">Charlie Davis</td><td style="border: 1px solid #000; text-align: center;">UI/UX Designer</td><td style="border: 1px solid #000; text-align: center;">65000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">6</td><td style="border: 1px solid #000; text-align: center;">David Wilson</td><td style="border: 1px solid #000; text-align: center;">DevOps Engineer</td><td style="border: 1px solid #000; text-align: center;">82000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">7</td><td style="border: 1px solid #000; text-align: center;">Eve Taylor</td><td style="border: 1px solid #000; text-align: center;">Customer Support</td><td style="border: 1px solid #000; text-align: center;">45000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">8</td><td style="border: 1px solid #000; text-align: center;">Frank White</td><td style="border: 1px solid #000; text-align: center;">Quality Assurance</td><td style="border: 1px solid #000; text-align: center;">53000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">9</td><td style="border: 1px solid #000; text-align: center;">Grace Hall</td><td style="border: 1px solid #000; text-align: center;">Marketing Manager</td><td style="border: 1px solid #000; text-align: center;">68000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">10</td><td style="border: 1px solid #000; text-align: center;">Henry Lee</td><td style="border: 1px solid #000; text-align: center;">Technical Writer</td><td style="border: 1px solid #000; text-align: center;">52000.0</td></tr></tbody></table>
198
+
199
+ ## Select Query
200
+
201
+ More details at [Statements](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/statements.md).
202
+
203
+ ```py
204
+ # Query select and fetch
205
+
206
+ table.select.where.between(ID, 3, 5).then.fetchall()
207
+
208
+ # ->
209
+ # [(3, 'Alice Johnson', 'Product Manager', 90000.0),
210
+ # (4, 'Bob Brown', 'Project Manager', 78000.0),
211
+ # (5, 'Charlie Davis', 'UI/UX Designer', 65000.0)]
212
+ ```
213
+
214
+ ```py
215
+ # Inspect query select in Jupyter
216
+
217
+ table.select(Name, Salary).where.lt(Salary, 70_000)
218
+ ```
219
+
220
+ <table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">Employees</caption><thead><tr><td style="border: 1px solid #555; text-align: center;">Name</td><td style="border: 1px solid #555; text-align: center;">Salary</td></tr></thead><tbody><tr><td style="border: 1px solid #000; text-align: center;">Charlie Davis</td><td style="border: 1px solid #000; text-align: center;">65000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">Eve Taylor</td><td style="border: 1px solid #000; text-align: center;">45000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">Frank White</td><td style="border: 1px solid #000; text-align: center;">53000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">Grace Hall</td><td style="border: 1px solid #000; text-align: center;">68000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">Henry Lee</td><td style="border: 1px solid #000; text-align: center;">52000.0</td></tr></tbody></table>
221
+
222
+ ## Update Query
223
+
224
+ ```py
225
+ # equal to ...update.set(Salary, 50_000)...
226
+ table.update(Salary, 50_000).where.eq(Name, "Eve Taylor").then.execute()
227
+ ```
228
+
229
+ ## Delete Query
230
+
231
+ ```py
232
+ table.delete.where.eq(ID, 5).then.execute()
233
+ ```
234
+
235
+ ## Transaction
236
+
237
+ More details at [Transaction](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/transactions.md).
238
+
239
+ ```py
240
+ # Operate within a transaction
241
+
242
+ with table.transaction():
243
+ for row in employees_data:
244
+ table.upsert(*row)
245
+ ```
246
+
247
+ ## Get Item
248
+
249
+ More details at [Syntax Sugar](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/syntax_sugar.md).
250
+
251
+ ```py
252
+ # Fetch row by primary key
253
+
254
+ table[9]
255
+
256
+ # -> (9, 'Grace Hall', 'Marketing Manager', 68000.0)
257
+ ```
258
+
259
+ ```py
260
+ # Fetch slice by integer primary key
261
+
262
+ table[4:10:2]
263
+
264
+ # ->
265
+ # [(4, 'Bob Brown', 'Project Manager', 78000.0),
266
+ # (6, 'David Wilson', 'DevOps Engineer', 82000.0),
267
+ # (8, 'Frank White', 'Quality Assurance', 53000.0)]
268
+ ```
269
+
270
+ ## Csv Converter
271
+
272
+ ```py
273
+ # Save table to csv
274
+ from sqlengine.utils import to_csv
275
+
276
+ to_csv(table, "temp/table.csv")
277
+ ```
278
+
279
+ ```py
280
+ # Save query result to csv
281
+
282
+ to_csv(table.select.where.gt(Salary, 70_000), "temp/query.csv")
283
+ ```
284
+
285
+ # Full Documentation
286
+
287
+ For detailed usage, API reference, and advanced examples, see the [full documentation](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/index.md).
@@ -0,0 +1,275 @@
1
+ - [Sql-Engine](#sql-engine)
2
+ - [Features](#features)
3
+ - [Purpose](#purpose)
4
+ - [Installation](#installation)
5
+ - [Env](#env)
6
+ - [Quick Start](#quick-start)
7
+ - [Table Declaration](#table-declaration)
8
+ - [Instantiation](#instantiation)
9
+ - [Row insertion](#row-insertion)
10
+ - [Jupyter view](#jupyter-view)
11
+ - [Select Query](#select-query)
12
+ - [Update Query](#update-query)
13
+ - [Delete Query](#delete-query)
14
+ - [Transaction](#transaction)
15
+ - [Get Item](#get-item)
16
+ - [Csv Converter](#csv-converter)
17
+ - [Full Documentation](#full-documentation)
18
+
19
+
20
+ # Sql-Engine
21
+
22
+ My Sql-Engine is a cute little wrapper for `sqlite3` table manipulations without any third party dependencies.
23
+
24
+
25
+ ## Features
26
+
27
+ Abstracts SQL queries into tiny little methods like, `insert`, `insert_many`, `upsert`, and not so little and tiny query builders a-la `select`, `delete`, `update`, etc. Sql-Engine also provides bulk insertion and transaction methods, like `insert_many` and `select.fetchmany_iterator`. Methods can be executed in transaction mode thanks to `transaction` context manager.
28
+
29
+
30
+ Sql-Engine implements Jupyter integration and dynamic schema building. You can easily instantiate existing database table and view it in cute little html representation.
31
+
32
+ ```py
33
+ from sqlengine import schema
34
+
35
+ table = schema.table_from_database("temp/chinook.db", "Invoice")
36
+ table
37
+ ```
38
+
39
+ <table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">Invoice</caption><thead><tr><td style="border: 1px solid #555; text-align: center;">InvoiceId</td><td style="border: 1px solid #555; text-align: center;">CustomerId</td><td style="border: 1px solid #555; text-align: center;">InvoiceDate</td><td style="border: 1px solid #555; text-align: center;">BillingAddress</td><td style="border: 1px solid #555; text-align: center;">BillingCity</td><td style="border: 1px solid #555; text-align: center;">BillingState</td><td style="border: 1px solid #555; text-align: center;">BillingCountry</td><td style="border: 1px solid #555; text-align: center;">BillingPostalCode</td><td style="border: 1px solid #555; text-align: center;">Total</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;">2</td><td style="border: 1px solid #000; text-align: center;">2021-01-01 00:00:00</td><td style="border: 1px solid #000; text-align: center;">Theodor-Heuss-Straße 34</td><td style="border: 1px solid #000; text-align: center;">Stuttgart</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">Germany</td><td style="border: 1px solid #000; text-align: center;">70174</td><td style="border: 1px solid #000; text-align: center;">1.98</td></tr><tr><td style="border: 1px solid #000; text-align: center;">2</td><td style="border: 1px solid #000; text-align: center;">4</td><td style="border: 1px solid #000; text-align: center;">2021-01-02 00:00:00</td><td style="border: 1px solid #000; text-align: center;">Ullevålsveien 14</td><td style="border: 1px solid #000; text-align: center;">Oslo</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">Norway</td><td style="border: 1px solid #000; text-align: center;">0171</td><td style="border: 1px solid #000; text-align: center;">3.96</td></tr><tr><td style="border: 1px solid #000; text-align: center;">3</td><td style="border: 1px solid #000; text-align: center;">8</td><td style="border: 1px solid #000; text-align: center;">2021-01-03 00:00:00</td><td style="border: 1px solid #000; text-align: center;">Grétrystraat 63</td><td style="border: 1px solid #000; text-align: center;">Brussels</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">Belgium</td><td style="border: 1px solid #000; text-align: center;">1000</td><td style="border: 1px solid #000; text-align: center;">5.94</td></tr><tr><td style="border: 1px solid #000; text-align: center;">4</td><td style="border: 1px solid #000; text-align: center;">14</td><td style="border: 1px solid #000; text-align: center;">2021-01-06 00:00:00</td><td style="border: 1px solid #000; text-align: center;">8210 111 ST NW</td><td style="border: 1px solid #000; text-align: center;">Edmonton</td><td style="border: 1px solid #000; text-align: center;">AB</td><td style="border: 1px solid #000; text-align: center;">Canada</td><td style="border: 1px solid #000; text-align: center;">T6G 2C7</td><td style="border: 1px solid #000; text-align: center;">8.91</td></tr><tr><td style="border: 1px solid #000; text-align: center;">5</td><td style="border: 1px solid #000; text-align: center;">23</td><td style="border: 1px solid #000; text-align: center;">2021-01-11 00:00:00</td><td style="border: 1px solid #000; text-align: center;">69 Salem Street</td><td style="border: 1px solid #000; text-align: center;">Boston</td><td style="border: 1px solid #000; text-align: center;">MA</td><td style="border: 1px solid #000; text-align: center;">USA</td><td style="border: 1px solid #000; text-align: center;">2113</td><td style="border: 1px solid #000; text-align: center;">13.86</td></tr><tr><td style="border: 1px solid #000; text-align: center;">6</td><td style="border: 1px solid #000; text-align: center;">37</td><td style="border: 1px solid #000; text-align: center;">2021-01-19 00:00:00</td><td style="border: 1px solid #000; text-align: center;">Berger Straße 10</td><td style="border: 1px solid #000; text-align: center;">Frankfurt</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">Germany</td><td style="border: 1px solid #000; text-align: center;">60316</td><td style="border: 1px solid #000; text-align: center;">0.99</td></tr><tr><td style="border: 1px solid #000; text-align: center;">7</td><td style="border: 1px solid #000; text-align: center;">38</td><td style="border: 1px solid #000; text-align: center;">2021-02-01 00:00:00</td><td style="border: 1px solid #000; text-align: center;">Barbarossastraße 19</td><td style="border: 1px solid #000; text-align: center;">Berlin</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">Germany</td><td style="border: 1px solid #000; text-align: center;">10779</td><td style="border: 1px solid #000; text-align: center;">1.98</td></tr><tr><td style="border: 1px solid #000; text-align: center;">8</td><td style="border: 1px solid #000; text-align: center;">40</td><td style="border: 1px solid #000; text-align: center;">2021-02-01 00:00:00</td><td style="border: 1px solid #000; text-align: center;">8, Rue Hanovre</td><td style="border: 1px solid #000; text-align: center;">Paris</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">France</td><td style="border: 1px solid #000; text-align: center;">75002</td><td style="border: 1px solid #000; text-align: center;">1.98</td></tr><tr><td style="border: 1px solid #000; text-align: center;">9</td><td style="border: 1px solid #000; text-align: center;">42</td><td style="border: 1px solid #000; text-align: center;">2021-02-02 00:00:00</td><td style="border: 1px solid #000; text-align: center;">9, Place Louis Barthou</td><td style="border: 1px solid #000; text-align: center;">Bordeaux</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">France</td><td style="border: 1px solid #000; text-align: center;">33000</td><td style="border: 1px solid #000; text-align: center;">3.96</td></tr><tr><td style="border: 1px solid #000; text-align: center;">10</td><td style="border: 1px solid #000; text-align: center;">46</td><td style="border: 1px solid #000; text-align: center;">2021-02-03 00:00:00</td><td style="border: 1px solid #000; text-align: center;">3 Chatham Street</td><td style="border: 1px solid #000; text-align: center;">Dublin</td><td style="border: 1px solid #000; text-align: center;">Dublin</td><td style="border: 1px solid #000; text-align: center;">Ireland</td><td style="border: 1px solid #000; text-align: center;">None</td><td style="border: 1px solid #000; text-align: center;">5.94</td></tr><tr><td colspan="9" style="text-align:center;color:#888;font-style:italic;padding:8px;">... more rows ...</td></tr></tbody></table>
40
+
41
+ ---
42
+
43
+ You can preview select statements before fetching data to your variables.
44
+
45
+ ```py
46
+ table.select("InvoiceId", "CustomerId", "BillingAddress", "BillingCountry", "Total")\
47
+ .where\
48
+ .gte("Total", 2.0)\
49
+ .then\
50
+ .order_by("CustomerId")\
51
+ .limit(10)
52
+ ```
53
+
54
+ <table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">Invoice</caption><thead><tr><td style="border: 1px solid #555; text-align: center;">InvoiceId</td><td style="border: 1px solid #555; text-align: center;">CustomerId</td><td style="border: 1px solid #555; text-align: center;">BillingAddress</td><td style="border: 1px solid #555; text-align: center;">BillingCountry</td><td style="border: 1px solid #555; text-align: center;">Total</td></tr></thead><tbody><tr><td style="border: 1px solid #000; text-align: center;">98</td><td style="border: 1px solid #000; text-align: center;">1</td><td style="border: 1px solid #000; text-align: center;">Av. Brigadeiro Faria Lima, 2170</td><td style="border: 1px solid #000; text-align: center;">Brazil</td><td style="border: 1px solid #000; text-align: center;">3.98</td></tr><tr><td style="border: 1px solid #000; text-align: center;">121</td><td style="border: 1px solid #000; text-align: center;">1</td><td style="border: 1px solid #000; text-align: center;">Av. Brigadeiro Faria Lima, 2170</td><td style="border: 1px solid #000; text-align: center;">Brazil</td><td style="border: 1px solid #000; text-align: center;">3.96</td></tr><tr><td style="border: 1px solid #000; text-align: center;">143</td><td style="border: 1px solid #000; text-align: center;">1</td><td style="border: 1px solid #000; text-align: center;">Av. Brigadeiro Faria Lima, 2170</td><td style="border: 1px solid #000; text-align: center;">Brazil</td><td style="border: 1px solid #000; text-align: center;">5.94</td></tr><tr><td style="border: 1px solid #000; text-align: center;">327</td><td style="border: 1px solid #000; text-align: center;">1</td><td style="border: 1px solid #000; text-align: center;">Av. Brigadeiro Faria Lima, 2170</td><td style="border: 1px solid #000; text-align: center;">Brazil</td><td style="border: 1px solid #000; text-align: center;">13.86</td></tr><tr><td style="border: 1px solid #000; text-align: center;">382</td><td style="border: 1px solid #000; text-align: center;">1</td><td style="border: 1px solid #000; text-align: center;">Av. Brigadeiro Faria Lima, 2170</td><td style="border: 1px solid #000; text-align: center;">Brazil</td><td style="border: 1px solid #000; text-align: center;">8.91</td></tr><tr><td style="border: 1px solid #000; text-align: center;">12</td><td style="border: 1px solid #000; text-align: center;">2</td><td style="border: 1px solid #000; text-align: center;">Theodor-Heuss-Straße 34</td><td style="border: 1px solid #000; text-align: center;">Germany</td><td style="border: 1px solid #000; text-align: center;">13.86</td></tr><tr><td style="border: 1px solid #000; text-align: center;">67</td><td style="border: 1px solid #000; text-align: center;">2</td><td style="border: 1px solid #000; text-align: center;">Theodor-Heuss-Straße 34</td><td style="border: 1px solid #000; text-align: center;">Germany</td><td style="border: 1px solid #000; text-align: center;">8.91</td></tr><tr><td style="border: 1px solid #000; text-align: center;">219</td><td style="border: 1px solid #000; text-align: center;">2</td><td style="border: 1px solid #000; text-align: center;">Theodor-Heuss-Straße 34</td><td style="border: 1px solid #000; text-align: center;">Germany</td><td style="border: 1px solid #000; text-align: center;">3.96</td></tr><tr><td style="border: 1px solid #000; text-align: center;">241</td><td style="border: 1px solid #000; text-align: center;">2</td><td style="border: 1px solid #000; text-align: center;">Theodor-Heuss-Straße 34</td><td style="border: 1px solid #000; text-align: center;">Germany</td><td style="border: 1px solid #000; text-align: center;">5.94</td></tr><tr><td style="border: 1px solid #000; text-align: center;">99</td><td style="border: 1px solid #000; text-align: center;">3</td><td style="border: 1px solid #000; text-align: center;">1498 rue Bélanger</td><td style="border: 1px solid #000; text-align: center;">Canada</td><td style="border: 1px solid #000; text-align: center;">3.98</td></tr></tbody></table>
55
+
56
+
57
+ ## Purpose
58
+
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()`.
60
+
61
+ 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
+
63
+
64
+ ## Installation
65
+
66
+ To install `sqlengine`, you can use `pip`:
67
+
68
+ ```sh
69
+ pip install sqlengine-lite
70
+ ```
71
+
72
+ ## Env
73
+
74
+ You may set environment variable for logging. By default it's `WARNING`.
75
+
76
+ ```console
77
+ SQL_ENGINE_LOG_LEVEL=INFO
78
+ ```
79
+
80
+ # Quick Start
81
+
82
+ All you have to do to create your own cute little table is to [inherit](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#class-declaration) `SqlTableMixin` class or to create your own [schema](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#schema-declaration) and declare desired properties of your table's columns. They are:
83
+
84
+
85
+ _Name of the table that will be used in queries. If omitted in inherited class declaration, then it will take the class name._
86
+ ```py
87
+ __tablename__ : Optional[str]
88
+ ```
89
+
90
+ _Column names of the table_
91
+ ```py
92
+ __columns__ : list[str]
93
+ ```
94
+
95
+ _Column types of the table_
96
+ ```py
97
+ __types__ : list[SqlType | str]
98
+ ```
99
+
100
+ _List of primary keys_
101
+ ```py
102
+ __primary__ : list[str]
103
+ ```
104
+
105
+ ## Table Declaration
106
+
107
+ More details at [Declaration](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#table-declaration).
108
+
109
+ ```py
110
+ from sqlengine import SqlTableMixin
111
+
112
+ # Helper constants for column names
113
+ ID = "ID"
114
+ Name = "Name"
115
+ Occupation = "Occupation"
116
+ Salary = "Salary"
117
+
118
+
119
+ class Employees(SqlTableMixin):
120
+
121
+ __columns__ = [ID, Name, Occupation, Salary]
122
+ __types__ = [int, str, str, float]
123
+ __primary__ = [ID]
124
+
125
+ # You may overwrite your insert methods for type consistency
126
+ def insert(self, id : int, name : str, occupation : str, salary : float) -> None:
127
+ return super().insert(id, name, occupation, salary)
128
+
129
+ def upsert(self, id : int, name : str, occupation : str, salary : float) -> None:
130
+ return super().upsert(id, name, occupation, salary)
131
+ ```
132
+
133
+ ## Instantiation
134
+
135
+ More details at [Instantiation](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/table_declaration.md#table-instantiation).
136
+
137
+ ```py
138
+ # Create an instance of the table class
139
+ # with provided path to create or connect to
140
+
141
+ table = Employees("temp/data.db")
142
+ ```
143
+
144
+ ## Row insertion
145
+
146
+ ```py
147
+ # Insert one row
148
+
149
+ table.insert(1, "John Doe", "Software Engineer", 75000.0)
150
+ ```
151
+
152
+ ```py
153
+ # Bulk insert multiple rows
154
+
155
+ employees_data = [
156
+ (2, "Jane Smith", "Data Scientist", 80000.0),
157
+ (3, "Alice Johnson", "Product Manager", 90000.0),
158
+ (4, "Bob Brown", "Project Manager", 78000.0),
159
+ (5, "Charlie Davis", "UI/UX Designer", 65000.0),
160
+ (6, "David Wilson", "DevOps Engineer", 82000.0),
161
+ (7, "Eve Taylor", "Customer Support", 45000.0),
162
+ (8, "Frank White", "Quality Assurance", 53000.0),
163
+ (9, "Grace Hall", "Marketing Manager", 68000.0),
164
+ (10, "Henry Lee", "Technical Writer", 52000.0)
165
+ ]
166
+
167
+ table.insert_many(employees_data)
168
+ ```
169
+
170
+
171
+ ```py
172
+ # Upsert one row
173
+
174
+ table.upsert(1, "Jane Doe", "Data Scientist", 80000.0)
175
+ ```
176
+
177
+ ## Jupyter view
178
+
179
+ ```py
180
+ # Inspect tables in Jupyter Notebook
181
+
182
+ table
183
+ ```
184
+
185
+ <table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">Employees</caption><thead><tr><td style="border: 1px solid #555; text-align: center;">ID</td><td style="border: 1px solid #555; text-align: center;">Name</td><td style="border: 1px solid #555; text-align: center;">Occupation</td><td style="border: 1px solid #555; text-align: center;">Salary</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;">Jane Doe</td><td style="border: 1px solid #000; text-align: center;">Data Scientist</td><td style="border: 1px solid #000; text-align: center;">80000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">2</td><td style="border: 1px solid #000; text-align: center;">Jane Smith</td><td style="border: 1px solid #000; text-align: center;">Data Scientist</td><td style="border: 1px solid #000; text-align: center;">80000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">3</td><td style="border: 1px solid #000; text-align: center;">Alice Johnson</td><td style="border: 1px solid #000; text-align: center;">Product Manager</td><td style="border: 1px solid #000; text-align: center;">90000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">4</td><td style="border: 1px solid #000; text-align: center;">Bob Brown</td><td style="border: 1px solid #000; text-align: center;">Project Manager</td><td style="border: 1px solid #000; text-align: center;">78000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">5</td><td style="border: 1px solid #000; text-align: center;">Charlie Davis</td><td style="border: 1px solid #000; text-align: center;">UI/UX Designer</td><td style="border: 1px solid #000; text-align: center;">65000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">6</td><td style="border: 1px solid #000; text-align: center;">David Wilson</td><td style="border: 1px solid #000; text-align: center;">DevOps Engineer</td><td style="border: 1px solid #000; text-align: center;">82000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">7</td><td style="border: 1px solid #000; text-align: center;">Eve Taylor</td><td style="border: 1px solid #000; text-align: center;">Customer Support</td><td style="border: 1px solid #000; text-align: center;">45000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">8</td><td style="border: 1px solid #000; text-align: center;">Frank White</td><td style="border: 1px solid #000; text-align: center;">Quality Assurance</td><td style="border: 1px solid #000; text-align: center;">53000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">9</td><td style="border: 1px solid #000; text-align: center;">Grace Hall</td><td style="border: 1px solid #000; text-align: center;">Marketing Manager</td><td style="border: 1px solid #000; text-align: center;">68000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">10</td><td style="border: 1px solid #000; text-align: center;">Henry Lee</td><td style="border: 1px solid #000; text-align: center;">Technical Writer</td><td style="border: 1px solid #000; text-align: center;">52000.0</td></tr></tbody></table>
186
+
187
+ ## Select Query
188
+
189
+ More details at [Statements](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/statements.md).
190
+
191
+ ```py
192
+ # Query select and fetch
193
+
194
+ table.select.where.between(ID, 3, 5).then.fetchall()
195
+
196
+ # ->
197
+ # [(3, 'Alice Johnson', 'Product Manager', 90000.0),
198
+ # (4, 'Bob Brown', 'Project Manager', 78000.0),
199
+ # (5, 'Charlie Davis', 'UI/UX Designer', 65000.0)]
200
+ ```
201
+
202
+ ```py
203
+ # Inspect query select in Jupyter
204
+
205
+ table.select(Name, Salary).where.lt(Salary, 70_000)
206
+ ```
207
+
208
+ <table style="border-collapse: collapse; font-size: 14px;"><caption style="font-size: 18px; font-weight: bold;">Employees</caption><thead><tr><td style="border: 1px solid #555; text-align: center;">Name</td><td style="border: 1px solid #555; text-align: center;">Salary</td></tr></thead><tbody><tr><td style="border: 1px solid #000; text-align: center;">Charlie Davis</td><td style="border: 1px solid #000; text-align: center;">65000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">Eve Taylor</td><td style="border: 1px solid #000; text-align: center;">45000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">Frank White</td><td style="border: 1px solid #000; text-align: center;">53000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">Grace Hall</td><td style="border: 1px solid #000; text-align: center;">68000.0</td></tr><tr><td style="border: 1px solid #000; text-align: center;">Henry Lee</td><td style="border: 1px solid #000; text-align: center;">52000.0</td></tr></tbody></table>
209
+
210
+ ## Update Query
211
+
212
+ ```py
213
+ # equal to ...update.set(Salary, 50_000)...
214
+ table.update(Salary, 50_000).where.eq(Name, "Eve Taylor").then.execute()
215
+ ```
216
+
217
+ ## Delete Query
218
+
219
+ ```py
220
+ table.delete.where.eq(ID, 5).then.execute()
221
+ ```
222
+
223
+ ## Transaction
224
+
225
+ More details at [Transaction](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/transactions.md).
226
+
227
+ ```py
228
+ # Operate within a transaction
229
+
230
+ with table.transaction():
231
+ for row in employees_data:
232
+ table.upsert(*row)
233
+ ```
234
+
235
+ ## Get Item
236
+
237
+ More details at [Syntax Sugar](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/syntax_sugar.md).
238
+
239
+ ```py
240
+ # Fetch row by primary key
241
+
242
+ table[9]
243
+
244
+ # -> (9, 'Grace Hall', 'Marketing Manager', 68000.0)
245
+ ```
246
+
247
+ ```py
248
+ # Fetch slice by integer primary key
249
+
250
+ table[4:10:2]
251
+
252
+ # ->
253
+ # [(4, 'Bob Brown', 'Project Manager', 78000.0),
254
+ # (6, 'David Wilson', 'DevOps Engineer', 82000.0),
255
+ # (8, 'Frank White', 'Quality Assurance', 53000.0)]
256
+ ```
257
+
258
+ ## Csv Converter
259
+
260
+ ```py
261
+ # Save table to csv
262
+ from sqlengine.utils import to_csv
263
+
264
+ to_csv(table, "temp/table.csv")
265
+ ```
266
+
267
+ ```py
268
+ # Save query result to csv
269
+
270
+ to_csv(table.select.where.gt(Salary, 70_000), "temp/query.csv")
271
+ ```
272
+
273
+ # Full Documentation
274
+
275
+ For detailed usage, API reference, and advanced examples, see the [full documentation](https://github.com/suffermuffin/SQL-Engine/blob/main/docs/index.md).