buildaquery 0.2.0__tar.gz → 0.4.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.
Files changed (43) hide show
  1. {buildaquery-0.2.0 → buildaquery-0.4.0}/PKG-INFO +104 -7
  2. {buildaquery-0.2.0 → buildaquery-0.4.0}/README.md +114 -19
  3. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/abstract_syntax_tree/README.md +1 -1
  4. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/compiler/README.md +34 -1
  5. buildaquery-0.4.0/buildaquery/compiler/__init__.py +6 -0
  6. buildaquery-0.4.0/buildaquery/compiler/mysql/README.md +25 -0
  7. buildaquery-0.4.0/buildaquery/compiler/mysql/__init__.py +1 -0
  8. buildaquery-0.4.0/buildaquery/compiler/mysql/mysql_compiler.py +342 -0
  9. buildaquery-0.4.0/buildaquery/compiler/oracle/README.md +31 -0
  10. buildaquery-0.4.0/buildaquery/compiler/oracle/__init__.py +3 -0
  11. buildaquery-0.4.0/buildaquery/compiler/oracle/oracle_compiler.py +348 -0
  12. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/compiler/postgres/README.md +1 -1
  13. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/compiler/sqlite/README.md +2 -0
  14. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/execution/README.md +24 -1
  15. buildaquery-0.4.0/buildaquery/execution/__init__.py +6 -0
  16. buildaquery-0.4.0/buildaquery/execution/mysql.py +185 -0
  17. buildaquery-0.4.0/buildaquery/execution/oracle.py +189 -0
  18. buildaquery-0.4.0/buildaquery/tests/test_compiler_mysql.py +341 -0
  19. buildaquery-0.4.0/buildaquery/tests/test_compiler_oracle.py +374 -0
  20. buildaquery-0.4.0/buildaquery/tests/test_execution_mysql.py +51 -0
  21. buildaquery-0.4.0/buildaquery/tests/test_execution_oracle.py +51 -0
  22. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/traversal/README.md +1 -1
  23. {buildaquery-0.2.0 → buildaquery-0.4.0}/pyproject.toml +11 -9
  24. buildaquery-0.2.0/buildaquery/compiler/__init__.py +0 -0
  25. buildaquery-0.2.0/buildaquery/execution/__init__.py +0 -0
  26. {buildaquery-0.2.0 → buildaquery-0.4.0}/LICENSE.txt +0 -0
  27. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/__init__.py +0 -0
  28. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/abstract_syntax_tree/__init__.py +0 -0
  29. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/abstract_syntax_tree/models.py +0 -0
  30. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/compiler/compiled_query.py +0 -0
  31. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/compiler/postgres/postgres_compiler.py +0 -0
  32. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/compiler/sqlite/__init__.py +0 -0
  33. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/compiler/sqlite/sqlite_compiler.py +0 -0
  34. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/execution/base.py +0 -0
  35. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/execution/postgres.py +0 -0
  36. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/execution/sqlite.py +0 -0
  37. /buildaquery-0.2.0/buildaquery/tests/test_ast.py → /buildaquery-0.4.0/buildaquery/tests/test_ast_postgres.py +0 -0
  38. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/tests/test_compiler_postgres.py +0 -0
  39. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/tests/test_compiler_sqlite.py +0 -0
  40. /buildaquery-0.2.0/buildaquery/tests/test_execution.py → /buildaquery-0.4.0/buildaquery/tests/test_execution_postgres.py +0 -0
  41. /buildaquery-0.2.0/buildaquery/tests/test_traversal.py → /buildaquery-0.4.0/buildaquery/tests/test_traversal_postgres.py +0 -0
  42. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/traversal/__init__.py +0 -0
  43. {buildaquery-0.2.0 → buildaquery-0.4.0}/buildaquery/traversal/visitor_pattern.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: buildaquery
3
- Version: 0.2.0
4
- Summary: A Python-based query builder for PostgreSQL.
3
+ Version: 0.4.0
4
+ Summary: A Python-based query builder for PostgreSQL, SQLite, MySQL, and Oracle.
5
5
  License: MIT
