sqlpiston 0.1.0__py3-none-any.whl
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.
- sqlpiston/__init__.py +27 -0
- sqlpiston/_types.py +5 -0
- sqlpiston/builder/__init__.py +0 -0
- sqlpiston/builder/ddl.py +228 -0
- sqlpiston/builder/dml.py +124 -0
- sqlpiston/builder/nodes.py +248 -0
- sqlpiston/builder/selectable.py +153 -0
- sqlpiston/compiler/__init__.py +0 -0
- sqlpiston/compiler/base.py +581 -0
- sqlpiston/compiler/mysql.py +50 -0
- sqlpiston/compiler/sqlite.py +51 -0
- sqlpiston/core/__init__.py +0 -0
- sqlpiston/core/engine/__init__.py +3 -0
- sqlpiston/core/engine/base.py +99 -0
- sqlpiston/core/engine/mysql.py +80 -0
- sqlpiston/core/engine/sqlite.py +76 -0
- sqlpiston/core/pool.py +49 -0
- sqlpiston/core/session.py +61 -0
- sqlpiston/orm/__init__.py +0 -0
- sqlpiston/orm/mapper.py +68 -0
- sqlpiston-0.1.0.dist-info/METADATA +180 -0
- sqlpiston-0.1.0.dist-info/RECORD +25 -0
- sqlpiston-0.1.0.dist-info/WHEEL +5 -0
- sqlpiston-0.1.0.dist-info/licenses/LICENSE +21 -0
- sqlpiston-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, List, Literal, Optional, Tuple, Union
|
|
2
|
+
|
|
3
|
+
from sqlpiston.builder.nodes import (
|
|
4
|
+
ASTNode, ExistsNode, Field, SQLFunction,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
pass # all types needed are in nodes.py already
|
|
9
|
+
|
|
10
|
+
# SelectColumn is already defined via Field/SQLFunction/Select
|
|
11
|
+
# We extend the concept here: a column in a SELECT clause can be str, Field, SQLFunction, or Select (scalar subquery)
|
|
12
|
+
SelectColumn = Union[str, Field, SQLFunction, 'Select']
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Select(ASTNode):
|
|
16
|
+
"""Fluent SELECT builder. All modifiers return self for chaining.
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
Select().select("id", "name").from_table("users").where(Field("age") >= 18)
|
|
20
|
+
|
|
21
|
+
Subquery usage:
|
|
22
|
+
sub = Select().select("id").from_table("users").as_("u")
|
|
23
|
+
Select().select("*").from_table(sub)
|
|
24
|
+
|
|
25
|
+
Scalar subquery in WHERE:
|
|
26
|
+
sub = Select().select(SQLFunction("avg", "salary")).from_table("employees")
|
|
27
|
+
Select().select("name").from_table("staff").where(Field("salary") > sub)
|
|
28
|
+
|
|
29
|
+
EXISTS:
|
|
30
|
+
sub = Select().select("1").from_table("orders").where(...)
|
|
31
|
+
Select().select("name").from_table("users").where(sub.exists())
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self) -> None:
|
|
35
|
+
self._columns: List[SelectColumn] = []
|
|
36
|
+
self._distinct: bool = False
|
|
37
|
+
self._from: Optional[Union[str, 'Select']] = None
|
|
38
|
+
self._where: Optional[ASTNode] = None
|
|
39
|
+
self._joins: List[Tuple[str, str, ASTNode]] = []
|
|
40
|
+
self._group_by: List[Union[str, Field]] = []
|
|
41
|
+
self._having: Optional[ASTNode] = None
|
|
42
|
+
self._order_by: List[Tuple[Union[str, Field], str]] = []
|
|
43
|
+
self._limit: Optional[int] = None
|
|
44
|
+
self._offset: Optional[int] = None
|
|
45
|
+
self._alias: Optional[str] = None
|
|
46
|
+
self._ctes: Optional[List['CTE']] = None
|
|
47
|
+
|
|
48
|
+
def select(self, *columns: SelectColumn) -> 'Select':
|
|
49
|
+
self._columns.extend(columns)
|
|
50
|
+
return self
|
|
51
|
+
|
|
52
|
+
def distinct(self) -> 'Select':
|
|
53
|
+
self._distinct = True
|
|
54
|
+
return self
|
|
55
|
+
|
|
56
|
+
def from_table(self, table: Union[str, 'Select']) -> 'Select':
|
|
57
|
+
self._from = table
|
|
58
|
+
return self
|
|
59
|
+
|
|
60
|
+
def where(self, condition: ASTNode) -> 'Select':
|
|
61
|
+
self._where = condition
|
|
62
|
+
return self
|
|
63
|
+
|
|
64
|
+
def join(self, table: str, on: ASTNode,
|
|
65
|
+
how: Literal['INNER', 'LEFT', 'RIGHT', 'CROSS'] = 'INNER') -> 'Select':
|
|
66
|
+
self._joins.append((table, how, on))
|
|
67
|
+
return self
|
|
68
|
+
|
|
69
|
+
def left_join(self, table: str, on: ASTNode) -> 'Select':
|
|
70
|
+
return self.join(table, on, how='LEFT')
|
|
71
|
+
|
|
72
|
+
def right_join(self, table: str, on: ASTNode) -> 'Select':
|
|
73
|
+
return self.join(table, on, how='RIGHT')
|
|
74
|
+
|
|
75
|
+
def cross_join(self, table: str) -> 'Select':
|
|
76
|
+
self._joins.append((table, 'CROSS', None)) # type: ignore[arg-type] # CROSS JOIN has no ON
|
|
77
|
+
return self
|
|
78
|
+
|
|
79
|
+
def group_by(self, *fields: Union[str, Field]) -> 'Select':
|
|
80
|
+
self._group_by.extend(fields)
|
|
81
|
+
return self
|
|
82
|
+
|
|
83
|
+
def having(self, condition: ASTNode) -> 'Select':
|
|
84
|
+
self._having = condition
|
|
85
|
+
return self
|
|
86
|
+
|
|
87
|
+
def order_by(self, field: Union[str, Field],
|
|
88
|
+
direction: Literal['ASC', 'DESC'] = 'ASC') -> 'Select':
|
|
89
|
+
self._order_by.append((field, direction))
|
|
90
|
+
return self
|
|
91
|
+
|
|
92
|
+
def limit(self, count: int) -> 'Select':
|
|
93
|
+
self._limit = count
|
|
94
|
+
return self
|
|
95
|
+
|
|
96
|
+
def offset(self, start: int) -> 'Select':
|
|
97
|
+
self._offset = start
|
|
98
|
+
return self
|
|
99
|
+
|
|
100
|
+
def as_(self, alias: str) -> 'Select':
|
|
101
|
+
self._alias = alias
|
|
102
|
+
return self
|
|
103
|
+
|
|
104
|
+
def exists(self) -> ExistsNode:
|
|
105
|
+
return ExistsNode(self, negated=False)
|
|
106
|
+
|
|
107
|
+
def not_exists(self) -> ExistsNode:
|
|
108
|
+
return ExistsNode(self, negated=True)
|
|
109
|
+
|
|
110
|
+
def union(self, other: 'Select') -> 'CompoundSelect':
|
|
111
|
+
return CompoundSelect('UNION', self, other)
|
|
112
|
+
|
|
113
|
+
def union_all(self, other: 'Select') -> 'CompoundSelect':
|
|
114
|
+
return CompoundSelect('UNION ALL', self, other)
|
|
115
|
+
|
|
116
|
+
def intersect(self, other: 'Select') -> 'CompoundSelect':
|
|
117
|
+
return CompoundSelect('INTERSECT', self, other)
|
|
118
|
+
|
|
119
|
+
def except_(self, other: 'Select') -> 'CompoundSelect':
|
|
120
|
+
return CompoundSelect('EXCEPT', self, other)
|
|
121
|
+
|
|
122
|
+
def cte(self, name: str) -> 'CTE':
|
|
123
|
+
return CTE(name, self)
|
|
124
|
+
|
|
125
|
+
def with_(self, *ctes: 'CTE') -> 'Select':
|
|
126
|
+
self._ctes = list(ctes)
|
|
127
|
+
return self
|
|
128
|
+
|
|
129
|
+
def __repr__(self) -> str:
|
|
130
|
+
return f"Select(columns={self._columns}, from={self._from!r}, alias={self._alias!r})"
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class CompoundSelect(ASTNode):
|
|
134
|
+
"""Combine two Selects: UNION / UNION ALL / INTERSECT / EXCEPT."""
|
|
135
|
+
|
|
136
|
+
def __init__(self, operator: str, left: Select, right: Select) -> None:
|
|
137
|
+
self.operator = operator
|
|
138
|
+
self.left = left
|
|
139
|
+
self.right = right
|
|
140
|
+
|
|
141
|
+
def __repr__(self) -> str:
|
|
142
|
+
return f"CompoundSelect({self.operator})"
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class CTE(ASTNode):
|
|
146
|
+
"""CTE node: name AS (SELECT ...)"""
|
|
147
|
+
|
|
148
|
+
def __init__(self, name: str, select: Select) -> None:
|
|
149
|
+
self.name = name
|
|
150
|
+
self.select = select
|
|
151
|
+
|
|
152
|
+
def __repr__(self) -> str:
|
|
153
|
+
return f"CTE({self.name})"
|
|
File without changes
|