1# sql/roles.py
2# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7
8from .. import util
9
10
11class SQLRole(object):
12 """Define a "role" within a SQL statement structure.
13
14 Classes within SQL Core participate within SQLRole hierarchies in order
15 to more accurately indicate where they may be used within SQL statements
16 of all types.
17
18 .. versionadded:: 1.4
19
20 """
21
22 allows_lambda = False
23 uses_inspection = False
24
25
26class UsesInspection(object):
27 _post_inspect = None
28 uses_inspection = True
29
30
31class AllowsLambdaRole(object):
32 allows_lambda = True
33
34
35class HasCacheKeyRole(SQLRole):
36 _role_name = "Cacheable Core or ORM object"
37
38
39class ExecutableOptionRole(SQLRole):
40 __slots__ = ()
41 _role_name = "ExecutionOption Core or ORM object"
42
43
44class LiteralValueRole(SQLRole):
45 _role_name = "Literal Python value"
46
47
48class ColumnArgumentRole(SQLRole):
49 _role_name = "Column expression"
50
51
52class ColumnArgumentOrKeyRole(ColumnArgumentRole):
53 _role_name = "Column expression or string key"
54
55
56class StrAsPlainColumnRole(ColumnArgumentRole):
57 _role_name = "Column expression or string key"
58
59
60class ColumnListRole(SQLRole):
61 """Elements suitable for forming comma separated lists of expressions."""
62
63
64class TruncatedLabelRole(SQLRole):
65 _role_name = "String SQL identifier"
66
67
68class ColumnsClauseRole(AllowsLambdaRole, UsesInspection, ColumnListRole):
69 _role_name = "Column expression or FROM clause"
70
71 @property
72 def _select_iterable(self):
73 raise NotImplementedError()
74
75
76class LimitOffsetRole(SQLRole):
77 _role_name = "LIMIT / OFFSET expression"
78
79
80class ByOfRole(ColumnListRole):
81 _role_name = "GROUP BY / OF / etc. expression"
82
83
84class GroupByRole(AllowsLambdaRole, UsesInspection, ByOfRole):
85 # note there's a special case right now where you can pass a whole
86 # ORM entity to group_by() and it splits out. we may not want to keep
87 # this around
88
89 _role_name = "GROUP BY expression"
90
91
92class OrderByRole(AllowsLambdaRole, ByOfRole):
93 _role_name = "ORDER BY expression"
94
95
96class StructuralRole(SQLRole):
97 pass
98
99
100class StatementOptionRole(StructuralRole):
101 _role_name = "statement sub-expression element"
102
103
104class OnClauseRole(AllowsLambdaRole, StructuralRole):
105 _role_name = "SQL expression for ON clause"
106
107
108class WhereHavingRole(OnClauseRole):
109 _role_name = "SQL expression for WHERE/HAVING role"
110
111
112class ExpressionElementRole(SQLRole):
113 _role_name = "SQL expression element"
114
115
116class ConstExprRole(ExpressionElementRole):
117 _role_name = "Constant True/False/None expression"
118
119
120class LabeledColumnExprRole(ExpressionElementRole):
121 pass
122
123
124class BinaryElementRole(ExpressionElementRole):
125 _role_name = "SQL expression element or literal value"
126
127
128class InElementRole(SQLRole):
129 _role_name = (
130 "IN expression list, SELECT construct, or bound parameter object"
131 )
132
133
134class JoinTargetRole(AllowsLambdaRole, UsesInspection, StructuralRole):
135 _role_name = (
136 "Join target, typically a FROM expression, or ORM "
137 "relationship attribute"
138 )
139
140
141class FromClauseRole(ColumnsClauseRole, JoinTargetRole):
142 _role_name = "FROM expression, such as a Table or alias() object"
143
144 _is_subquery = False
145
146 @property
147 def _hide_froms(self):
148 raise NotImplementedError()
149
150
151class StrictFromClauseRole(FromClauseRole):
152 # does not allow text() or select() objects
153
154 @property
155 def description(self):
156 raise NotImplementedError()
157
158
159class AnonymizedFromClauseRole(StrictFromClauseRole):
160 # calls .alias() as a post processor
161
162 def _anonymous_fromclause(self, name=None, flat=False):
163 raise NotImplementedError()
164
165
166class ReturnsRowsRole(SQLRole):
167 _role_name = (
168 "Row returning expression such as a SELECT, a FROM clause, or an "
169 "INSERT/UPDATE/DELETE with RETURNING"
170 )
171
172
173class StatementRole(SQLRole):
174 _role_name = "Executable SQL or text() construct"
175
176 _propagate_attrs = util.immutabledict()
177
178
179class SelectStatementRole(StatementRole, ReturnsRowsRole):
180 _role_name = "SELECT construct or equivalent text() construct"
181
182 def subquery(self):
183 raise NotImplementedError(
184 "All SelectStatementRole objects should implement a "
185 ".subquery() method."
186 )
187
188
189class HasCTERole(ReturnsRowsRole):
190 pass
191
192
193class IsCTERole(SQLRole):
194 _role_name = "CTE object"
195
196
197class CompoundElementRole(AllowsLambdaRole, SQLRole):
198 """SELECT statements inside a CompoundSelect, e.g. UNION, EXTRACT, etc."""
199
200 _role_name = (
201 "SELECT construct for inclusion in a UNION or other set construct"
202 )
203
204
205# TODO: are we using this?
206class DMLRole(StatementRole):
207 pass
208
209
210class DMLTableRole(FromClauseRole):
211 _role_name = "subject table for an INSERT, UPDATE or DELETE"
212
213
214class DMLColumnRole(SQLRole):
215 _role_name = "SET/VALUES column expression or string key"
216
217
218class DMLSelectRole(SQLRole):
219 """A SELECT statement embedded in DML, typically INSERT from SELECT"""
220
221 _role_name = "SELECT statement or equivalent textual object"
222
223
224class DDLRole(StatementRole):
225 pass
226
227
228class DDLExpressionRole(StructuralRole):
229 _role_name = "SQL expression element for DDL constraint"
230
231
232class DDLConstraintColumnRole(SQLRole):
233 _role_name = "String column name or column expression for DDL constraint"
234
235
236class DDLReferredColumnRole(DDLConstraintColumnRole):
237 _role_name = (
238 "String column name or Column object for DDL foreign key constraint"
239 )