sqlpiston 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.
Files changed (46) hide show
  1. sqlpiston-0.1.0/LICENSE +21 -0
  2. sqlpiston-0.1.0/PKG-INFO +180 -0
  3. sqlpiston-0.1.0/README.md +153 -0
  4. sqlpiston-0.1.0/lib/sqlpiston/__init__.py +27 -0
  5. sqlpiston-0.1.0/lib/sqlpiston/_types.py +5 -0
  6. sqlpiston-0.1.0/lib/sqlpiston/builder/__init__.py +0 -0
  7. sqlpiston-0.1.0/lib/sqlpiston/builder/ddl.py +228 -0
  8. sqlpiston-0.1.0/lib/sqlpiston/builder/dml.py +124 -0
  9. sqlpiston-0.1.0/lib/sqlpiston/builder/nodes.py +248 -0
  10. sqlpiston-0.1.0/lib/sqlpiston/builder/selectable.py +153 -0
  11. sqlpiston-0.1.0/lib/sqlpiston/compiler/__init__.py +0 -0
  12. sqlpiston-0.1.0/lib/sqlpiston/compiler/base.py +581 -0
  13. sqlpiston-0.1.0/lib/sqlpiston/compiler/mysql.py +50 -0
  14. sqlpiston-0.1.0/lib/sqlpiston/compiler/sqlite.py +51 -0
  15. sqlpiston-0.1.0/lib/sqlpiston/core/__init__.py +0 -0
  16. sqlpiston-0.1.0/lib/sqlpiston/core/engine/__init__.py +3 -0
  17. sqlpiston-0.1.0/lib/sqlpiston/core/engine/base.py +99 -0
  18. sqlpiston-0.1.0/lib/sqlpiston/core/engine/mysql.py +80 -0
  19. sqlpiston-0.1.0/lib/sqlpiston/core/engine/sqlite.py +76 -0
  20. sqlpiston-0.1.0/lib/sqlpiston/core/pool.py +49 -0
  21. sqlpiston-0.1.0/lib/sqlpiston/core/session.py +61 -0
  22. sqlpiston-0.1.0/lib/sqlpiston/orm/__init__.py +0 -0
  23. sqlpiston-0.1.0/lib/sqlpiston/orm/mapper.py +68 -0
  24. sqlpiston-0.1.0/lib/sqlpiston.egg-info/PKG-INFO +180 -0
  25. sqlpiston-0.1.0/lib/sqlpiston.egg-info/SOURCES.txt +44 -0
  26. sqlpiston-0.1.0/lib/sqlpiston.egg-info/dependency_links.txt +1 -0
  27. sqlpiston-0.1.0/lib/sqlpiston.egg-info/requires.txt +6 -0
  28. sqlpiston-0.1.0/lib/sqlpiston.egg-info/top_level.txt +1 -0
  29. sqlpiston-0.1.0/pyproject.toml +51 -0
  30. sqlpiston-0.1.0/setup.cfg +4 -0
  31. sqlpiston-0.1.0/tests/test_compiler_coverage.py +354 -0
  32. sqlpiston-0.1.0/tests/test_compiler_ddl.py +231 -0
  33. sqlpiston-0.1.0/tests/test_compiler_mysql.py +230 -0
  34. sqlpiston-0.1.0/tests/test_compiler_sqlite.py +72 -0
  35. sqlpiston-0.1.0/tests/test_coverage_final.py +250 -0
  36. sqlpiston-0.1.0/tests/test_coverage_final2.py +107 -0
  37. sqlpiston-0.1.0/tests/test_coverage_final3.py +42 -0
  38. sqlpiston-0.1.0/tests/test_coverage_gaps.py +187 -0
  39. sqlpiston-0.1.0/tests/test_ddl.py +136 -0
  40. sqlpiston-0.1.0/tests/test_dml.py +86 -0
  41. sqlpiston-0.1.0/tests/test_engine.py +69 -0
  42. sqlpiston-0.1.0/tests/test_final_gaps.py +119 -0
  43. sqlpiston-0.1.0/tests/test_integration.py +156 -0
  44. sqlpiston-0.1.0/tests/test_mapper.py +121 -0
  45. sqlpiston-0.1.0/tests/test_nodes.py +233 -0
  46. sqlpiston-0.1.0/tests/test_selectable.py +182 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 MuliMuri
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,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: sqlpiston
3
+ Version: 0.1.0
4
+ Summary: A low-level SQL library that builds parameterized SQL queries through Python operator overloading
5
+ Author: MuliMuri
6
+ License: MIT
7
+ Keywords: sql,query-builder,orm,mysql,sqlite
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Database
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=8; extra == "dev"
23
+ Requires-Dist: coverage>=7; extra == "dev"
24
+ Requires-Dist: flake8>=7; extra == "dev"
25
+ Requires-Dist: mypy>=1; extra == "dev"
26
+ Dynamic: license-file
27
+
28
+ # SQLPiston
29
+
30
+ [![test](https://github.com/MuliMuri/sqlpiston/actions/workflows/test.yml/badge.svg)](https://github.com/MuliMuri/sqlpiston/actions/workflows/test.yml)
31
+ [![lint](https://github.com/MuliMuri/sqlpiston/actions/workflows/lint.yml/badge.svg)](https://github.com/MuliMuri/sqlpiston/actions/workflows/lint.yml)
32
+ [![coverage](https://codecov.io/gh/MuliMuri/sqlpiston/branch/main/graph/badge.svg)](https://codecov.io/gh/MuliMuri/sqlpiston)
33
+ [![pypi](https://img.shields.io/pypi/v/sqlpiston.svg)](https://pypi.org/project/sqlpiston/)
34
+ [![python](https://img.shields.io/pypi/pyversions/sqlpiston)](https://pypi.org/project/sqlpiston/)
35
+ [![license](https://img.shields.io/github/license/MuliMuri/sqlpiston)](https://github.com/MuliMuri/sqlpiston/blob/main/LICENSE)
36
+
37
+ *Write once, query everywhere — build SQL with Python operators.*
38
+
39
+ [中文版](README_ZH.md)
40
+
41
+ SQLPiston is a low-level SQL library that builds parameterized SQL queries
42
+ through Python operator overloading. AST nodes carry zero SQL knowledge;
43
+ dialect-specific compilers translate the same AST into the right SQL for each
44
+ database.
45
+
46
+ ## Install
47
+
48
+ ```bash
49
+ git clone https://github.com/MuliMuri/sqlpiston.git
50
+ cd sqlpiston
51
+ pip install -e .
52
+ ```
53
+
54
+ Python 3.9+ on Linux / macOS / Windows.
55
+
56
+ ## Basic Usage
57
+
58
+ ```python
59
+ from sqlpiston import Select, Field, Insert
60
+ from sqlpiston import DBEngine, DBType, Session
61
+ from dataclasses import dataclass
62
+
63
+ eng = DBEngine(DBType.SQLite)
64
+ eng.init_engine(":memory:")
65
+ session = Session(eng)
66
+
67
+ stmt = (
68
+ Select()
69
+ .select("id", "name", "age")
70
+ .from_table("users")
71
+ .where((Field("age") >= 18) & (Field("status") == "active"))
72
+ .order_by("id", "ASC")
73
+ .limit(10)
74
+ )
75
+ result = session.execute(stmt)
76
+
77
+ @dataclass
78
+ class User:
79
+ id: int
80
+ name: str
81
+ age: int
82
+
83
+ users = result.map(User)
84
+
85
+ session.execute(
86
+ Insert().into("users").values({"name": "Alice", "age": 25})
87
+ )
88
+
89
+ session.commit()
90
+ session.close()
91
+ ```
92
+
93
+ <details>
94
+ <summary>More examples: JOIN, subquery, CTE, UPSERT, DDL</summary>
95
+
96
+ **JOIN**
97
+
98
+ ```python
99
+ Select() \
100
+ .select("users.name", "orders.total") \
101
+ .from_table("users") \
102
+ .join("orders",
103
+ Field("user_id", "orders") == Field("id", "users")) \
104
+ .where(Field("total", "orders") > 100)
105
+ ```
106
+
107
+ **Subquery (IN / EXISTS / Scalar)**
108
+
109
+ ```python
110
+ # IN subquery
111
+ admin_ids = Select().select("id").from_table("admins")
112
+ stmt = Select().select("name").from_table("users") \
113
+ .where(Field("id").is_in(admin_ids))
114
+
115
+ # EXISTS
116
+ sub = Select().select("1").from_table("orders") \
117
+ .where(Field("user_id", "orders") == Field("id", "users"))
118
+ stmt = Select().select("name").from_table("users").where(sub.exists())
119
+
120
+ # Scalar
121
+ avg = Select().select(SQLFunction("avg", "salary")).from_table("employees")
122
+ stmt = Select().select("name").from_table("staff").where(Field("salary") > avg)
123
+ ```
124
+
125
+ **CTE**
126
+
127
+ ```python
128
+ cte = Select().select("*").from_table("sales") \
129
+ .where(Field("amount") > 100).cte("big_sales")
130
+ stmt = Select().with_(cte).select("*").from_table("big_sales")
131
+ ```
132
+
133
+ **UPSERT — same AST, different SQL per dialect**
134
+
135
+ ```python
136
+ Upsert() \
137
+ .into("users") \
138
+ .values({"id": 1, "name": "X"}) \
139
+ .on_conflict("id") \
140
+ .do_update({"name": "X"})
141
+ ```
142
+
143
+ | Dialect | Generated SQL |
144
+ |---------|--------------|
145
+ | MySQL | `INSERT INTO ... ON DUPLICATE KEY UPDATE` |
146
+ | SQLite | `INSERT INTO ... ON CONFLICT DO UPDATE` |
147
+
148
+ **DDL**
149
+
150
+ ```python
151
+ CreateTable().table("users").if_not_exists() \
152
+ .column("id", "INTEGER", primary_key=True, nullable=False) \
153
+ .column("name", "VARCHAR(255)")
154
+
155
+ AlterTable().table("users").add_column("age", "INTEGER", default=0)
156
+
157
+ DropTable().table("users").if_exists()
158
+ ```
159
+
160
+ </details>
161
+
162
+ ## Highlights
163
+
164
+ - **Operator overloading** — `Field("age") >= 18` builds AST, not a boolean
165
+ - **Dialect-aware** — same AST compiles to MySQL (%s, backticks) or SQLite (?, double-quotes)
166
+ - **Full standard SQL** — SELECT, INSERT, UPDATE, DELETE, UPSERT, CTE, UNION, subqueries, DDL
167
+ - **Parameterized by design** — values never interpolated into SQL strings
168
+ - **Weak ORM** — result-to-dataclass mapping, no identity map or change tracking
169
+
170
+ ## Documentation
171
+
172
+ ```bash
173
+ cd docs
174
+ pip install -r requirements.txt
175
+ sphinx-build -b html source build/html
176
+ ```
177
+
178
+ ## License
179
+
180
+ MIT
@@ -0,0 +1,153 @@
1
+ # SQLPiston
2
+
3
+ [![test](https://github.com/MuliMuri/sqlpiston/actions/workflows/test.yml/badge.svg)](https://github.com/MuliMuri/sqlpiston/actions/workflows/test.yml)
4
+ [![lint](https://github.com/MuliMuri/sqlpiston/actions/workflows/lint.yml/badge.svg)](https://github.com/MuliMuri/sqlpiston/actions/workflows/lint.yml)
5
+ [![coverage](https://codecov.io/gh/MuliMuri/sqlpiston/branch/main/graph/badge.svg)](https://codecov.io/gh/MuliMuri/sqlpiston)
6
+ [![pypi](https://img.shields.io/pypi/v/sqlpiston.svg)](https://pypi.org/project/sqlpiston/)
7
+ [![python](https://img.shields.io/pypi/pyversions/sqlpiston)](https://pypi.org/project/sqlpiston/)
8
+ [![license](https://img.shields.io/github/license/MuliMuri/sqlpiston)](https://github.com/MuliMuri/sqlpiston/blob/main/LICENSE)
9
+
10
+ *Write once, query everywhere — build SQL with Python operators.*
11
+
12
+ [中文版](README_ZH.md)
13
+
14
+ SQLPiston is a low-level SQL library that builds parameterized SQL queries
15
+ through Python operator overloading. AST nodes carry zero SQL knowledge;
16
+ dialect-specific compilers translate the same AST into the right SQL for each
17
+ database.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ git clone https://github.com/MuliMuri/sqlpiston.git
23
+ cd sqlpiston
24
+ pip install -e .
25
+ ```
26
+
27
+ Python 3.9+ on Linux / macOS / Windows.
28
+
29
+ ## Basic Usage
30
+
31
+ ```python
32
+ from sqlpiston import Select, Field, Insert
33
+ from sqlpiston import DBEngine, DBType, Session
34
+ from dataclasses import dataclass
35
+
36
+ eng = DBEngine(DBType.SQLite)
37
+ eng.init_engine(":memory:")
38
+ session = Session(eng)
39
+
40
+ stmt = (
41
+ Select()
42
+ .select("id", "name", "age")
43
+ .from_table("users")
44
+ .where((Field("age") >= 18) & (Field("status") == "active"))
45
+ .order_by("id", "ASC")
46
+ .limit(10)
47
+ )
48
+ result = session.execute(stmt)
49
+
50
+ @dataclass
51
+ class User:
52
+ id: int
53
+ name: str
54
+ age: int
55
+
56
+ users = result.map(User)
57
+
58
+ session.execute(
59
+ Insert().into("users").values({"name": "Alice", "age": 25})
60
+ )
61
+
62
+ session.commit()
63
+ session.close()
64
+ ```
65
+
66
+ <details>
67
+ <summary>More examples: JOIN, subquery, CTE, UPSERT, DDL</summary>
68
+
69
+ **JOIN**
70
+
71
+ ```python
72
+ Select() \
73
+ .select("users.name", "orders.total") \
74
+ .from_table("users") \
75
+ .join("orders",
76
+ Field("user_id", "orders") == Field("id", "users")) \
77
+ .where(Field("total", "orders") > 100)
78
+ ```
79
+
80
+ **Subquery (IN / EXISTS / Scalar)**
81
+
82
+ ```python
83
+ # IN subquery
84
+ admin_ids = Select().select("id").from_table("admins")
85
+ stmt = Select().select("name").from_table("users") \
86
+ .where(Field("id").is_in(admin_ids))
87
+
88
+ # EXISTS
89
+ sub = Select().select("1").from_table("orders") \
90
+ .where(Field("user_id", "orders") == Field("id", "users"))
91
+ stmt = Select().select("name").from_table("users").where(sub.exists())
92
+
93
+ # Scalar
94
+ avg = Select().select(SQLFunction("avg", "salary")).from_table("employees")
95
+ stmt = Select().select("name").from_table("staff").where(Field("salary") > avg)
96
+ ```
97
+
98
+ **CTE**
99
+
100
+ ```python
101
+ cte = Select().select("*").from_table("sales") \
102
+ .where(Field("amount") > 100).cte("big_sales")
103
+ stmt = Select().with_(cte).select("*").from_table("big_sales")
104
+ ```
105
+
106
+ **UPSERT — same AST, different SQL per dialect**
107
+
108
+ ```python
109
+ Upsert() \
110
+ .into("users") \
111
+ .values({"id": 1, "name": "X"}) \
112
+ .on_conflict("id") \
113
+ .do_update({"name": "X"})
114
+ ```
115
+
116
+ | Dialect | Generated SQL |
117
+ |---------|--------------|
118
+ | MySQL | `INSERT INTO ... ON DUPLICATE KEY UPDATE` |
119
+ | SQLite | `INSERT INTO ... ON CONFLICT DO UPDATE` |
120
+
121
+ **DDL**
122
+
123
+ ```python
124
+ CreateTable().table("users").if_not_exists() \
125
+ .column("id", "INTEGER", primary_key=True, nullable=False) \
126
+ .column("name", "VARCHAR(255)")
127
+
128
+ AlterTable().table("users").add_column("age", "INTEGER", default=0)
129
+
130
+ DropTable().table("users").if_exists()
131
+ ```
132
+
133
+ </details>
134
+
135
+ ## Highlights
136
+
137
+ - **Operator overloading** — `Field("age") >= 18` builds AST, not a boolean
138
+ - **Dialect-aware** — same AST compiles to MySQL (%s, backticks) or SQLite (?, double-quotes)
139
+ - **Full standard SQL** — SELECT, INSERT, UPDATE, DELETE, UPSERT, CTE, UNION, subqueries, DDL
140
+ - **Parameterized by design** — values never interpolated into SQL strings
141
+ - **Weak ORM** — result-to-dataclass mapping, no identity map or change tracking
142
+
143
+ ## Documentation
144
+
145
+ ```bash
146
+ cd docs
147
+ pip install -r requirements.txt
148
+ sphinx-build -b html source build/html
149
+ ```
150
+
151
+ ## License
152
+
153
+ MIT
@@ -0,0 +1,27 @@
1
+ from sqlpiston._types import ColumnValue, SQLValue
2
+ from sqlpiston.builder.ddl import (
3
+ AlterTable, ColumnDef, CreateIndex, CreateTable, CreateView,
4
+ DropIndex, DropTable, DropView, Truncate,
5
+ )
6
+ from sqlpiston.builder.dml import Delete, Insert, Update, Upsert
7
+ from sqlpiston.builder.nodes import CaseNode, Field, SQLFunction
8
+ from sqlpiston.builder.selectable import CTE, CompoundSelect, Select
9
+ from sqlpiston.compiler.base import Dialect
10
+ from sqlpiston.core.engine import DBType
11
+ from sqlpiston.core.engine.base import DBEngine
12
+ from sqlpiston.core.pool import ConnectionPool
13
+ from sqlpiston.core.session import Session
14
+ from sqlpiston.orm.mapper import ResultSet
15
+
16
+ __all__ = [
17
+ 'SQLValue', 'ColumnValue',
18
+ 'AlterTable', 'ColumnDef', 'CreateIndex', 'CreateTable', 'CreateView',
19
+ 'DropIndex', 'DropTable', 'DropView', 'Truncate',
20
+ 'Delete', 'Insert', 'Update', 'Upsert',
21
+ 'CaseNode', 'Field', 'SQLFunction',
22
+ 'CTE', 'CompoundSelect', 'Select',
23
+ 'Dialect',
24
+ 'DBType', 'DBEngine',
25
+ 'ConnectionPool', 'Session',
26
+ 'ResultSet',
27
+ ]
@@ -0,0 +1,5 @@
1
+ from typing import TypeAlias, Union
2
+
3
+
4
+ SQLValue: TypeAlias = Union[str, int, float, bool, None, bytes]
5
+ ColumnValue = SQLValue
File without changes
@@ -0,0 +1,228 @@
1
+ from dataclasses import dataclass
2
+ from enum import Enum
3
+ from typing import TYPE_CHECKING, List, Optional, Tuple
4
+
5
+ from sqlpiston.builder.nodes import ASTNode
6
+ from sqlpiston._types import SQLValue
7
+
8
+ if TYPE_CHECKING:
9
+ from sqlpiston.builder.selectable import Select
10
+
11
+
12
+ class AlterAction(Enum):
13
+ ADD = 1
14
+ DROP = 2
15
+ MODIFY = 3
16
+
17
+
18
+ @dataclass
19
+ class ColumnDef:
20
+ """Column definition for CREATE TABLE."""
21
+ name: str
22
+ type_: str
23
+ nullable: bool = True
24
+ primary_key: bool = False
25
+ default: Optional[SQLValue] = None
26
+ unique: bool = False
27
+
28
+
29
+ class CreateTable(ASTNode):
30
+ """CREATE TABLE name (col1 TYPE, col2 TYPE, ...)"""
31
+
32
+ def __init__(self) -> None:
33
+ self._table: Optional[str] = None
34
+ self._if_not_exists: bool = False
35
+ self._columns: List[ColumnDef] = []
36
+
37
+ def table(self, name: str) -> 'CreateTable':
38
+ self._table = name
39
+ return self
40
+
41
+ def if_not_exists(self) -> 'CreateTable':
42
+ self._if_not_exists = True
43
+ return self
44
+
45
+ def column(self, name: str, type_: str, *,
46
+ nullable: bool = True,
47
+ primary_key: bool = False,
48
+ default: Optional[SQLValue] = None,
49
+ unique: bool = False) -> 'CreateTable':
50
+ self._columns.append(ColumnDef(
51
+ name=name, type_=type_, nullable=nullable,
52
+ primary_key=primary_key, default=default, unique=unique,
53
+ ))
54
+ return self
55
+
56
+ def columns(self, *col_defs: ColumnDef) -> 'CreateTable':
57
+ self._columns.extend(col_defs)
58
+ return self
59
+
60
+ def __repr__(self) -> str:
61
+ return f"CreateTable(table={self._table!r}, columns={len(self._columns)})"
62
+
63
+
64
+ class AlterTable(ASTNode):
65
+ """ALTER TABLE name ADD/DROP/MODIFY COLUMN ..."""
66
+
67
+ def __init__(self) -> None:
68
+ self._table: Optional[str] = None
69
+ self._actions: List[Tuple[AlterAction, str, Optional[str], Optional[ColumnDef]]] = []
70
+
71
+ def table(self, name: str) -> 'AlterTable':
72
+ self._table = name
73
+ return self
74
+
75
+ def add_column(self, name: str, type_: str, *,
76
+ nullable: bool = True,
77
+ default: Optional[SQLValue] = None) -> 'AlterTable':
78
+ col_def = ColumnDef(name=name, type_=type_, nullable=nullable, default=default)
79
+ self._actions.append((AlterAction.ADD, name, type_, col_def))
80
+ return self
81
+
82
+ def drop_column(self, name: str) -> 'AlterTable':
83
+ self._actions.append((AlterAction.DROP, name, None, None))
84
+ return self
85
+
86
+ def modify_column(self, name: str, type_: str, *,
87
+ nullable: bool = True,
88
+ default: Optional[SQLValue] = None) -> 'AlterTable':
89
+ col_def = ColumnDef(name=name, type_=type_, nullable=nullable, default=default)
90
+ self._actions.append((AlterAction.MODIFY, name, type_, col_def))
91
+ return self
92
+
93
+ def __repr__(self) -> str:
94
+ return f"AlterTable(table={self._table!r}, actions={len(self._actions)})"
95
+
96
+
97
+ class DropTable(ASTNode):
98
+ """DROP TABLE [IF EXISTS] name"""
99
+
100
+ def __init__(self) -> None:
101
+ self._table: Optional[str] = None
102
+ self._if_exists: bool = False
103
+
104
+ def table(self, name: str) -> 'DropTable':
105
+ self._table = name
106
+ return self
107
+
108
+ def if_exists(self) -> 'DropTable':
109
+ self._if_exists = True
110
+ return self
111
+
112
+ def __repr__(self) -> str:
113
+ return f"DropTable(table={self._table!r}, if_exists={self._if_exists})"
114
+
115
+
116
+ class CreateIndex(ASTNode):
117
+ """CREATE [UNIQUE] INDEX [IF NOT EXISTS] name ON table (col1, col2, ...)"""
118
+
119
+ def __init__(self) -> None:
120
+ self._name: Optional[str] = None
121
+ self._table: Optional[str] = None
122
+ self._columns: Tuple[str, ...] = ()
123
+ self._unique: bool = False
124
+ self._if_not_exists: bool = False
125
+
126
+ def name(self, idx_name: str) -> 'CreateIndex':
127
+ self._name = idx_name
128
+ return self
129
+
130
+ def on(self, table: str) -> 'CreateIndex':
131
+ self._table = table
132
+ return self
133
+
134
+ def columns(self, *cols: str) -> 'CreateIndex':
135
+ self._columns = cols
136
+ return self
137
+
138
+ def unique(self) -> 'CreateIndex':
139
+ self._unique = True
140
+ return self
141
+
142
+ def if_not_exists(self) -> 'CreateIndex':
143
+ self._if_not_exists = True
144
+ return self
145
+
146
+ def __repr__(self) -> str:
147
+ return f"CreateIndex(name={self._name!r}, table={self._table!r})"
148
+
149
+
150
+ class DropIndex(ASTNode):
151
+ """DROP INDEX [IF EXISTS] name ON table"""
152
+
153
+ def __init__(self) -> None:
154
+ self._name: Optional[str] = None
155
+ self._table: Optional[str] = None
156
+ self._if_exists: bool = False
157
+
158
+ def name(self, idx_name: str) -> 'DropIndex':
159
+ self._name = idx_name
160
+ return self
161
+
162
+ def on(self, table: str) -> 'DropIndex':
163
+ self._table = table
164
+ return self
165
+
166
+ def if_exists(self) -> 'DropIndex':
167
+ self._if_exists = True
168
+ return self
169
+
170
+ def __repr__(self) -> str:
171
+ return f"DropIndex(name={self._name!r}, table={self._table!r})"
172
+
173
+
174
+ class CreateView(ASTNode):
175
+ """CREATE VIEW [IF NOT EXISTS] name AS SELECT ..."""
176
+
177
+ def __init__(self) -> None:
178
+ self._name: Optional[str] = None
179
+ self._select: Optional['Select'] = None
180
+ self._if_not_exists: bool = False
181
+
182
+ def name(self, view_name: str) -> 'CreateView':
183
+ self._name = view_name
184
+ return self
185
+
186
+ def as_(self, select: 'Select') -> 'CreateView':
187
+ self._select = select
188
+ return self
189
+
190
+ def if_not_exists(self) -> 'CreateView':
191
+ self._if_not_exists = True
192
+ return self
193
+
194
+ def __repr__(self) -> str:
195
+ return f"CreateView(name={self._name!r})"
196
+
197
+
198
+ class DropView(ASTNode):
199
+ """DROP VIEW [IF EXISTS] name"""
200
+
201
+ def __init__(self) -> None:
202
+ self._name: Optional[str] = None
203
+ self._if_exists: bool = False
204
+
205
+ def name(self, view_name: str) -> 'DropView':
206
+ self._name = view_name
207
+ return self
208
+
209
+ def if_exists(self) -> 'DropView':
210
+ self._if_exists = True
211
+ return self
212
+
213
+ def __repr__(self) -> str:
214
+ return f"DropView(name={self._name!r})"
215
+
216
+
217
+ class Truncate(ASTNode):
218
+ """TRUNCATE TABLE name"""
219
+
220
+ def __init__(self) -> None:
221
+ self._table: Optional[str] = None
222
+
223
+ def table(self, name: str) -> 'Truncate':
224
+ self._table = name
225
+ return self
226
+
227
+ def __repr__(self) -> str:
228
+ return f"Truncate(table={self._table!r})"