buildaquery 0.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.
- buildaquery-0.1.0/LICENSE.txt +21 -0
- buildaquery-0.1.0/PKG-INFO +255 -0
- buildaquery-0.1.0/README.md +221 -0
- buildaquery-0.1.0/buildaquery/__init__.py +0 -0
- buildaquery-0.1.0/buildaquery/abstract_syntax_tree/README.md +54 -0
- buildaquery-0.1.0/buildaquery/abstract_syntax_tree/__init__.py +1 -0
- buildaquery-0.1.0/buildaquery/abstract_syntax_tree/models.py +285 -0
- buildaquery-0.1.0/buildaquery/compiler/README.md +48 -0
- buildaquery-0.1.0/buildaquery/compiler/__init__.py +0 -0
- buildaquery-0.1.0/buildaquery/compiler/postgres/README.md +25 -0
- buildaquery-0.1.0/buildaquery/compiler/postgres/postgres_compiler.py +366 -0
- buildaquery-0.1.0/buildaquery/execution/README.md +42 -0
- buildaquery-0.1.0/buildaquery/execution/__init__.py +0 -0
- buildaquery-0.1.0/buildaquery/execution/base.py +33 -0
- buildaquery-0.1.0/buildaquery/execution/postgres.py +118 -0
- buildaquery-0.1.0/buildaquery/tests/test_ast.py +40 -0
- buildaquery-0.1.0/buildaquery/tests/test_compiler_postgres.py +343 -0
- buildaquery-0.1.0/buildaquery/tests/test_execution.py +56 -0
- buildaquery-0.1.0/buildaquery/tests/test_traversal.py +44 -0
- buildaquery-0.1.0/buildaquery/traversal/README.md +46 -0
- buildaquery-0.1.0/buildaquery/traversal/__init__.py +1 -0
- buildaquery-0.1.0/buildaquery/traversal/visitor_pattern.py +36 -0
- buildaquery-0.1.0/pyproject.toml +52 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AnirudhB3000
|
|
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,255 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: buildaquery
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python-based query builder for PostgreSQL.
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE.txt
|
|
7
|
+
Author: Anirudh Bhattacharya
|
|
8
|
+
Author-email: anirudhbhattacharya1@gmail.com
|
|
9
|
+
Requires-Python: >=3.12,<4.0
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Topic :: Database
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Dist: Pygments (==2.19.2)
|
|
23
|
+
Requires-Dist: colorama (==0.4.6)
|
|
24
|
+
Requires-Dist: packaging (==26.0)
|
|
25
|
+
Requires-Dist: pluggy (==1.6.0)
|
|
26
|
+
Requires-Dist: psycopg (==3.3.3)
|
|
27
|
+
Requires-Dist: python-dotenv (>=1.2.1,<2.0.0)
|
|
28
|
+
Requires-Dist: typing_extensions (==4.15.0)
|
|
29
|
+
Requires-Dist: tzdata (==2025.3)
|
|
30
|
+
Project-URL: Homepage, https://github.com/AnirudhB3000/buildaquery
|
|
31
|
+
Project-URL: Repository, https://github.com/AnirudhB3000/buildaquery
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# Build-a-Query
|
|
35
|
+
|
|
36
|
+
A Python-based query builder designed to represent, compile, and execute SQL queries using a dialect-agnostic Abstract Syntax Tree (AST). Initial support is focused on PostgreSQL.
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
- **Dialect-Agnostic AST**: Build queries using high-level Python objects.
|
|
41
|
+
- **Full DML Support**: Create `SELECT`, `INSERT`, `UPDATE`, and `DELETE` statements.
|
|
42
|
+
- **Advanced Querying**: Support for CTEs (`WITH`), Subqueries, Set Operations (`UNION`, `INTERSECT`, `EXCEPT`), and Window Functions (`OVER`).
|
|
43
|
+
- **Rich Expression Logic**: Includes `CASE` expressions, `IN`, `BETWEEN`, and type casting.
|
|
44
|
+
- **DDL Support**: Basic schema management with `CREATE TABLE` and `DROP TABLE`.
|
|
45
|
+
- **Visitor Pattern Traversal**: Extensible architecture for analysis and compilation.
|
|
46
|
+
- **Secure Compilation**: Automatic parameterization to prevent SQL injection.
|
|
47
|
+
- **Execution Layer**: Built-in support for executing compiled queries via `psycopg`.
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
### For Users
|
|
52
|
+
|
|
53
|
+
Install Build-a-Query via pip:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install buildaquery
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Requirements:**
|
|
60
|
+
- Python 3.12+
|
|
61
|
+
- **PostgreSQL database**: A running PostgreSQL instance (version 12+ recommended). You can set this up locally, via Docker, or use a cloud service.
|
|
62
|
+
- Example with Docker: `docker run --name postgres -e POSTGRES_PASSWORD=yourpassword -d -p 5432:5432 postgres:15`
|
|
63
|
+
- `psycopg` (automatically installed as a dependency) - the PostgreSQL adapter for Python.
|
|
64
|
+
- `python-dotenv` (automatically installed as a dependency) - for loading environment variables from a `.env` file.
|
|
65
|
+
|
|
66
|
+
### Environment Variables
|
|
67
|
+
|
|
68
|
+
To connect to your PostgreSQL database, set the following environment variables (or use a `.env` file with `python-dotenv`):
|
|
69
|
+
|
|
70
|
+
- `DB_HOST`: PostgreSQL host (e.g., `localhost`)
|
|
71
|
+
- `DB_PORT`: PostgreSQL port (e.g., `5432`)
|
|
72
|
+
- `DB_NAME`: Database name (e.g., `mydatabase`)
|
|
73
|
+
- `DB_USER`: Database username (e.g., `postgres`)
|
|
74
|
+
- `DB_PASSWORD`: Database password (e.g., `yourpassword`)
|
|
75
|
+
|
|
76
|
+
Example `.env` file:
|
|
77
|
+
```
|
|
78
|
+
DB_HOST=localhost
|
|
79
|
+
DB_PORT=5432
|
|
80
|
+
DB_NAME=buildaquery
|
|
81
|
+
DB_USER=postgres
|
|
82
|
+
DB_PASSWORD=yourpassword
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### For Developers
|
|
86
|
+
|
|
87
|
+
Clone the repository and set up the development environment:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
git clone https://github.com/yourusername/buildaquery.git
|
|
91
|
+
cd buildaquery
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Install dependencies using Poetry:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
poetry install
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Activate the virtual environment:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
poetry shell
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Quick Start
|
|
107
|
+
|
|
108
|
+
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).
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from dotenv import load_dotenv
|
|
112
|
+
import os
|
|
113
|
+
from buildaquery.execution.postgres import PostgresExecutor
|
|
114
|
+
from buildaquery.abstract_syntax_tree.models import (
|
|
115
|
+
CreateStatementNode, TableNode, ColumnDefinitionNode,
|
|
116
|
+
InsertStatementNode, ColumnNode, LiteralNode,
|
|
117
|
+
SelectStatementNode, StarNode, DropStatementNode
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Load environment variables
|
|
121
|
+
load_dotenv()
|
|
122
|
+
|
|
123
|
+
# Build connection string from environment variables
|
|
124
|
+
db_host = os.getenv('DB_HOST')
|
|
125
|
+
db_port = os.getenv('DB_PORT')
|
|
126
|
+
db_name = os.getenv('DB_NAME')
|
|
127
|
+
db_user = os.getenv('DB_USER')
|
|
128
|
+
db_password = os.getenv('DB_PASSWORD')
|
|
129
|
+
|
|
130
|
+
connection_string = f"postgresql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}"
|
|
131
|
+
|
|
132
|
+
# Set up executor with your PostgreSQL connection
|
|
133
|
+
executor = PostgresExecutor(connection_info=connection_string)
|
|
134
|
+
|
|
135
|
+
# Define table
|
|
136
|
+
users_table = TableNode(name="users")
|
|
137
|
+
|
|
138
|
+
# Create table
|
|
139
|
+
create_stmt = CreateStatementNode(
|
|
140
|
+
table=users_table,
|
|
141
|
+
columns=[
|
|
142
|
+
ColumnDefinitionNode(name="id", data_type="SERIAL", primary_key=True),
|
|
143
|
+
ColumnDefinitionNode(name="name", data_type="TEXT", not_null=True),
|
|
144
|
+
ColumnDefinitionNode(name="age", data_type="INTEGER")
|
|
145
|
+
]
|
|
146
|
+
)
|
|
147
|
+
executor.execute(create_stmt)
|
|
148
|
+
|
|
149
|
+
# Insert data
|
|
150
|
+
insert_stmt = InsertStatementNode(
|
|
151
|
+
table=users_table,
|
|
152
|
+
columns=[ColumnNode(name="name"), ColumnNode(name="age")],
|
|
153
|
+
values=[LiteralNode(value="Alice"), LiteralNode(value=30)]
|
|
154
|
+
)
|
|
155
|
+
executor.execute(insert_stmt)
|
|
156
|
+
|
|
157
|
+
# Query data
|
|
158
|
+
select_stmt = SelectStatementNode(
|
|
159
|
+
select_list=[StarNode()], # SELECT *
|
|
160
|
+
from_table=users_table
|
|
161
|
+
)
|
|
162
|
+
results = executor.execute(select_stmt)
|
|
163
|
+
print(results) # [(1, 'Alice', 30)]
|
|
164
|
+
|
|
165
|
+
# Drop table
|
|
166
|
+
drop_stmt = DropStatementNode(table=users_table, if_exists=True)
|
|
167
|
+
executor.execute(drop_stmt)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
For more examples, see the `examples/` directory.
|
|
171
|
+
|
|
172
|
+
## Development Setup
|
|
173
|
+
|
|
174
|
+
### Prerequisites
|
|
175
|
+
|
|
176
|
+
- Python 3.12+
|
|
177
|
+
- Poetry (for dependency management)
|
|
178
|
+
- Docker (for running integration tests)
|
|
179
|
+
|
|
180
|
+
### Setting Up the Environment
|
|
181
|
+
|
|
182
|
+
1. Clone the repository:
|
|
183
|
+
```bash
|
|
184
|
+
git clone https://github.com/yourusername/buildaquery.git
|
|
185
|
+
cd buildaquery
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
2. Install dependencies:
|
|
189
|
+
```bash
|
|
190
|
+
poetry install
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
3. Activate the virtual environment:
|
|
194
|
+
```bash
|
|
195
|
+
poetry shell
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Running Tests
|
|
199
|
+
|
|
200
|
+
#### Unit Tests
|
|
201
|
+
|
|
202
|
+
Run unit tests for all modules:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
poetry run pytest buildaquery/tests
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### Integration Tests
|
|
209
|
+
|
|
210
|
+
Integration tests require a PostgreSQL database. Start the test database using Docker:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
docker-compose up -d
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Then run integration tests:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
poetry run pytest tests
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
#### All Tests
|
|
223
|
+
|
|
224
|
+
Run all tests (unit and integration):
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
poetry run all-tests
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Running Examples
|
|
231
|
+
|
|
232
|
+
Execute the sample script:
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
poetry run python examples/sample_query.py
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Project Structure
|
|
239
|
+
|
|
240
|
+
- `buildaquery/abstract_syntax_tree/`: Defines query nodes and AST models.
|
|
241
|
+
- `buildaquery/traversal/`: Base classes for AST traversal (Visitor/Transformer pattern).
|
|
242
|
+
- `buildaquery/compiler/`: Dialect-specific SQL generation (currently PostgreSQL).
|
|
243
|
+
- `buildaquery/execution/`: Database connection and execution logic.
|
|
244
|
+
- `tests/`: Exhaustive unit and integration tests.
|
|
245
|
+
- `examples/`: Practical demonstrations of the library.
|
|
246
|
+
- `scripts/`: Utility scripts for testing and maintenance.
|
|
247
|
+
|
|
248
|
+
## Contributing
|
|
249
|
+
|
|
250
|
+
Contributions are welcome! Please see the contributing guidelines for more information.
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
This project is licensed under the MIT License - see the [LICENSE.txt](LICENSE.txt) file for details.
|
|
255
|
+
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Build-a-Query
|
|
2
|
+
|
|
3
|
+
A Python-based query builder designed to represent, compile, and execute SQL queries using a dialect-agnostic Abstract Syntax Tree (AST). Initial support is focused on PostgreSQL.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Dialect-Agnostic AST**: Build queries using high-level Python objects.
|
|
8
|
+
- **Full DML Support**: Create `SELECT`, `INSERT`, `UPDATE`, and `DELETE` statements.
|
|
9
|
+
- **Advanced Querying**: Support for CTEs (`WITH`), Subqueries, Set Operations (`UNION`, `INTERSECT`, `EXCEPT`), and Window Functions (`OVER`).
|
|
10
|
+
- **Rich Expression Logic**: Includes `CASE` expressions, `IN`, `BETWEEN`, and type casting.
|
|
11
|
+
- **DDL Support**: Basic schema management with `CREATE TABLE` and `DROP TABLE`.
|
|
12
|
+
- **Visitor Pattern Traversal**: Extensible architecture for analysis and compilation.
|
|
13
|
+
- **Secure Compilation**: Automatic parameterization to prevent SQL injection.
|
|
14
|
+
- **Execution Layer**: Built-in support for executing compiled queries via `psycopg`.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
### For Users
|
|
19
|
+
|
|
20
|
+
Install Build-a-Query via pip:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install buildaquery
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Requirements:**
|
|
27
|
+
- Python 3.12+
|
|
28
|
+
- **PostgreSQL database**: A running PostgreSQL instance (version 12+ recommended). You can set this up locally, via Docker, or use a cloud service.
|
|
29
|
+
- Example with Docker: `docker run --name postgres -e POSTGRES_PASSWORD=yourpassword -d -p 5432:5432 postgres:15`
|
|
30
|
+
- `psycopg` (automatically installed as a dependency) - the PostgreSQL adapter for Python.
|
|
31
|
+
- `python-dotenv` (automatically installed as a dependency) - for loading environment variables from a `.env` file.
|
|
32
|
+
|
|
33
|
+
### Environment Variables
|
|
34
|
+
|
|
35
|
+
To connect to your PostgreSQL database, set the following environment variables (or use a `.env` file with `python-dotenv`):
|
|
36
|
+
|
|
37
|
+
- `DB_HOST`: PostgreSQL host (e.g., `localhost`)
|
|
38
|
+
- `DB_PORT`: PostgreSQL port (e.g., `5432`)
|
|
39
|
+
- `DB_NAME`: Database name (e.g., `mydatabase`)
|
|
40
|
+
- `DB_USER`: Database username (e.g., `postgres`)
|
|
41
|
+
- `DB_PASSWORD`: Database password (e.g., `yourpassword`)
|
|
42
|
+
|
|
43
|
+
Example `.env` file:
|
|
44
|
+
```
|
|
45
|
+
DB_HOST=localhost
|
|
46
|
+
DB_PORT=5432
|
|
47
|
+
DB_NAME=buildaquery
|
|
48
|
+
DB_USER=postgres
|
|
49
|
+
DB_PASSWORD=yourpassword
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### For Developers
|
|
53
|
+
|
|
54
|
+
Clone the repository and set up the development environment:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
git clone https://github.com/yourusername/buildaquery.git
|
|
58
|
+
cd buildaquery
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Install dependencies using Poetry:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
poetry install
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Activate the virtual environment:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
poetry shell
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Quick Start
|
|
74
|
+
|
|
75
|
+
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).
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from dotenv import load_dotenv
|
|
79
|
+
import os
|
|
80
|
+
from buildaquery.execution.postgres import PostgresExecutor
|
|
81
|
+
from buildaquery.abstract_syntax_tree.models import (
|
|
82
|
+
CreateStatementNode, TableNode, ColumnDefinitionNode,
|
|
83
|
+
InsertStatementNode, ColumnNode, LiteralNode,
|
|
84
|
+
SelectStatementNode, StarNode, DropStatementNode
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Load environment variables
|
|
88
|
+
load_dotenv()
|
|
89
|
+
|
|
90
|
+
# Build connection string from environment variables
|
|
91
|
+
db_host = os.getenv('DB_HOST')
|
|
92
|
+
db_port = os.getenv('DB_PORT')
|
|
93
|
+
db_name = os.getenv('DB_NAME')
|
|
94
|
+
db_user = os.getenv('DB_USER')
|
|
95
|
+
db_password = os.getenv('DB_PASSWORD')
|
|
96
|
+
|
|
97
|
+
connection_string = f"postgresql://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}"
|
|
98
|
+
|
|
99
|
+
# Set up executor with your PostgreSQL connection
|
|
100
|
+
executor = PostgresExecutor(connection_info=connection_string)
|
|
101
|
+
|
|
102
|
+
# Define table
|
|
103
|
+
users_table = TableNode(name="users")
|
|
104
|
+
|
|
105
|
+
# Create table
|
|
106
|
+
create_stmt = CreateStatementNode(
|
|
107
|
+
table=users_table,
|
|
108
|
+
columns=[
|
|
109
|
+
ColumnDefinitionNode(name="id", data_type="SERIAL", primary_key=True),
|
|
110
|
+
ColumnDefinitionNode(name="name", data_type="TEXT", not_null=True),
|
|
111
|
+
ColumnDefinitionNode(name="age", data_type="INTEGER")
|
|
112
|
+
]
|
|
113
|
+
)
|
|
114
|
+
executor.execute(create_stmt)
|
|
115
|
+
|
|
116
|
+
# Insert data
|
|
117
|
+
insert_stmt = InsertStatementNode(
|
|
118
|
+
table=users_table,
|
|
119
|
+
columns=[ColumnNode(name="name"), ColumnNode(name="age")],
|
|
120
|
+
values=[LiteralNode(value="Alice"), LiteralNode(value=30)]
|
|
121
|
+
)
|
|
122
|
+
executor.execute(insert_stmt)
|
|
123
|
+
|
|
124
|
+
# Query data
|
|
125
|
+
select_stmt = SelectStatementNode(
|
|
126
|
+
select_list=[StarNode()], # SELECT *
|
|
127
|
+
from_table=users_table
|
|
128
|
+
)
|
|
129
|
+
results = executor.execute(select_stmt)
|
|
130
|
+
print(results) # [(1, 'Alice', 30)]
|
|
131
|
+
|
|
132
|
+
# Drop table
|
|
133
|
+
drop_stmt = DropStatementNode(table=users_table, if_exists=True)
|
|
134
|
+
executor.execute(drop_stmt)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
For more examples, see the `examples/` directory.
|
|
138
|
+
|
|
139
|
+
## Development Setup
|
|
140
|
+
|
|
141
|
+
### Prerequisites
|
|
142
|
+
|
|
143
|
+
- Python 3.12+
|
|
144
|
+
- Poetry (for dependency management)
|
|
145
|
+
- Docker (for running integration tests)
|
|
146
|
+
|
|
147
|
+
### Setting Up the Environment
|
|
148
|
+
|
|
149
|
+
1. Clone the repository:
|
|
150
|
+
```bash
|
|
151
|
+
git clone https://github.com/yourusername/buildaquery.git
|
|
152
|
+
cd buildaquery
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
2. Install dependencies:
|
|
156
|
+
```bash
|
|
157
|
+
poetry install
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
3. Activate the virtual environment:
|
|
161
|
+
```bash
|
|
162
|
+
poetry shell
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Running Tests
|
|
166
|
+
|
|
167
|
+
#### Unit Tests
|
|
168
|
+
|
|
169
|
+
Run unit tests for all modules:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
poetry run pytest buildaquery/tests
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
#### Integration Tests
|
|
176
|
+
|
|
177
|
+
Integration tests require a PostgreSQL database. Start the test database using Docker:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
docker-compose up -d
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Then run integration tests:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
poetry run pytest tests
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### All Tests
|
|
190
|
+
|
|
191
|
+
Run all tests (unit and integration):
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
poetry run all-tests
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Running Examples
|
|
198
|
+
|
|
199
|
+
Execute the sample script:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
poetry run python examples/sample_query.py
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Project Structure
|
|
206
|
+
|
|
207
|
+
- `buildaquery/abstract_syntax_tree/`: Defines query nodes and AST models.
|
|
208
|
+
- `buildaquery/traversal/`: Base classes for AST traversal (Visitor/Transformer pattern).
|
|
209
|
+
- `buildaquery/compiler/`: Dialect-specific SQL generation (currently PostgreSQL).
|
|
210
|
+
- `buildaquery/execution/`: Database connection and execution logic.
|
|
211
|
+
- `tests/`: Exhaustive unit and integration tests.
|
|
212
|
+
- `examples/`: Practical demonstrations of the library.
|
|
213
|
+
- `scripts/`: Utility scripts for testing and maintenance.
|
|
214
|
+
|
|
215
|
+
## Contributing
|
|
216
|
+
|
|
217
|
+
Contributions are welcome! Please see the contributing guidelines for more information.
|
|
218
|
+
|
|
219
|
+
## License
|
|
220
|
+
|
|
221
|
+
This project is licensed under the MIT License - see the [LICENSE.txt](LICENSE.txt) file for details.
|
|
File without changes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Abstract Syntax Tree (AST)
|
|
2
|
+
|
|
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
|
+
|
|
5
|
+
## Core Concepts
|
|
6
|
+
|
|
7
|
+
The AST is built using a hierarchy of nodes, all inheriting from the base `ASTNode` class.
|
|
8
|
+
|
|
9
|
+
### Node Categories
|
|
10
|
+
|
|
11
|
+
- **`ExpressionNode`**: Represents values, column references, and operations (e.g., `LiteralNode`, `ColumnNode`, `BinaryOperationNode`, `FunctionCallNode`).
|
|
12
|
+
- **`StatementNode`**: Represents complete SQL statements. Currently, `SelectStatementNode` is the primary implementation.
|
|
13
|
+
- **`FromClauseNode`**: Represents entities that can appear in a `FROM` clause, such as `TableNode` or `JoinClauseNode`.
|
|
14
|
+
|
|
15
|
+
## Key Components
|
|
16
|
+
|
|
17
|
+
- **`SelectStatementNode`**: The central node for `SELECT` queries. It encapsulates the select list, `FROM` table/joins, `WHERE` conditions, `GROUP BY`, `HAVING`, `ORDER BY`, and pagination (`LIMIT`, `OFFSET`, `TOP`).
|
|
18
|
+
- **`TopClauseNode`**: A specialized node for limiting results, particularly for dialects that support the `TOP` syntax. It is mutually exclusive with standard `LIMIT`/`OFFSET`.
|
|
19
|
+
- **`JoinClauseNode`**: Represents various types of table joins (INNER, LEFT, etc.) with associated join conditions.
|
|
20
|
+
- **Operations**: Support for both binary (e.g., `+`, `-`, `AND`, `OR`) and unary (e.g., `NOT`, `-`) operations.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
Nodes are typically instantiated and composed to build a tree representing a query. For example:
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from buildaquery.abstract_syntax_tree.models import SelectStatementNode, ColumnNode, TableNode
|
|
28
|
+
|
|
29
|
+
# SELECT * FROM users
|
|
30
|
+
query = SelectStatementNode(
|
|
31
|
+
select_list=[ColumnNode(name="*")],
|
|
32
|
+
from_table=TableNode(name="users")
|
|
33
|
+
)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
This tree can then be processed by visitors (see the `traversal` module) for compilation or analysis.
|
|
37
|
+
|
|
38
|
+
## TODOs
|
|
39
|
+
|
|
40
|
+
To make the AST more robust and capable of representing the full scope of the SQL language, the following enhancements are planned:
|
|
41
|
+
|
|
42
|
+
- **DML Support**:
|
|
43
|
+
- [x] Implement `InsertStatementNode` and `UpdateStatementNode`.
|
|
44
|
+
- [x] Implement `DeleteStatementNode`.
|
|
45
|
+
- **Advanced Query Features**:
|
|
46
|
+
- [x] **CTEs**: Add `WithClauseNode` for common table expressions.
|
|
47
|
+
- [x] **Set Operations**: Implement `UnionNode`, `IntersectNode`, and `ExceptNode`.
|
|
48
|
+
- [x] **DISTINCT**: Add support for `SELECT DISTINCT`.
|
|
49
|
+
- **Richer Expression Logic**:
|
|
50
|
+
- **Specialized Expressions**: Add [x] `CaseExpressionNode`, [x] `InNode`, and [x] `BetweenNode`.
|
|
51
|
+
- [x] **Subqueries**: Enable `SelectStatementNode` to be used within expressions.
|
|
52
|
+
- [x] **Window Functions**: Support `OVER` clauses and partitioning.
|
|
53
|
+
- [x] **Schema & Namespacing**: Update `TableNode` and `ColumnNode` to support qualified names (e.g., `schema.table`) and add a `CastNode` for type casting.
|
|
54
|
+
- **DDL Support**: [x] Add nodes for `CREATE`, `ALTER`, and `DROP` statements for schema management.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .models import *
|