6
6
  License-File: LICENSE.txt
7
7
  Author: Anirudh Bhattacharya
@@ -21,6 +21,8 @@ Classifier: Topic :: Software Development :: Libraries
21
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
22
  Requires-Dist: Pygments (==2.19.2)
23
23
  Requires-Dist: colorama (==0.4.6)
24
+ Requires-Dist: mysql-connector-python (==9.4.0)
25
+ Requires-Dist: oracledb (>=3.4.2,<4.0.0)
24
26
  Requires-Dist: packaging (==26.0)
25
27
  Requires-Dist: pluggy (==1.6.0)
26
28
  Requires-Dist: psycopg (==3.3.3)
@@ -33,7 +35,7 @@ Description-Content-Type: text/markdown
33
35
 
34
36
  # Build-a-Query
35
37
 
36
- A Python-based query builder designed to represent, compile, and execute SQL queries using a dialect-agnostic Abstract Syntax Tree (AST). Supports PostgreSQL and SQLite.
38
+ A Python-based query builder designed to represent, compile, and execute SQL queries using a dialect-agnostic Abstract Syntax Tree (AST). Supports PostgreSQL, SQLite, MySQL, and Oracle.
37
39
 
38
40
  ## Features
39
41
 
@@ -44,7 +46,12 @@ A Python-based query builder designed to represent, compile, and execute SQL que
44
46
  - **DDL Support**: Basic schema management with `CREATE TABLE` and `DROP TABLE`.
45
47
  - **Visitor Pattern Traversal**: Extensible architecture for analysis and compilation.
46
48
  - **Secure Compilation**: Automatic parameterization to prevent SQL injection.
47
- - **Execution Layer**: Built-in support for executing compiled queries via `psycopg` (PostgreSQL) and the standard library `sqlite3` (SQLite).
49
+ - **Execution Layer**: Built-in support for executing compiled queries via `psycopg` (PostgreSQL), `mysql-connector-python` (MySQL), `oracledb` (Oracle), and the standard library `sqlite3` (SQLite).
50
+
51
+ ## Dialect Notes
52
+ - MySQL does not support `INTERSECT` / `EXCEPT` or `DROP TABLE ... CASCADE` in this implementation (the compiler raises `ValueError`).
53
+ - SQLite does not support `DROP TABLE ... CASCADE` (the compiler raises `ValueError`).
54
+ - Oracle does not support `IF EXISTS` / `IF NOT EXISTS` in `DROP TABLE`/`CREATE TABLE` (the compiler raises `ValueError`), and `EXCEPT` is compiled as `MINUS`.
48
55
 
49
56
  ## Installation
50
57
 
@@ -61,6 +68,12 @@ pip install buildaquery
61
68
  - **PostgreSQL database**: A running PostgreSQL instance (version 12+ recommended). You can set this up locally, via Docker, or use a cloud service.
62
69
  - Example with Docker: `docker run --name postgres -e POSTGRES_PASSWORD=yourpassword -d -p 5432:5432 postgres:15`
63
70
  - `psycopg` (automatically installed as a dependency) - the PostgreSQL adapter for Python.
71
+ - **MySQL database**: A running MySQL instance (version 8.0+ recommended).
72
+ - Example with Docker: `docker run --name mysql -e MYSQL_ROOT_PASSWORD=yourpassword -e MYSQL_DATABASE=buildaquery -d -p 3306:3306 mysql:8.0`
73
+ - `mysql-connector-python` (automatically installed as a dependency) - the MySQL adapter for Python.
74
+ - **Oracle database**: A running Oracle instance (Oracle XE is suitable for development).
75
+ - Example with Docker (Oracle XE): `docker run --name oracle-xe -e ORACLE_PASSWORD=yourpassword -e APP_USER=buildaquery -e APP_USER_PASSWORD=yourpassword -d -p 1521:1521 gvenzl/oracle-xe:21-slim`
76
+ - `oracledb` (automatically installed as a dependency) - the Oracle adapter for Python.
64
77
  - `python-dotenv` (automatically installed as a dependency) - for loading environment variables from a `.env` file.
