summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/cyextension/resultproxy.pyx
blob: 96a028d93383481f96682581c9ccde534a8deb9c (plain)
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
# TODO: this is mostly just copied over from the python implementation
# more improvements are likely possible
import operator

cdef int MD_INDEX = 0  # integer index in cursor.description
cdef int _KEY_OBJECTS_ONLY = 1

KEY_INTEGER_ONLY = 0
KEY_OBJECTS_ONLY = _KEY_OBJECTS_ONLY

cdef class BaseRow:
    cdef readonly object _parent
    cdef readonly tuple _data
    cdef readonly dict _keymap
    cdef readonly int _key_style

    def __init__(self, object parent, object processors, dict keymap, int key_style, object data):
        """Row objects are constructed by CursorResult objects."""

        self._parent = parent

        if processors:
            self._data = tuple(
                [
                    proc(value) if proc else value
                    for proc, value in zip(processors, data)
                ]
            )
        else:
            self._data = tuple(data)

        self._keymap = keymap

        self._key_style = key_style

    def __reduce__(self):
        return (
            rowproxy_reconstructor,
            (self.__class__, self.__getstate__()),
        )

    def __getstate__(self):
        return {
            "_parent": self._parent,
            "_data": self._data,
            "_key_style": self._key_style,
        }

    def __setstate__(self, dict state):
        self._parent = state["_parent"]
        self._data = state["_data"]
        self._keymap = self._parent._keymap
        self._key_style = state["_key_style"]

    def _values_impl(self):
        return list(self)

    def __iter__(self):
        return iter(self._data)

    def __len__(self):
        return len(self._data)

    def __hash__(self):
        return hash(self._data)

    def __getitem__(self, index):
        return self._data[index]

    cpdef _get_by_key_impl_mapping(self, key):
        try:
            rec = self._keymap[key]
        except KeyError as ke:
            rec = self._parent._key_fallback(key, ke)

        mdindex = rec[MD_INDEX]
        if mdindex is None:
            self._parent._raise_for_ambiguous_column_name(rec)
        elif (
            self._key_style == _KEY_OBJECTS_ONLY
            and isinstance(key, int)
        ):
            raise KeyError(key)

        return self._data[mdindex]

    def __getattr__(self, name):
        try:
            return self._get_by_key_impl_mapping(name)
        except KeyError as e:
           raise AttributeError(e.args[0]) from e


def rowproxy_reconstructor(cls, state):
    obj = cls.__new__(cls)
    obj.__setstate__(state)
    return obj


def tuplegetter(*indexes):
    it = operator.itemgetter(*indexes)

    if len(indexes) > 1:
        return it
    else:
        return lambda row: (it(row),)