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