65
78
  - **SQLite**: Uses Python's standard library `sqlite3` module.
66
79
  - **SQLite Version**: SQLite 3.x via Python's `sqlite3` module (the exact SQLite version depends on your Python build; check `sqlite3.sqlite_version` at runtime).
@@ -84,6 +97,10 @@ DB_USER=postgres
84
97
  DB_PASSWORD=yourpassword
85
98
  ```
86
99
 
100
+ For MySQL, you can use a connection URL directly in code (e.g., `mysql://user:password@host:3306/dbname`) or set your own environment variables and construct the URL similarly.
101
+
102
+ For Oracle, use a connection URL in the format `oracle://user:password@host:port/service_name` (for example: `oracle://buildaquery:password@127.0.0.1:1521/XEPDB1`).
103
+
87
104
  ### For Developers
88
105
 
89
106
  Clone the repository and set up the development environment:
@@ -209,7 +226,87 @@ drop_stmt = DropStatementNode(table=users_table, if_exists=True)
209
226
  executor.execute(drop_stmt)
210
227
  ```
211
228
 
212
- For more examples, see the `examples/` directory.
229
+ ### MySQL Quick Start
230
+
231
+ ```python
232
+ from buildaquery.execution.mysql import MySqlExecutor
233
+ from buildaquery.abstract_syntax_tree.models import (
234
+ CreateStatementNode, TableNode, ColumnDefinitionNode,
235
+ InsertStatementNode, ColumnNode, LiteralNode,
236
+ SelectStatementNode, StarNode, DropStatementNode
237
+ )
238
+
239
+ executor = MySqlExecutor(connection_info="mysql://root:password@127.0.0.1:3306/buildaquery")
240
+
241
+ users_table = TableNode(name="users")
242
+ create_stmt = CreateStatementNode(
243
+ table=users_table,
244
+ columns=[
245
+ ColumnDefinitionNode(name="id", data_type="INT AUTO_INCREMENT", primary_key=True),
246
+ ColumnDefinitionNode(name="name", data_type="VARCHAR(255)", not_null=True),
247
+ ColumnDefinitionNode(name="age", data_type="INT")
248
+ ]
249
+ )
250
+ executor.execute(create_stmt)
251
+
252
+ insert_stmt = InsertStatementNode(
253
+ table=users_table,
254
+ columns=[ColumnNode(name="name"), ColumnNode(name="age")],
255
+ values=[LiteralNode(value="Alice"), LiteralNode(value=30)]
256
+ )
257
+ executor.execute(insert_stmt)
258
+
259
+ select_stmt = SelectStatementNode(
260
+ select_list=[StarNode()],
261
+ from_table=users_table
262
+ )
263
+ print(executor.execute(select_stmt))
264
+
265
+ drop_stmt = DropStatementNode(table=users_table, if_exists=True)
266
+ executor.execute(drop_stmt)
267
+ ```
268
+
269
+ ### Oracle Quick Start
270
+
271
+ ```python
272
+ from buildaquery.execution.oracle import OracleExecutor
273
+ from buildaquery.abstract_syntax_tree.models import (
274
+ CreateStatementNode, TableNode, ColumnDefinitionNode,
275
+ InsertStatementNode, ColumnNode, LiteralNode,
276
+ SelectStatementNode, StarNode, DropStatementNode
277
+ )
278
+
279
+ executor = OracleExecutor(connection_info="oracle://buildaquery:password@127.0.0.1:1521/XEPDB1")
280
+
281
+ users_table = TableNode(name="users")
282
+ create_stmt = CreateStatementNode(
283
+ table=users_table,
284
+ columns=[
285
+ ColumnDefinitionNode(name="id", data_type="NUMBER", primary_key=True),
286
+ ColumnDefinitionNode(name="name", data_type="VARCHAR2(255)", not_null=True),
287
+ ColumnDefinitionNode(name="age", data_type="NUMBER")
288
+ ]
289
+ )
290
+ executor.execute(create_stmt)
291
+
292
+ insert_stmt = InsertStatementNode(
293
+ table=users_table,
294
+ columns=[ColumnNode(name="id"), ColumnNode(name="name"), ColumnNode(name="age")],
295
+ values=[LiteralNode(value=1), LiteralNode(value="Alice"), LiteralNode(value=30)]
296
+ )
297
+ executor.execute(insert_stmt)
298
+
299
+ select_stmt = SelectStatementNode(
300
+ select_list=[StarNode()],
301
+ from_table=users_table
302
+ )
303
+ print(executor.execute(select_stmt))
304
+
305
+ drop_stmt = DropStatementNode(table=users_table, cascade=True)
306
+ executor.execute(drop_stmt)
307
+ ```
308
+
309
+ For more examples, see the `examples/` directory (including `examples/sample_mysql.py` and `examples/sample_oracle.py`).
213
310
 
214
311
  ## Development Setup
215
312
 
@@ -249,7 +346,7 @@ poetry run pytest buildaquery/tests
249
346
 
250
347
  #### Integration Tests
251
348
 
252
- Integration tests require a PostgreSQL database. Start the test database using Docker:
349
+ Integration tests require PostgreSQL, MySQL, and Oracle databases (and the respective drivers). Start the test databases using Docker:
253
350
 
254
351
  ```bash
