-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrender.py
More file actions
123 lines (95 loc) · 3.71 KB
/
Copy pathrender.py
File metadata and controls
123 lines (95 loc) · 3.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
from enum import Enum
from typing import Optional, Union
from .nodes import SQLNode
from .clauses import SQLClause
from .sqlcontext import *
from .compiler.annotate import annotate, AnnotateContext
from .compiler.resolve import *
from .compiler.link import *
from .compiler.translate import *
from .compiler.serialize import serialize, SerializationContext, SQLString
# -----------------------------------------------------------
# utility functions to generate SQL context for specific dialects
# -----------------------------------------------------------
def dialect_default() -> SQLDialect:
return dialect_postgres()
def dialect_postgres() -> SQLDialect:
return SQLDialect(
name="postgresql",
var_style=VarStyle.NUMBERED,
var_prefix="$",
)
# -----------------------------------------------------------
# render routines
# -----------------------------------------------------------
class RenderDepth(Enum):
"""
FunSQL compiles the specified query node structure to a SQL string in multiple passes.
RenderDepth specifies the number of passes executed, to help with debugging.
Implementation of the Ordered enumeration is copied from the [reference](https://docs.python.org/3/library/enum.html#orderedenum)
"""
ANNOTATE = 1
RESOLVE = 2
LINK = 3
TRANSLATE = 4
SERIALIZE = 5
def __lt__(self, other):
if isinstance(other, RenderDepth):
return self.value < other.value
return NotImplemented
def __le__(self, other):
if isinstance(other, RenderDepth):
return self.value <= other.value
return NotImplemented
def __gt__(self, other):
if isinstance(other, RenderDepth):
return self.value > other.value
return NotImplemented
def __ge__(self, other):
if isinstance(other, RenderDepth):
return self.value >= other.value
return NotImplemented
def render(
node: SQLNode,
depth: RenderDepth = RenderDepth.SERIALIZE,
catalog: Optional[SQLCatalog] = None,
) -> Union[SQLNode, SQLClause, SQLString]:
"""
Render the SQL node expression to a SQLString object.
Args:
node: the SQL node to render.
depth: num of compiler passes to run
catalog: the SQL catalog to use for resolving SQL table references, and query dialect
Returns:
SQLString If all the compiler passes are made, SQLClause object if translate pass has been made,
else a SQLNode object.
NOTE: calling the `render` method might mutate the SQLNode objects. Work with
a fresh object on each call.
"""
assert isinstance(node, SQLNode)
if catalog is None:
catalog = SQLCatalog(dialect=dialect_default())
ann_ctx = AnnotateContext(catalog=catalog)
node_annotated = annotate(node, ann_ctx)
if not depth > RenderDepth.ANNOTATE:
return node_annotated
resolve_toplevel(ann_ctx)
if not depth > RenderDepth.RESOLVE:
return node_annotated
link_toplevel(ann_ctx)
if not depth > RenderDepth.LINK:
return node_annotated
translate_ctx = TranslateContext(ann_ctx)
output_clause = translate_toplevel(node_annotated, translate_ctx)
if not depth > RenderDepth.TRANSLATE:
return output_clause
serialize_ctx = SerializationContext(dialect=catalog.dialect)
serialize(output_clause, serialize_ctx)
return serialize_ctx.render()
def render_clause(clause: SQLClause, dialect: SQLDialect) -> SQLString:
"""Render the SQL clause to a SQLString object."""
assert isinstance(clause, SQLClause)
assert isinstance(dialect, SQLDialect)
serialize_ctx = SerializationContext(dialect=dialect)
serialize(clause, serialize_ctx)
return serialize_ctx.render()