Edit on GitHub

sqlglot.serde

  1from __future__ import annotations
  2
  3import typing as t
  4
  5from sqlglot import expressions as exp
  6from types import ModuleType
  7
  8
  9StackVal = tuple[t.Any, t.Optional[int], t.Optional[str], bool]
 10
 11
 12INDEX = "i"
 13ARG_KEY = "k"
 14IS_ARRAY = "a"
 15CLASS = "c"
 16TYPE = "t"
 17COMMENTS = "o"
 18META = "m"
 19VALUE = "v"
 20DATA_TYPE = "DataType.Type"
 21
 22
 23def dump(expression: exp.Expr) -> list[dict[str, t.Any]]:
 24    """
 25    Dump an Expr into a JSON serializable List.
 26    """
 27    i = 0
 28    payloads: list[dict[str, t.Any]] = []
 29    stack: list[StackVal] = [(expression, None, None, False)]
 30
 31    while stack:
 32        node, index, arg_key, is_array = stack.pop()
 33
 34        payload: dict[str, t.Any] = {}
 35
 36        if index is not None:
 37            payload[INDEX] = index
 38        if arg_key is not None:
 39            payload[ARG_KEY] = arg_key
 40        if is_array:
 41            payload[IS_ARRAY] = is_array
 42
 43        payloads.append(payload)
 44
 45        if hasattr(node, "parent"):
 46            klass = node.__class__.__qualname__
 47
 48            if node.__class__.__module__ != exp.__name__:
 49                klass = f"{node.__module__}.{klass}"
 50
 51            payload[CLASS] = klass
 52
 53            if node.type:
 54                payload[TYPE] = dump(node.type)
 55            if node.comments:
 56                payload[COMMENTS] = node.comments
 57            if node._meta is not None:
 58                payload[META] = node._meta
 59            if node.args:
 60                for k, vs in reversed(node.args.items()):
 61                    if type(vs) is list:
 62                        for v in reversed(vs):
 63                            stack.append((v, i, k, True))
 64                    elif vs is not None:
 65                        stack.append((vs, i, k, False))
 66        elif type(node) is exp.DType:
 67            payload[CLASS] = DATA_TYPE
 68            payload[VALUE] = node.value
 69        else:
 70            payload[VALUE] = node
 71
 72        i += 1
 73
 74    return payloads
 75
 76
 77def load(
 78    payloads: list[dict[str, t.Any]] | None,
 79) -> exp.Expr | exp.DType | None:
 80    """
 81    Load a list of dicts generated by dump into an Expr.
 82    """
 83
 84    if not payloads:
 85        return None
 86
 87    payload, *tail = payloads
 88    root = _load(payload)
 89    nodes: list[object] = [root]
 90    for payload in tail:
 91        if CLASS in payload:
 92            node: object = _load(payload)
 93        else:
 94            node = payload[VALUE]
 95
 96        nodes.append(node)
 97        parent: exp.Expr = nodes[payload[INDEX]]
 98        arg_key: str = payload[ARG_KEY]
 99
100        if payload.get(IS_ARRAY):
101            parent.append(arg_key, node)
102        else:
103            parent.set(arg_key, node)
104
105    return root
106
107
108def _load(payload: dict[str, t.Any]) -> exp.Expr | exp.DType:
109    class_name: str = payload[CLASS]
110
111    if class_name == DATA_TYPE:
112        return exp.DType(payload[VALUE])
113    module: ModuleType
114    if "." in class_name:
115        module_path, class_name = class_name.rsplit(".", maxsplit=1)
116        module = __import__(module_path, fromlist=[class_name])
117    else:
118        module = exp
119
120    expression = getattr(module, class_name)()
121    expression._type = load(payload.get(TYPE))
122    expression.comments = payload.get(COMMENTS)
123    expression._meta = payload.get(META)
124    return expression
StackVal = tuple[typing.Any, typing.Optional[int], typing.Optional[str], bool]
INDEX = 'i'
ARG_KEY = 'k'
IS_ARRAY = 'a'
CLASS = 'c'
TYPE = 't'
COMMENTS = 'o'
META = 'm'
VALUE = 'v'
DATA_TYPE = 'DataType.Type'
def dump(expression: sqlglot.expressions.core.Expr) -> list[dict[str, typing.Any]]:
24def dump(expression: exp.Expr) -> list[dict[str, t.Any]]:
25    """
26    Dump an Expr into a JSON serializable List.
27    """
28    i = 0
29    payloads: list[dict[str, t.Any]] = []
30    stack: list[StackVal] = [(expression, None, None, False)]
31
32    while stack:
33        node, index, arg_key, is_array = stack.pop()
34
35        payload: dict[str, t.Any] = {}
36
37        if index is not None:
38            payload[INDEX] = index
39        if arg_key is not None:
40            payload[ARG_KEY] = arg_key
41        if is_array:
42            payload[IS_ARRAY] = is_array
43
44        payloads.append(payload)
45
46        if hasattr(node, "parent"):
47            klass = node.__class__.__qualname__
48
49            if node.__class__.__module__ != exp.__name__:
50                klass = f"{node.__module__}.{klass}"
51
52            payload[CLASS] = klass
53
54            if node.type:
55                payload[TYPE] = dump(node.type)
56            if node.comments:
57                payload[COMMENTS] = node.comments
58            if node._meta is not None:
59                payload[META] = node._meta
60            if node.args:
61                for k, vs in reversed(node.args.items()):
62                    if type(vs) is list:
63                        for v in reversed(vs):
64                            stack.append((v, i, k, True))
65                    elif vs is not None:
66                        stack.append((vs, i, k, False))
67        elif type(node) is exp.DType:
68            payload[CLASS] = DATA_TYPE
69            payload[VALUE] = node.value
70        else:
71            payload[VALUE] = node
72
73        i += 1
74
75    return payloads

Dump an Expr into a JSON serializable List.

def load( payloads: list[dict[str, typing.Any]] | None) -> sqlglot.expressions.core.Expr | sqlglot.expressions.datatypes.DType | None:
 78def load(
 79    payloads: list[dict[str, t.Any]] | None,
 80) -> exp.Expr | exp.DType | None:
 81    """
 82    Load a list of dicts generated by dump into an Expr.
 83    """
 84
 85    if not payloads:
 86        return None
 87
 88    payload, *tail = payloads
 89    root = _load(payload)
 90    nodes: list[object] = [root]
 91    for payload in tail:
 92        if CLASS in payload:
 93            node: object = _load(payload)
 94        else:
 95            node = payload[VALUE]
 96
 97        nodes.append(node)
 98        parent: exp.Expr = nodes[payload[INDEX]]
 99        arg_key: str = payload[ARG_KEY]
100
101        if payload.get(IS_ARRAY):
102            parent.append(arg_key, node)
103        else:
104            parent.set(arg_key, node)
105
106    return root

Load a list of dicts generated by dump into an Expr.