255
352
  docker-compose up -d
@@ -283,7 +380,7 @@ poetry run python examples/sample_query.py
283
380
 
284
381
  - `buildaquery/abstract_syntax_tree/`: Defines query nodes and AST models.
285
382
  - `buildaquery/traversal/`: Base classes for AST traversal (Visitor/Transformer pattern).
286
- - `buildaquery/compiler/`: Dialect-specific SQL generation (PostgreSQL and SQLite).
383
+ - `buildaquery/compiler/`: Dialect-specific SQL generation (PostgreSQL, SQLite, MySQL, Oracle).
287
384
  - `buildaquery/execution/`: Database connection and execution logic.
288
385
  - `tests/`: Exhaustive unit and integration tests.
289
386
  - `examples/`: Practical demonstrations of the library.
@@ -1,8 +1,8 @@
1
1
  # Build-a-Query
2
2
 
3
- A Python-based query builder designed to represent, compile, and execute SQL queries using a dialect-agnostic Abstract Syntax Tree (AST). Supports PostgreSQL and SQLite.
3
+ A Python-based query builder designed to represent, compile, and execute SQL queries using a dialect-agnostic Abstract Syntax Tree (AST). Supports PostgreSQL, SQLite, MySQL, and Oracle.
4
4
 
5
- ## Features
5
+ ## Features
6
6
 
7
7
  - **Dialect-Agnostic AST**: Build queries using high-level Python objects.
8
8
  - **Full DML Support**: Create `SELECT`, `INSERT`, `UPDATE`, and `DELETE` statements.
@@ -11,7 +11,12 @@ A Python-based query builder designed to represent, compile, and execute SQL que
11
11
  - **DDL Support**: Basic schema management with `CREATE TABLE` and `DROP TABLE`.
12
12
  - **Visitor Pattern Traversal**: Extensible architecture for analysis and compilation.
13
13
  - **Secure Compilation**: Automatic parameterization to prevent SQL injection.
14
- - **Execution Layer**: Built-in support for executing compiled queries via `psycopg` (PostgreSQL) and the standard library `sqlite3` (SQLite).
14
+ - **Execution Layer**: Built-in support for executing compiled queries via `psycopg` (PostgreSQL), `mysql-connector-python` (MySQL), `oracledb` (Oracle), and the standard library `sqlite3` (SQLite).
15
+
16
+ ## Dialect Notes
17
+ - MySQL does not support `INTERSECT` / `EXCEPT` or `DROP TABLE ... CASCADE` in this implementation (the compiler raises `ValueError`).
18
+ - SQLite does not support `DROP TABLE ... CASCADE` (the compiler raises `ValueError`).
19
+ - Oracle does not support `IF EXISTS` / `IF NOT EXISTS` in `DROP TABLE`/`CREATE TABLE` (the compiler raises `ValueError`), and `EXCEPT` is compiled as `MINUS`.
15
20
 
