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.
@@ -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