sqlleaf 0.0.1__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.
- sqlleaf/__init__.py +4 -0
- sqlleaf/dialects/__init__.py +0 -0
- sqlleaf/dialects/plpgsql/__init__.py +0 -0
- sqlleaf/dialects/plpgsql/p_classes.py +275 -0
- sqlleaf/dialects/plpgsql/p_dialect.py +148 -0
- sqlleaf/dialects/plpgsql/p_generator.py +538 -0
- sqlleaf/dialects/plpgsql/p_parser.py +1244 -0
- sqlleaf/dialects/plpgsql/p_tokenizer.py +41 -0
- sqlleaf/exception.py +67 -0
- sqlleaf/lineage.py +326 -0
- sqlleaf/mappings.py +376 -0
- sqlleaf/models/__init__.py +0 -0
- sqlleaf/models/context.py +153 -0
- sqlleaf/models/hook.py +9 -0
- sqlleaf/models/node/__init__.py +27 -0
- sqlleaf/models/node/base.py +148 -0
- sqlleaf/models/node/column.py +345 -0
- sqlleaf/models/node/dynamodb.py +39 -0
- sqlleaf/models/node/function.py +33 -0
- sqlleaf/models/node/interval.py +21 -0
- sqlleaf/models/node/json.py +57 -0
- sqlleaf/models/node/literal.py +17 -0
- sqlleaf/models/node/null.py +14 -0
- sqlleaf/models/node/pivot.py +34 -0
- sqlleaf/models/node/program.py +27 -0
- sqlleaf/models/node/sequence.py +18 -0
- sqlleaf/models/node/star.py +11 -0
- sqlleaf/models/node/stream.py +14 -0
- sqlleaf/models/node/user_defined_function.py +33 -0
- sqlleaf/models/node/var.py +7 -0
- sqlleaf/models/node/variable.py +8 -0
- sqlleaf/models/node/window.py +22 -0
- sqlleaf/models/query/__init__.py +33 -0
- sqlleaf/models/query/base.py +248 -0
- sqlleaf/models/query/call.py +111 -0
- sqlleaf/models/query/copy.py +144 -0
- sqlleaf/models/query/ctas.py +61 -0
- sqlleaf/models/query/database.py +48 -0
- sqlleaf/models/query/delete.py +26 -0
- sqlleaf/models/query/execute.py +66 -0
- sqlleaf/models/query/holder.py +49 -0
- sqlleaf/models/query/insert.py +69 -0
- sqlleaf/models/query/merge.py +29 -0
- sqlleaf/models/query/multitable_insert.py +42 -0
- sqlleaf/models/query/prepare.py +61 -0
- sqlleaf/models/query/procedure.py +72 -0
- sqlleaf/models/query/put.py +38 -0
- sqlleaf/models/query/replace.py +32 -0
- sqlleaf/models/query/schema.py +47 -0
- sqlleaf/models/query/select.py +27 -0
- sqlleaf/models/query/sequence.py +33 -0
- sqlleaf/models/query/set.py +74 -0
- sqlleaf/models/query/stage.py +78 -0
- sqlleaf/models/query/table.py +220 -0
- sqlleaf/models/query/trigger.py +42 -0
- sqlleaf/models/query/type.py +42 -0
- sqlleaf/models/query/unload.py +64 -0
- sqlleaf/models/query/update.py +45 -0
- sqlleaf/models/query/user_defined_function.py +297 -0
- sqlleaf/models/query/values.py +20 -0
- sqlleaf/models/query/view.py +41 -0
- sqlleaf/path.py +194 -0
- sqlleaf/processors/__init__.py +0 -0
- sqlleaf/processors/collector/__init__.py +0 -0
- sqlleaf/processors/collector/collector.py +827 -0
- sqlleaf/processors/collector/substitute.py +396 -0
- sqlleaf/processors/generator/__init__.py +0 -0
- sqlleaf/processors/generator/dialects/__init__.py +5 -0
- sqlleaf/processors/generator/dialects/athena.py +45 -0
- sqlleaf/processors/generator/dialects/base.py +572 -0
- sqlleaf/processors/generator/dialects/mysql.py +19 -0
- sqlleaf/processors/generator/dialects/postgres.py +182 -0
- sqlleaf/processors/generator/dialects/redshift.py +192 -0
- sqlleaf/processors/generator/dialects/snowflake.py +90 -0
- sqlleaf/processors/generator/generator.py +495 -0
- sqlleaf/processors/transformer/__init__.py +31 -0
- sqlleaf/processors/transformer/base.py +899 -0
- sqlleaf/processors/transformer/call.py +9 -0
- sqlleaf/processors/transformer/copy.py +63 -0
- sqlleaf/processors/transformer/ctas.py +39 -0
- sqlleaf/processors/transformer/delete.py +14 -0
- sqlleaf/processors/transformer/execute.py +9 -0
- sqlleaf/processors/transformer/expressions/__init__.py +11 -0
- sqlleaf/processors/transformer/expressions/functional_notation.py +22 -0
- sqlleaf/processors/transformer/expressions/row.py +451 -0
- sqlleaf/processors/transformer/expressions/values.py +318 -0
- sqlleaf/processors/transformer/insert.py +125 -0
- sqlleaf/processors/transformer/merge.py +14 -0
- sqlleaf/processors/transformer/multitable_insert.py +14 -0
- sqlleaf/processors/transformer/replace.py +23 -0
- sqlleaf/processors/transformer/transformer.py +141 -0
- sqlleaf/processors/transformer/udf.py +235 -0
- sqlleaf/processors/transformer/unload.py +30 -0
- sqlleaf/processors/transformer/update.py +174 -0
- sqlleaf/processors/transformer/values.py +10 -0
- sqlleaf/settings/__init__.py +19 -0
- sqlleaf/settings/athena.py +23 -0
- sqlleaf/settings/base.py +37 -0
- sqlleaf/settings/postgres.py +52 -0
- sqlleaf/system_tables/load.py +0 -0
- sqlleaf/typing.py +93 -0
- sqlleaf/util/__init__.py +34 -0
- sqlleaf/util/expression.py +282 -0
- sqlleaf/util/graph.py +116 -0
- sqlleaf/util/helpers.py +42 -0
- sqlleaf/util/iterators.py +37 -0
- sqlleaf-0.0.1.dist-info/METADATA +46 -0
- sqlleaf-0.0.1.dist-info/RECORD +111 -0
- sqlleaf-0.0.1.dist-info/WHEEL +5 -0
- sqlleaf-0.0.1.dist-info/licenses/LICENSE +21 -0
- sqlleaf-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from sqlglot.generators.postgres import PostgresGenerator
|
|
4
|
+
from sqlleaf.dialects.plpgsql.p_classes import *
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class PlPgSQLGenerator(PostgresGenerator):
|
|
8
|
+
# Keep explicit TRANSFORMS to adhere to project guidance for this experimental dialect.
|
|
9
|
+
TRANSFORMS = {
|
|
10
|
+
**PostgresGenerator.TRANSFORMS,
|
|
11
|
+
PGBlock: lambda self, e: self.pgblock_sql(e),
|
|
12
|
+
# PGDeclare: lambda self, e: self.pgdeclare_sql(e),
|
|
13
|
+
exp.Declare: lambda self, e: self.pgdeclare_sql(e),
|
|
14
|
+
PGDeclareItem: lambda self, e: self.pgdeclareitem_sql(e),
|
|
15
|
+
PGTypeRef: lambda self, e: self.pgtyperef_sql(e),
|
|
16
|
+
PGException: lambda self, e: self.pgexception_sql(e),
|
|
17
|
+
PGWhen: lambda self, e: self.pgwhen_sql(e),
|
|
18
|
+
PGCase: lambda self, e: self.pgcase_sql(e),
|
|
19
|
+
PGIf: lambda self, e: self.pgif_sql(e),
|
|
20
|
+
PGIfBranch: lambda self, e: self.pgifbranch_sql(e),
|
|
21
|
+
PGLoop: lambda self, e: self.pgloop_sql(e),
|
|
22
|
+
# PGWhile: lambda self, e: self.pgwhile_sql(e),
|
|
23
|
+
PGReturn: lambda self, e: self.pgreturn_sql(e),
|
|
24
|
+
PGExit: lambda self, e: self.pgexit_sql(e),
|
|
25
|
+
PGContinue: lambda self, e: self.pgcontinue_sql(e),
|
|
26
|
+
PGRaise: lambda self, e: self.pgraise_sql(e),
|
|
27
|
+
PGSqlState: lambda self, e: self.pgsqlstate_sql(e),
|
|
28
|
+
PGOpenCursor: lambda self, e: self.pgopencursor_sql(e),
|
|
29
|
+
PGFetch: lambda self, e: self.pgfetch_sql(e),
|
|
30
|
+
PGMove: lambda self, e: self.pgmove_sql(e),
|
|
31
|
+
PGClose: lambda self, e: self.pgclose_sql(e),
|
|
32
|
+
PGNext: lambda self, e: self.pgnext_sql(e),
|
|
33
|
+
PGPrior: lambda self, e: self.pgprior_sql(e),
|
|
34
|
+
PGFirst: lambda self, e: self.pgfirst_sql(e),
|
|
35
|
+
PGLast: lambda self, e: self.pglast_sql(e),
|
|
36
|
+
PGForward: lambda self, e: self.pgforward_sql(e),
|
|
37
|
+
PGBackward: lambda self, e: self.pgbackward_sql(e),
|
|
38
|
+
PGAbsolute: lambda self, e: self.pgabsolute_sql(e),
|
|
39
|
+
PGRelative: lambda self, e: self.pgrelative_sql(e),
|
|
40
|
+
PGAll: lambda self, e: self.pgall_sql(e),
|
|
41
|
+
PGAssert: lambda self, e: self.pgassert_sql(e),
|
|
42
|
+
PGForIn: lambda self, e: self.pgforin_sql(e),
|
|
43
|
+
PGForEach: lambda self, e: self.pgforeach_sql(e),
|
|
44
|
+
PGExecute: lambda self, e: self.pgexecute_sql(e),
|
|
45
|
+
PGGetDiagnostics: lambda self, e: self.pggetdiagnostics_sql(e),
|
|
46
|
+
PGCursorArg: lambda self, e: self.pgcursorarg_sql(e),
|
|
47
|
+
PGCursorCall: lambda self, e: self.pgcursorcall_sql(e),
|
|
48
|
+
PGFound: lambda self, e: self.pgfound_sql(e),
|
|
49
|
+
PGOthers: lambda self , e: self.pgothers_sql(e),
|
|
50
|
+
PGPerform: lambda self , e: self.pgperform_sql(e),
|
|
51
|
+
PGDelete: lambda self , e: self.pgdelete_sql(e),
|
|
52
|
+
PGUpdate: lambda self , e: self.pgupdate_sql(e),
|
|
53
|
+
PGInto: lambda self , e: self.pginto_sql(e),
|
|
54
|
+
PGReturning: lambda self , e: self.pgreturning_sql(e),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
def _loop_control_sql(self, keyword: str, expression: exp.Expression) -> str:
|
|
58
|
+
sql = keyword
|
|
59
|
+
if expression.args.get("this") is not None:
|
|
60
|
+
sql += self.seg(self.sql(expression, "this"))
|
|
61
|
+
if expression.args.get("when") is not None:
|
|
62
|
+
sql += self.seg("WHEN")
|
|
63
|
+
sql += self.seg(self.sql(expression, "when"))
|
|
64
|
+
return sql
|
|
65
|
+
|
|
66
|
+
def _current_of_sql(self, expression: exp.Expression) -> str:
|
|
67
|
+
current_of = expression.args.get("current_of")
|
|
68
|
+
if not current_of:
|
|
69
|
+
return ""
|
|
70
|
+
return self.seg(f"WHERE CURRENT OF {self.sql(current_of)}")
|
|
71
|
+
|
|
72
|
+
def _label_names(
|
|
73
|
+
self,
|
|
74
|
+
expression: exp.Expression,
|
|
75
|
+
label_key: str = "label",
|
|
76
|
+
end_label_key: str | None = None,
|
|
77
|
+
) -> tuple[str, str]:
|
|
78
|
+
"""
|
|
79
|
+
Return the leading and trailing labels for a labelled block.
|
|
80
|
+
"""
|
|
81
|
+
label = expression.args.get(label_key)
|
|
82
|
+
end_label = expression.args.get(end_label_key or label_key)
|
|
83
|
+
prefix = f"<<{self.sql(label)}>> " if label is not None else ""
|
|
84
|
+
suffix = f" {self.sql(end_label)}" if end_label is not None else ""
|
|
85
|
+
return prefix, suffix
|
|
86
|
+
|
|
87
|
+
def pgupdate_sql(self, expression: PGUpdate) -> str:
|
|
88
|
+
update = exp.Update(**{k: v for k, v in expression.args.items() if k != "current_of"})
|
|
89
|
+
return self.update_sql(update) + self._current_of_sql(expression)
|
|
90
|
+
|
|
91
|
+
def pgdelete_sql(self, expression: PGDelete) -> str:
|
|
92
|
+
delete = exp.Delete(**{k: v for k, v in expression.args.items() if k != "current_of"})
|
|
93
|
+
return self.delete_sql(delete) + self._current_of_sql(expression)
|
|
94
|
+
|
|
95
|
+
# Also expose the auto-discovered method
|
|
96
|
+
def pgblock_sql(self, expression: PGBlock) -> str:
|
|
97
|
+
# Leading label, if any
|
|
98
|
+
label = expression.args.get("label")
|
|
99
|
+
prefix = f"<<{self.sql(label)}>> " if label is not None else ""
|
|
100
|
+
sql = prefix
|
|
101
|
+
|
|
102
|
+
# DECLARE section first if present
|
|
103
|
+
declare = expression.args.get("declare")
|
|
104
|
+
if declare and declare.expressions:
|
|
105
|
+
sql += self.sql(declare) + self.sep()
|
|
106
|
+
|
|
107
|
+
# BEGIN ... statements ... [EXCEPTION ...] END
|
|
108
|
+
sql += "BEGIN"
|
|
109
|
+
|
|
110
|
+
if expression.expressions:
|
|
111
|
+
body_sql = self.expressions(sqls=[self.sql(e) for e in expression.expressions], sep=f";{self.sep()}")
|
|
112
|
+
sql += self.seg(body_sql + ";")
|
|
113
|
+
|
|
114
|
+
ex = expression.args.get("exception")
|
|
115
|
+
if ex is not None:
|
|
116
|
+
sql += self.seg(self.sql(ex))
|
|
117
|
+
|
|
118
|
+
sql += self.seg("END")
|
|
119
|
+
# Trailing END label when present
|
|
120
|
+
if label is not None:
|
|
121
|
+
sql += self.seg(self.sql(label))
|
|
122
|
+
return sql
|
|
123
|
+
|
|
124
|
+
def pgperform_sql(self, expression: PGPerform) -> str:
|
|
125
|
+
select = exp.Select(**expression.args)
|
|
126
|
+
select_sql = self.select_sql(select)
|
|
127
|
+
return select_sql.replace("SELECT", "PERFORM", 1)
|
|
128
|
+
|
|
129
|
+
def pgdeclare_sql(self, expression: exp.Declare) -> str:
|
|
130
|
+
# Render each item separated by semicolons; keep a final trailing semicolon
|
|
131
|
+
if not expression.expressions:
|
|
132
|
+
return "DECLARE"
|
|
133
|
+
items_sql = self.expressions(sqls=[self.sql(item) for item in expression.expressions], sep=f";{self.sep()}")
|
|
134
|
+
return "DECLARE" + self.seg(items_sql + ";")
|
|
135
|
+
|
|
136
|
+
def pgtyperef_sql(self, expression: PGTypeRef) -> str:
|
|
137
|
+
ref = self.sql(expression.this)
|
|
138
|
+
keyword = "%ROWTYPE" if expression.args.get("rowtype") else "%TYPE"
|
|
139
|
+
suffix = "[]" if expression.args.get("array") else ""
|
|
140
|
+
return ref + keyword + suffix
|
|
141
|
+
|
|
142
|
+
def pgdeclareitem_sql(self, expression: PGDeclareItem) -> str:
|
|
143
|
+
name = self.sql(expression.this)
|
|
144
|
+
# Cursor declaration
|
|
145
|
+
if expression.args.get("cursor"):
|
|
146
|
+
parts: list[str] = [name]
|
|
147
|
+
scroll = expression.args.get("scroll")
|
|
148
|
+
|
|
149
|
+
if scroll is True:
|
|
150
|
+
parts.append("SCROLL")
|
|
151
|
+
elif scroll is False:
|
|
152
|
+
parts.append("NO SCROLL")
|
|
153
|
+
|
|
154
|
+
# CURSOR and optional args
|
|
155
|
+
cursor_args = expression.args.get("cursor_args")
|
|
156
|
+
if cursor_args:
|
|
157
|
+
parts.append(self.func("CURSOR", *cursor_args))
|
|
158
|
+
else:
|
|
159
|
+
parts.append("CURSOR")
|
|
160
|
+
|
|
161
|
+
# FOR query
|
|
162
|
+
parts.append("FOR")
|
|
163
|
+
parts.append(self.sql(expression.args.get("query")))
|
|
164
|
+
return " ".join(parts)
|
|
165
|
+
|
|
166
|
+
# Handle alias variant early: name ALIAS FOR $n
|
|
167
|
+
if expression.args.get("alias_for"):
|
|
168
|
+
target = self.sql(expression.args.get("expression"))
|
|
169
|
+
return name + self.seg("ALIAS") + self.seg("FOR") + self.seg(target)
|
|
170
|
+
|
|
171
|
+
kind = expression.args.get("kind")
|
|
172
|
+
typ = self.sql(kind) if kind is not None else ""
|
|
173
|
+
typ_render = typ if isinstance(kind, PGTypeRef) else (typ.upper() if typ else "")
|
|
174
|
+
const_kw = " CONSTANT" if expression.args.get("constant") else ""
|
|
175
|
+
collate = expression.args.get("collate")
|
|
176
|
+
collate_sql = (self.seg("COLLATE") + self.seg(self.sql(collate))) if collate is not None else ""
|
|
177
|
+
not_null_sql = self.seg("NOT NULL") if expression.args.get("not_null") else ""
|
|
178
|
+
init = expression.args.get("expression")
|
|
179
|
+
|
|
180
|
+
if init is not None and expression.args.get("default"):
|
|
181
|
+
return (
|
|
182
|
+
name
|
|
183
|
+
+ const_kw
|
|
184
|
+
+ (self.seg(typ_render) if typ_render else "")
|
|
185
|
+
+ collate_sql
|
|
186
|
+
+ not_null_sql
|
|
187
|
+
+ self.seg("DEFAULT")
|
|
188
|
+
+ self.seg(self.sql(init))
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
if init is not None:
|
|
192
|
+
op = expression.args.get("assign") or ":="
|
|
193
|
+
return (
|
|
194
|
+
name
|
|
195
|
+
+ const_kw
|
|
196
|
+
+ (self.seg(typ_render) if typ_render else "")
|
|
197
|
+
+ collate_sql
|
|
198
|
+
+ not_null_sql
|
|
199
|
+
+ self.seg(op)
|
|
200
|
+
+ self.seg(self.sql(init))
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
return name + const_kw + (self.seg(typ_render) if typ_render else "") + collate_sql + not_null_sql
|
|
204
|
+
|
|
205
|
+
def pgcursorarg_sql(self, expression: PGCursorArg) -> str:
|
|
206
|
+
kind = self.sql(expression.args.get("kind"))
|
|
207
|
+
kind_render = kind.upper() if kind else ""
|
|
208
|
+
return self.sql(expression.this) + (self.seg(kind_render) if kind_render else "")
|
|
209
|
+
|
|
210
|
+
def pgexception_sql(self, expression: "PGException") -> str:
|
|
211
|
+
whens = expression.args.get("whens") or []
|
|
212
|
+
sql = "EXCEPTION"
|
|
213
|
+
if whens:
|
|
214
|
+
sql += self.seg(self.expressions(sqls=[self.sql(w) for w in whens], sep=self.sep()))
|
|
215
|
+
return sql
|
|
216
|
+
|
|
217
|
+
def pgwhen_sql(self, expression: "PGWhen") -> str:
|
|
218
|
+
# Prefer the normal generator dispatch for conditions
|
|
219
|
+
condition = expression.args.get("condition")
|
|
220
|
+
conds = self.sql(condition) if condition is not None else ""
|
|
221
|
+
body = expression.args.get("then") or []
|
|
222
|
+
body_sql = self.expressions(sqls=[self.sql(stmt) for stmt in body], sep=f";{self.sep()}")
|
|
223
|
+
if body_sql:
|
|
224
|
+
body_sql += ";"
|
|
225
|
+
return "WHEN" + (self.seg(conds) if conds else "") + self.seg("THEN") + (self.seg(body_sql) if body_sql else "")
|
|
226
|
+
|
|
227
|
+
def pgif_sql(self, expression: PGIf) -> str:
|
|
228
|
+
branches = expression.args.get("ifs") or []
|
|
229
|
+
sql_parts: list[str] = []
|
|
230
|
+
for i, branch in enumerate(branches):
|
|
231
|
+
keyword = "IF" if i == 0 else "ELSIF"
|
|
232
|
+
sql_parts.append(self._pgifbranch_sql(branch, keyword))
|
|
233
|
+
|
|
234
|
+
default = expression.args.get("default")
|
|
235
|
+
if default is not None:
|
|
236
|
+
else_body = self.expressions(sqls=[self.sql(stmt) for stmt in default], sep=f";{self.sep()}")
|
|
237
|
+
if else_body:
|
|
238
|
+
else_body += ";"
|
|
239
|
+
sql_parts.append("ELSE" + (self.seg(else_body) if else_body else ""))
|
|
240
|
+
|
|
241
|
+
sql_parts.append("END IF")
|
|
242
|
+
return self.sep().join(sql_parts) if self.pretty else " ".join(sql_parts)
|
|
243
|
+
|
|
244
|
+
def pgcase_sql(self, expression: PGCase) -> str:
|
|
245
|
+
branches = expression.args.get("ifs") or []
|
|
246
|
+
sql_parts = ["CASE", *[self.sql(branch) for branch in branches]]
|
|
247
|
+
|
|
248
|
+
default = expression.args.get("default")
|
|
249
|
+
if default is not None:
|
|
250
|
+
else_body = self.expressions(sqls=[self.sql(stmt) for stmt in default], sep=f";{self.sep()}")
|
|
251
|
+
if else_body:
|
|
252
|
+
else_body += ";"
|
|
253
|
+
sql_parts.append("ELSE" + (self.seg(else_body) if else_body else ""))
|
|
254
|
+
|
|
255
|
+
sql_parts.append("END CASE")
|
|
256
|
+
return self.sep().join(sql_parts) if self.pretty else " ".join(sql_parts)
|
|
257
|
+
|
|
258
|
+
def _pgifbranch_sql(self, branch: PGIfBranch, keyword: str) -> str:
|
|
259
|
+
cond = self.sql(branch, "condition")
|
|
260
|
+
body = self.expressions(sqls=[self.sql(stmt) for stmt in (branch.args.get("then") or [])], sep=f";{self.sep()}")
|
|
261
|
+
if body:
|
|
262
|
+
body += ";"
|
|
263
|
+
return keyword + (self.seg(cond) if cond else "") + self.seg("THEN") + (self.seg(body) if body else "")
|
|
264
|
+
|
|
265
|
+
def pgifbranch_sql(self, expression: PGIfBranch) -> str:
|
|
266
|
+
# Standalone rendering (defaults to IF); PGIf normally supplies the keyword.
|
|
267
|
+
return self._pgifbranch_sql(expression, "IF")
|
|
268
|
+
|
|
269
|
+
# Auto-discovered generator for PGOthers
|
|
270
|
+
def pgothers_sql(self, expression: exp.Expression) -> str:
|
|
271
|
+
return "OTHERS"
|
|
272
|
+
|
|
273
|
+
# Auto-discovered generator for PGSqlState
|
|
274
|
+
def pgsqlstate_sql(self, expression: exp.Expression) -> str:
|
|
275
|
+
value = expression.this
|
|
276
|
+
if value is not None:
|
|
277
|
+
return "SQLSTATE" + self.seg(self.sql(value))
|
|
278
|
+
return "SQLSTATE"
|
|
279
|
+
|
|
280
|
+
def pgreturn_sql(self, expression: PGReturn) -> str:
|
|
281
|
+
# Prefer rendering RETURN QUERY if query flag is set
|
|
282
|
+
if expression.args.get("query"):
|
|
283
|
+
q = expression.args.get("this")
|
|
284
|
+
return f"RETURN QUERY {self.sql(q)}"
|
|
285
|
+
|
|
286
|
+
# Render RETURN NEXT when flagged
|
|
287
|
+
if expression.args.get("next"):
|
|
288
|
+
e = expression.args.get("this")
|
|
289
|
+
return f"RETURN NEXT {self.sql(e)}"
|
|
290
|
+
|
|
291
|
+
value = expression.this
|
|
292
|
+
if value is not None:
|
|
293
|
+
return f"RETURN {self.sql(value)}"
|
|
294
|
+
return "RETURN"
|
|
295
|
+
|
|
296
|
+
# Shared Return node renderer (for interoperability)
|
|
297
|
+
def return_sql(self, expression: exp.Return) -> str:
|
|
298
|
+
value = expression.this
|
|
299
|
+
if value is not None:
|
|
300
|
+
return f"RETURN {self.sql(value)}"
|
|
301
|
+
return "RETURN"
|
|
302
|
+
|
|
303
|
+
def pgraise_sql(self, expression: PGRaise) -> str:
|
|
304
|
+
parts: list[str] = ["RAISE"]
|
|
305
|
+
|
|
306
|
+
lvl = expression.args.get("level")
|
|
307
|
+
if lvl is not None:
|
|
308
|
+
# Render level in upper-case for canonical output
|
|
309
|
+
parts.append(self.sql(lvl).upper())
|
|
310
|
+
|
|
311
|
+
# payload: message (+args) | condition | sqlstate | none
|
|
312
|
+
msg = expression.args.get("message")
|
|
313
|
+
cond = expression.args.get("condition")
|
|
314
|
+
state = expression.args.get("sqlstate")
|
|
315
|
+
|
|
316
|
+
if msg is not None:
|
|
317
|
+
seg = self.sql(msg)
|
|
318
|
+
args = expression.args.get("expressions") or []
|
|
319
|
+
if args:
|
|
320
|
+
seg = f"{seg}, " + ", ".join(self.sql(a) for a in args)
|
|
321
|
+
parts.append(seg)
|
|
322
|
+
elif cond is not None:
|
|
323
|
+
parts.append(self.sql(cond))
|
|
324
|
+
elif state is not None:
|
|
325
|
+
# Call dedicated renderer to avoid relying on global transform
|
|
326
|
+
parts.append(self.pgsqlstate_sql(state))
|
|
327
|
+
|
|
328
|
+
using_args = expression.args.get("using") or []
|
|
329
|
+
if using_args:
|
|
330
|
+
parts.append("USING " + ", ".join(self.sql(a) for a in using_args))
|
|
331
|
+
|
|
332
|
+
return " ".join(parts)
|
|
333
|
+
|
|
334
|
+
def pgassert_sql(self, expression: PGAssert) -> str:
|
|
335
|
+
cond = self.sql(expression.args.get("condition"))
|
|
336
|
+
msg = expression.args.get("message")
|
|
337
|
+
if msg is not None:
|
|
338
|
+
return f"ASSERT {cond}, {self.sql(msg)}"
|
|
339
|
+
return f"ASSERT {cond}"
|
|
340
|
+
|
|
341
|
+
def pgloop_sql(self, expression: PGLoop) -> str:
|
|
342
|
+
body = expression.args.get("body") or []
|
|
343
|
+
body_sql = " ".join(f"{self.sql(stmt)};" for stmt in body)
|
|
344
|
+
prefix, suffix = self._label_names(expression, end_label_key="end_label")
|
|
345
|
+
# Render exactly: LOOP <stmts>; END LOOP [label]
|
|
346
|
+
if body_sql:
|
|
347
|
+
return f"{prefix}LOOP {body_sql} END LOOP{suffix}"
|
|
348
|
+
return f"{prefix}LOOP END LOOP{suffix}"
|
|
349
|
+
|
|
350
|
+
def whileblock_sql(self, expression: exp.WhileBlock) -> str:
|
|
351
|
+
cond_sql = self.sql(expression.this)
|
|
352
|
+
body = expression.args.get("body") or []
|
|
353
|
+
body_sql = " ".join(f"{self.sql(stmt)};" for stmt in body)
|
|
354
|
+
prefix, suffix = self._label_names(expression)
|
|
355
|
+
if body_sql:
|
|
356
|
+
return f"{prefix}WHILE {cond_sql} LOOP {body_sql} END LOOP{suffix}"
|
|
357
|
+
return f"{prefix}WHILE {cond_sql} LOOP END LOOP{suffix}"
|
|
358
|
+
|
|
359
|
+
def pgforin_sql(self, expression: PGForIn) -> str:
|
|
360
|
+
target_sql = self.sql(expression.this)
|
|
361
|
+
body_sql = " ".join(f"{self.sql(stmt)};" for stmt in expression.expressions)
|
|
362
|
+
prefix, suffix = self._label_names(expression)
|
|
363
|
+
|
|
364
|
+
query = expression.args.get("query")
|
|
365
|
+
if query is not None:
|
|
366
|
+
header = f"{prefix}FOR {target_sql} IN {self.sql(query)} LOOP"
|
|
367
|
+
else:
|
|
368
|
+
reverse = "REVERSE " if expression.args.get("reverse") else ""
|
|
369
|
+
start_sql = self.sql(expression.args.get("start"))
|
|
370
|
+
end_sql = self.sql(expression.args.get("end"))
|
|
371
|
+
step = expression.args.get("step")
|
|
372
|
+
by_sql = f" BY {self.sql(step)}" if step is not None else ""
|
|
373
|
+
header = f"{prefix}FOR {target_sql} IN {reverse}{start_sql}..{end_sql}{by_sql} LOOP"
|
|
374
|
+
|
|
375
|
+
if body_sql:
|
|
376
|
+
return f"{header} {body_sql} END LOOP{suffix}"
|
|
377
|
+
return f"{header} END LOOP{suffix}"
|
|
378
|
+
|
|
379
|
+
def pgcursorcall_sql(self, expression: PGCursorCall) -> str:
|
|
380
|
+
cursor_sql = self.sql(expression.this)
|
|
381
|
+
args = expression.args.get("expressions")
|
|
382
|
+
if args:
|
|
383
|
+
return f"{cursor_sql}({', '.join(self.sql(arg) for arg in args)})"
|
|
384
|
+
return cursor_sql
|
|
385
|
+
|
|
386
|
+
def pgforeach_sql(self, expression: PGForEach) -> str:
|
|
387
|
+
target_sql = self.sql(expression.this)
|
|
388
|
+
array_sql = self.sql(expression.args.get("expression"))
|
|
389
|
+
body_sql = " ".join(f"{self.sql(stmt)};" for stmt in expression.expressions)
|
|
390
|
+
|
|
391
|
+
slice_expr = expression.args.get("slice")
|
|
392
|
+
slice_sql = f" SLICE {self.sql(slice_expr)}" if slice_expr is not None else ""
|
|
393
|
+
|
|
394
|
+
prefix, suffix = self._label_names(expression)
|
|
395
|
+
|
|
396
|
+
header = f"{prefix}FOREACH {target_sql}{slice_sql} IN ARRAY {array_sql} LOOP"
|
|
397
|
+
if body_sql:
|
|
398
|
+
return f"{header} {body_sql} END LOOP{suffix}"
|
|
399
|
+
return f"{header} END LOOP{suffix}"
|
|
400
|
+
|
|
401
|
+
def pgexit_sql(self, expression: PGExit) -> str:
|
|
402
|
+
return self._loop_control_sql("EXIT", expression)
|
|
403
|
+
|
|
404
|
+
def pgcontinue_sql(self, expression: PGContinue) -> str:
|
|
405
|
+
return self._loop_control_sql("CONTINUE", expression)
|
|
406
|
+
|
|
407
|
+
# Shared node for EXIT/LEAVE
|
|
408
|
+
def leave_sql(self, expression: exp.Leave) -> str:
|
|
409
|
+
return self._loop_control_sql("EXIT", expression)
|
|
410
|
+
|
|
411
|
+
# Shared node for CONTINUE/ITERATE
|
|
412
|
+
def iterate_sql(self, expression: exp.Iterate) -> str:
|
|
413
|
+
return self._loop_control_sql("CONTINUE", expression)
|
|
414
|
+
|
|
415
|
+
def _direction_and_cursor_sql(self, expression: exp.Expression) -> str:
|
|
416
|
+
dir_expr = expression.args.get("direction")
|
|
417
|
+
if dir_expr is not None:
|
|
418
|
+
prep = expression.args.get("preposition") or "FROM"
|
|
419
|
+
return f"{self.sql(dir_expr)} {prep} {self.sql(expression.this)}"
|
|
420
|
+
return self.sql(expression.this)
|
|
421
|
+
|
|
422
|
+
def pgopencursor_sql(self, expression: PGOpenCursor) -> str:
|
|
423
|
+
# Unbound form: OPEN c [NO|SCROLL] FOR <query>
|
|
424
|
+
if expression.args.get("expression") is not None:
|
|
425
|
+
parts: list[str] = ["OPEN", self.sql(expression.this)]
|
|
426
|
+
scroll = expression.args.get("scroll")
|
|
427
|
+
if scroll is True:
|
|
428
|
+
parts.append("SCROLL")
|
|
429
|
+
elif scroll is False:
|
|
430
|
+
parts.append("NO SCROLL")
|
|
431
|
+
parts.append("FOR")
|
|
432
|
+
parts.append(self.sql(expression.args.get("expression")))
|
|
433
|
+
return " ".join(parts)
|
|
434
|
+
|
|
435
|
+
# Bound form: OPEN c or OPEN c(<args>)
|
|
436
|
+
name_sql = self.sql(expression.this)
|
|
437
|
+
if expression.expressions:
|
|
438
|
+
args_sql = ", ".join(self.sql(arg) for arg in expression.expressions)
|
|
439
|
+
name_sql = f"{name_sql}({args_sql})"
|
|
440
|
+
return f"OPEN {name_sql}"
|
|
441
|
+
|
|
442
|
+
def pgfetch_sql(self, expression: PGFetch) -> str:
|
|
443
|
+
targets_sql = ", ".join(self.sql(e) for e in expression.expressions)
|
|
444
|
+
frag = self._direction_and_cursor_sql(expression)
|
|
445
|
+
return f"FETCH {frag} INTO {targets_sql}"
|
|
446
|
+
|
|
447
|
+
def pgmove_sql(self, expression: "PGMove") -> str:
|
|
448
|
+
frag = self._direction_and_cursor_sql(expression)
|
|
449
|
+
return f"MOVE {frag}"
|
|
450
|
+
|
|
451
|
+
def pgclose_sql(self, expression: "PGClose") -> str:
|
|
452
|
+
return f"CLOSE {self.sql(expression.this)}"
|
|
453
|
+
|
|
454
|
+
def pgexecute_sql(self, expression: PGExecute) -> str:
|
|
455
|
+
parts: list[str] = ["EXECUTE", self.sql(expression.this)]
|
|
456
|
+
|
|
457
|
+
targets = expression.args.get("expressions")
|
|
458
|
+
if targets:
|
|
459
|
+
into = "INTO STRICT" if expression.args.get("strict") else "INTO"
|
|
460
|
+
parts.append(f"{into} " + ", ".join(self.sql(t) for t in targets))
|
|
461
|
+
|
|
462
|
+
using_args = expression.args.get("using")
|
|
463
|
+
if using_args:
|
|
464
|
+
parts.append("USING " + ", ".join(self.sql(a) for a in using_args))
|
|
465
|
+
|
|
466
|
+
return " ".join(parts)
|
|
467
|
+
|
|
468
|
+
def pginto_sql(self, expression: exp.Into) -> str:
|
|
469
|
+
strict = " STRICT" if expression.args.get("strict") else ""
|
|
470
|
+
|
|
471
|
+
targets = expression.args.get("expressions")
|
|
472
|
+
if targets:
|
|
473
|
+
target_sql = ", ".join(self.sql(target) for target in targets)
|
|
474
|
+
else:
|
|
475
|
+
target_sql = self.sql(expression, "this")
|
|
476
|
+
|
|
477
|
+
return f"{self.seg('INTO')}{strict} {target_sql}"
|
|
478
|
+
|
|
479
|
+
def pgreturning_sql(self, expression: PGReturning) -> str:
|
|
480
|
+
# Start with base RETURNING expressions
|
|
481
|
+
base = f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
|
|
482
|
+
into = expression.args.get("into")
|
|
483
|
+
if into is not None:
|
|
484
|
+
# Delegate to pginto_sql which prefixes with a space via seg('INTO')
|
|
485
|
+
base += self.sql(into)
|
|
486
|
+
return base
|
|
487
|
+
|
|
488
|
+
def pggetdiagnostics_sql(self, expression: PGGetDiagnostics) -> str:
|
|
489
|
+
parts: list[str] = ["GET"]
|
|
490
|
+
if expression.args.get("current"):
|
|
491
|
+
parts.append("CURRENT")
|
|
492
|
+
elif expression.args.get("stacked"):
|
|
493
|
+
parts.append("STACKED")
|
|
494
|
+
parts.append("DIAGNOSTICS")
|
|
495
|
+
|
|
496
|
+
assigns = expression.expressions or []
|
|
497
|
+
assigns_sql = ", ".join(self.sql(a) for a in assigns)
|
|
498
|
+
parts.append(assigns_sql)
|
|
499
|
+
return " ".join(parts)
|
|
500
|
+
|
|
501
|
+
def pgfound_sql(self, expression: PGFound) -> str:
|
|
502
|
+
return "FOUND"
|
|
503
|
+
|
|
504
|
+
# Direction generators (auto-discovered)
|
|
505
|
+
def pgnext_sql(self, expression: PGNext) -> str:
|
|
506
|
+
return "NEXT"
|
|
507
|
+
|
|
508
|
+
def pgprior_sql(self, expression: PGPrior) -> str:
|
|
509
|
+
return "PRIOR"
|
|
510
|
+
|
|
511
|
+
def pgfirst_sql(self, expression: PGFirst) -> str:
|
|
512
|
+
return "FIRST"
|
|
513
|
+
|
|
514
|
+
def pglast_sql(self, expression: PGLast) -> str:
|
|
515
|
+
return "LAST"
|
|
516
|
+
|
|
517
|
+
def pgforward_sql(self, expression: PGForward) -> str:
|
|
518
|
+
if expression.args.get("all"):
|
|
519
|
+
return "FORWARD ALL"
|
|
520
|
+
if expression.args.get("this") is not None:
|
|
521
|
+
return f"FORWARD {self.sql(expression.this)}"
|
|
522
|
+
return "FORWARD"
|
|
523
|
+
|
|
524
|
+
def pgbackward_sql(self, expression: PGBackward) -> str:
|
|
525
|
+
if expression.args.get("all"):
|
|
526
|
+
return "BACKWARD ALL"
|
|
527
|
+
if expression.args.get("this") is not None:
|
|
528
|
+
return f"BACKWARD {self.sql(expression.this)}"
|
|
529
|
+
return "BACKWARD"
|
|
530
|
+
|
|
531
|
+
def pgabsolute_sql(self, expression: PGAbsolute) -> str:
|
|
532
|
+
return f"ABSOLUTE {self.sql(expression.this)}"
|
|
533
|
+
|
|
534
|
+
def pgrelative_sql(self, expression: PGRelative) -> str:
|
|
535
|
+
return f"RELATIVE {self.sql(expression.this)}"
|
|
536
|
+
|
|
537
|
+
def pgall_sql(self, expression: PGAll) -> str:
|
|
538
|
+
return "ALL"
|