16
21
  ## Installation
17
22
 
@@ -28,13 +33,19 @@ pip install buildaquery
28
33
  - **PostgreSQL database**: A running PostgreSQL instance (version 12+ recommended). You can set this up locally, via Docker, or use a cloud service.
29
34
  - Example with Docker: `docker run --name postgres -e POSTGRES_PASSWORD=yourpassword -d -p 5432:5432 postgres:15`
30
35
  - `psycopg` (automatically installed as a dependency) - the PostgreSQL adapter for Python.
36
+ - **MySQL database**: A running MySQL instance (version 8.0+ recommended).
37
+ - Example with Docker: `docker run --name mysql -e MYSQL_ROOT_PASSWORD=yourpassword -e MYSQL_DATABASE=buildaquery -d -p 3306:3306 mysql:8.0`
38
+ - `mysql-connector-python` (automatically installed as a dependency) - the MySQL adapter for Python.
39
+ - **Oracle database**: A running Oracle instance (Oracle XE is suitable for development).
40
+ - Example with Docker (Oracle XE): `docker run --name oracle-xe -e ORACLE_PASSWORD=yourpassword -e APP_USER=buildaquery -e APP_USER_PASSWORD=yourpassword -d -p 1521:1521 gvenzl/oracle-xe:21-slim`
41
+ - `oracledb` (automatically installed as a dependency) - the Oracle adapter for Python.
31
42
  - `python-dotenv` (automatically installed as a dependency) - for loading environment variables from a `.env` file.
32
43
  - **SQLite**: Uses Python's standard library `sqlite3` module.
33
44
  - **SQLite Version**: SQLite 3.x via Python's `sqlite3` module (the exact SQLite version depends on your Python build; check `sqlite3.sqlite_version` at runtime).
34
45
 
35
46
  ### Environment Variables
36
47
 
37
- To connect to your PostgreSQL database, set the following environment variables (or use a `.env` file with `python-dotenv`):
48
+ To connect to your PostgreSQL database, set the following environment variables (or use a `.env` file with `python-dotenv`):
38
49
 
39
50
  - `DB_HOST`: PostgreSQL host (e.g., `localhost`)
40
51
  - `DB_PORT`: PostgreSQL port (e.g., `5432`)
@@ -42,14 +53,18 @@ To connect to your PostgreSQL database, set the following environment variables
42
53
  - `DB_USER`: Database username (e.g., `postgres`)
43
54
  - `DB_PASSWORD`: Database password (e.g., `yourpassword`)
44
55
 
45
- Example `.env` file:
46
- ```
47
- DB_HOST=localhost
48
- DB_PORT=5432
49
- DB_NAME=buildaquery
50
- DB_USER=postgres
51
- DB_PASSWORD=yourpassword
52
- ```
56
+ Example `.env` file:
57
+ ```
58
+ DB_HOST=localhost
59
+ DB_PORT=5432
60
+ DB_NAME=buildaquery
61
+ DB_USER=postgres
62
+ DB_PASSWORD=yourpassword
63
+ ```
64
+
65
+ For MySQL, you can use a connection URL directly in code (e.g., `mysql://user:password@host:3306/dbname`) or set your own environment variables and construct the URL similarly.
66
+
67
+ For Oracle, use a connection URL in the format `oracle://user:password@host:port/service_name` (for example: `oracle://buildaquery:password@127.0.0.1:1521/XEPDB1`).
53
68
 
54
69
  ### For Developers
55
70
 
@@ -72,7 +87,7 @@ Activate the virtual environment:
72
87
  poetry shell
73
88
  ```
74
89
 
75
- ## Quick Start
90
+ ## Quick Start
76
91
 
77
92
  Here's a simple example of creating a table, inserting data, querying it, and dropping the table. This example uses environment variables for database connection (see Environment Variables section above).
78
93
 
@@ -176,7 +191,87 @@ drop_stmt = DropStatementNode(table=users_table, if_exists=True)
176
191
  executor.execute(drop_stmt)
177
192
  ```
178
193
 
179
- For more examples, see the `examples/` directory.
194
+ ### MySQL Quick Start
195
+
196
+ ```python
197
+ from buildaquery.execution.mysql import MySqlExecutor
198
+ from buildaquery.abstract_syntax_tree.models import (
199
+ CreateStatementNode, TableNode, ColumnDefinitionNode,
200
+ InsertStatementNode, ColumnNode, LiteralNode,
201
+ SelectStatementNode, StarNode, DropStatementNode
202
+ )
203
+
204
+ executor = MySqlExecutor(connection_info="mysql://root:password@127.0.0.1:3306/buildaquery")
205
+
206
+ users_table = TableNode(name="users")
207
+ create_stmt = CreateStatementNode(
208
+ table=users_table,
209
+ columns=[
210
+ ColumnDefinitionNode(name="id", data_type="INT AUTO_INCREMENT", primary_key=True),
211
+ ColumnDefinitionNode(name="name", data_type="VARCHAR(255)", not_null=True),
212
+ ColumnDefinitionNode(name="age", data_type="INT")
213
+ ]
214
+ )
215
+ executor.execute(create_stmt)
216
+
217
+ insert_stmt = InsertStatementNode(
218
+ table=users_table,
219
+ columns=[ColumnNode(name="name"), ColumnNode(name="age")],
220
+ values=[LiteralNode(value="Alice"), LiteralNode(value=30)]
221
+ )
222
+ executor.execute(insert_stmt)
223
+
224
+ select_stmt = SelectStatementNode(
225
+ select_list=[StarNode()],
226
+ from_table=users_table
227
+ )
228
+ print(executor.execute(select_stmt))
229
+
230
+ drop_stmt = DropStatementNode(table=users_table, if_exists=True)
231
+ executor.execute(drop_stmt)
232
+ ```
233
+
234
+ ### Oracle Quick Start
235
+
236
+ ```python
237
+ from buildaquery.execution.oracle import OracleExecutor
238
+ from buildaquery.abstract_syntax_tree.models import (
239
+ CreateStatementNode, TableNode, ColumnDefinitionNode,
240
+ InsertStatementNode, ColumnNode, LiteralNode,
241
+ SelectStatementNode, StarNode, DropStatementNode
242
+ )
243
+
244
+ executor = OracleExecutor(connection_info="oracle://buildaquery:password@127.0.0.1:1521/XEPDB1")
245
+
246
+ users_table = TableNode(name="users")
247
+ create_stmt = CreateStatementNode(
248
+ table=users_table,
249
+ columns=[
250
+ ColumnDefinitionNode(name="id", data_type="NUMBER", primary_key=True),
251
+ ColumnDefinitionNode(name="name", data_type="VARCHAR2(255)", not_null=True),
252
+ ColumnDefinitionNode(name="age", data_type="NUMBER")
253
+ ]
254
+ )
255
+ executor.execute(create_stmt)
256
+
257
+ insert_stmt = InsertStatementNode(
258
+ table=users_table,
259
+ columns=[ColumnNode(name="id"), ColumnNode(name="name"), ColumnNode(name="age")],
260
+ values=[LiteralNode(value=1), LiteralNode(value="Alice"), LiteralNode(value=30)]
261
+ )
262
+ executor.execute(insert_stmt)
263
+
264
+ select_stmt = SelectStatementNode(
265
+ select_list=[StarNode()],
266
+ from_table=users_table
267
+ )
268
+ print(executor.execute(select_stmt))
269
+
270
+ drop_stmt = DropStatementNode(table=users_table, cascade=True)
271
+ executor.execute(drop_stmt)
272
+ ```
273
+
274
+ For more examples, see the `examples/` directory (including `examples/sample_mysql.py` and `examples/sample_oracle.py`).
180
275
 
181
276
  ## Development Setup
182
277
 
@@ -214,9 +309,9 @@ Run unit tests for all modules:
214
309
  poetry run pytest buildaquery/tests
215
310
  ```
216
311
 
217
- #### Integration Tests
218
-
219
- Integration tests require a PostgreSQL database. Start the test database using Docker:
312
+ #### Integration Tests
313
+
314
+ Integration tests require PostgreSQL, MySQL, and Oracle databases (and the respective drivers). Start the test databases using Docker:
220
315
 
221
316
  ```bash
222
317
  docker-compose up -d
@@ -250,9 +345,9 @@ poetry run python examples/sample_query.py
250
345
 
251
346
  - `buildaquery/abstract_syntax_tree/`: Defines query nodes and AST models.
252
347
  - `buildaquery/traversal/`: Base classes for AST traversal (Visitor/Transformer pattern).
253
- - `buildaquery/compiler/`: Dialect-specific SQL generation (PostgreSQL and SQLite).
348
+ - `buildaquery/compiler/`: Dialect-specific SQL generation (PostgreSQL, SQLite, MySQL, Oracle).
254
349
  - `buildaquery/execution/`: Database connection and execution logic.
255
- - `tests/`: Exhaustive unit and integration tests.
350
+ - `tests/`: Exhaustive unit and integration tests.
256
351
  - `examples/`: Practical demonstrations of the library.
257
352
  - `scripts/`: Utility scripts for testing and maintenance.
258
353
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  The `abstract_syntax_tree` module defines the data structures used to represent SQL queries as an Abstract Syntax Tree. This representation is agnostic of any specific SQL dialect and serves as the intermediate format that the query builder constructs and the compiler translates into SQL.
4
4
 
5
- **SQLite Version**: SQLite 3.x via Python's `sqlite3` module (the exact SQLite version depends on your Python build; check `sqlite3.sqlite_version` at runtime).
5
+ **Dialect Notes**: The AST is dialect-agnostic and compilers target PostgreSQL, SQLite, and MySQL.
6
6
 
7
7
  ## Core Concepts
8
8
 
@@ -44,12 +44,35 @@ The initial implementation supports PostgreSQL.
44
44
  - **Core AST Coverage**: Supports the same AST nodes as PostgreSQL where SQLite syntax allows.
45
45
  - **TOP Translation**: Maps `TopClauseNode` to `LIMIT`, with optional implicit `ORDER BY`.
46
46
  - **CASCADE Handling**: Raises a `ValueError` when `DropStatementNode.cascade=True`, because SQLite does not support `DROP TABLE ... CASCADE`.
47
+
48
+ ### MySQL (`MySqlCompiler`)
49
+
50
+ #### Key Features:
51
+ - **`%s` Placeholders**: Uses MySQL-compatible parameter style.
52
+ - **Core AST Coverage**: Supports the same AST nodes as PostgreSQL where MySQL syntax allows.
53
+ - **TOP Translation**: Maps `TopClauseNode` to `LIMIT`, with optional implicit `ORDER BY`.
54
+ - **Set Operation Limits**: Raises a `ValueError` for `INTERSECT` and `EXCEPT` (unsupported in MySQL).
55
+ - **CASCADE Handling**: Raises a `ValueError` when `DropStatementNode.cascade=True`, because MySQL does not support `DROP TABLE ... CASCADE`.
56
+
57
+ ### Oracle (`OracleCompiler`)
58
+
59
+ #### Key Features:
60
+ - **`:1` Placeholders**: Uses Oracle-style positional bind parameters.
61
+ - **LIMIT/OFFSET Translation**: Uses `OFFSET ... ROWS` and `FETCH FIRST ... ROWS ONLY`.
62
+ - **TOP Translation**: Maps `TopClauseNode` to `FETCH FIRST`, with optional implicit `ORDER BY`.
63
+ - **Set Operation Notes**:
64
+ - `EXCEPT` is translated to `MINUS`.
65
+ - `INTERSECT ALL` and `MINUS ALL` raise `ValueError`.
66
+ - **Table Aliases**: Emits `table alias` (Oracle does not allow `AS` for table aliases).
67
+ - **IF EXISTS/IF NOT EXISTS**: Raises `ValueError` for `DropStatementNode.if_exists=True` and `CreateStatementNode.if_not_exists=True`.
47
68
 
48
69
  ## Usage Example
49
70
 
50
71
  ```python
51
72
  from buildaquery.compiler.postgres.postgres_compiler import PostgresCompiler
52
73
  from buildaquery.compiler.sqlite.sqlite_compiler import SqliteCompiler
74
+ from buildaquery.compiler.mysql.mysql_compiler import MySqlCompiler
75
+ from buildaquery.compiler.oracle.oracle_compiler import OracleCompiler
53
76
 
54
77
  compiler = PostgresCompiler()
55
78
  compiled = compiler.compile(ast_root)
@@ -61,4 +84,14 @@ sqlite_compiler = SqliteCompiler()
61
84
  compiled = sqlite_compiler.compile(ast_root)
62
85
  print(compiled.sql) # "SELECT * FROM users WHERE id = ?"
63
86
  print(compiled.params) # [123]
64
- ```
87
+
88
+ mysql_compiler = MySqlCompiler()
89
+ compiled = mysql_compiler.compile(ast_root)
90
+ print(compiled.sql) # "SELECT * FROM users WHERE id = %s"
91
+ print(compiled.params) # [123]
92
+
93
+ oracle_compiler = OracleCompiler()
94
+ compiled = oracle_compiler.compile(ast_root)
95
+ print(compiled.sql) # "SELECT * FROM users WHERE id = :1"
96
+ print(compiled.params) # [123]
97
+ ```
@@ -0,0 +1,6 @@
1
+ from buildaquery.compiler.postgres.postgres_compiler import PostgresCompiler
2
+ from buildaquery.compiler.sqlite.sqlite_compiler import SqliteCompiler
3
+ from buildaquery.compiler.mysql.mysql_compiler import MySqlCompiler
4
+ from buildaquery.compiler.oracle.oracle_compiler import OracleCompiler
5
+
6
+ __all__ = ["PostgresCompiler", "SqliteCompiler", "MySqlCompiler", "OracleCompiler"]
@@ -0,0 +1,25 @@
1
+ # MySQL Compiler
2
+
3
+ The `MySqlCompiler` translates the AST into MySQL-compatible SQL with `%s` placeholders.
4
+
5
+ ## Notes
6
+
7
+ - **Placeholders**: Uses `%s` for parameters (compatible with `mysql-connector-python`).
8
+ - **TOP Translation**: `TopClauseNode` is translated into `LIMIT`, with optional implicit `ORDER BY`.
9
+ - **Unsupported Operations**:
10
+ - `INTERSECT` and `EXCEPT` raise `ValueError` (MySQL does not support them).
11
+ - `DROP TABLE ... CASCADE` raises `ValueError`.
12
+
13
+ ## Example
14
+
15
+ ```python
16
+ from buildaquery.compiler.mysql.mysql_compiler import MySqlCompiler
17
+ from buildaquery.abstract_syntax_tree.models import SelectStatementNode, StarNode, TableNode
18
+
19
+ compiler = MySqlCompiler()
20
+ query = SelectStatementNode(select_list=[StarNode()], from_table=TableNode(name="users"))
21
+ compiled = compiler.compile(query)
22
+
23
+ print(compiled.sql) # SELECT * FROM users
24
+ print(compiled.params) # []
25
+ ```
@@ -0,0 +1 @@
1
+ # Intentionally left empty to mark mysql as a package.