Skip to content

Commit ff3aad2

Browse files
authored
Implement gel.Record type (#560)
It's returned from `client.query_sql()` and represents SQL rows. Currently it supports: * integer indexing - fetch columns by position * string indexing - fetch columns by name * as_dict() method - render record as a dict
1 parent 1bc75c4 commit ff3aad2

File tree

16 files changed

+527
-55
lines changed

16 files changed

+527
-55
lines changed

gel/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from gel.datatypes.datatypes import (
2525
Tuple, NamedTuple, EnumValue, RelativeDuration, DateDuration, ConfigMemory
2626
)
27-
from gel.datatypes.datatypes import Set, Object, Array
27+
from gel.datatypes.datatypes import Record, Set, Object, Array
2828
from gel.datatypes.range import Range, MultiRange
2929

3030
from .abstract import (
@@ -68,6 +68,7 @@
6868
"RetryCondition",
6969
"RetryOptions",
7070
"Set",
71+
"Record",
7172
"State",
7273
"TransactionOptions",
7374
"Tuple",

gel/abstract.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import dataclasses
2323
import typing
2424

25+
from . import datatypes
2526
from . import describe
2627
from . import enums
2728
from . import options
@@ -278,7 +279,7 @@ def query_required_single_json(self, query: str, *args, **kwargs) -> str:
278279
annotations=self._get_annotations(),
279280
))
280281

281-
def query_sql(self, query: str, *args, **kwargs) -> typing.Any:
282+
def query_sql(self, query: str, *args, **kwargs) -> list[datatypes.Record]:
282283
return self._query(QueryContext(
283284
query=QueryWithArgs(
284285
query,

gel/datatypes/datatypes.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ typedef enum {
7070
L_LINK
7171
} edge_attr_lookup_t;
7272

73+
74+
#define EdgeRecordDesc_GET_NAMES(o) (((EdgeRecordDescObject *)(o))->names)
75+
76+
7377
PyObject * EdgeRecordDesc_InitType(void);
7478
PyObject * EdgeRecordDesc_New(PyObject *, PyObject *, PyObject *);
7579
PyObject * EdgeRecordDesc_PointerName(PyObject *, Py_ssize_t);
@@ -85,6 +89,8 @@ edge_attr_lookup_t EdgeRecordDesc_Lookup(PyObject *, PyObject *, Py_ssize_t *);
8589
PyObject * EdgeRecordDesc_List(PyObject *, uint8_t, uint8_t);
8690
PyObject * EdgeRecordDesc_GetDataclassFields(PyObject *);
8791

92+
PyObject * EdgeRecordDesc_GetNames(PyObject *);
93+
8894

8995
/* === gel.NamedTuple ==================================== */
9096

@@ -122,6 +128,29 @@ PyObject * EdgeObject_GetRecordDesc(PyObject *);
122128
int EdgeObject_SetItem(PyObject *, Py_ssize_t, PyObject *);
123129
PyObject * EdgeObject_GetItem(PyObject *, Py_ssize_t);
124130

125-
PyObject * EdgeObject_GetID(PyObject *ob);
131+
132+
/* === edgedb.Record ======================================== */
133+
134+
#define EDGE_RECORD_FREELIST_SIZE 2000
135+
#define EDGE_RECORD_FREELIST_MAXSAVE 20
136+
137+
extern PyTypeObject EdgeRecord_Type;
138+
139+
#define EdgeRecord_Check(d) (Py_TYPE(d) == &EdgeRecord_Type)
140+
141+
typedef struct {
142+
PyObject_VAR_HEAD
143+
PyObject *weakreflist;
144+
PyObject *desc;
145+
Py_hash_t cached_hash;
146+
PyObject *ob_item[1];
147+
} EdgeRecord;
148+
149+
PyObject * EdgeRecord_InitType(void);
150+
PyObject * EdgeRecord_New(PyObject *);
151+
PyObject * EdgeRecord_GetRecordDesc(PyObject *);
152+
153+
int EdgeRecord_SetItem(PyObject *, Py_ssize_t, PyObject *);
154+
PyObject * EdgeRecord_GetItem(PyObject *, Py_ssize_t);
126155

127156
#endif

gel/datatypes/datatypes.pxd

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ cdef extern from "datatypes.h":
5858
int EdgeObject_SetItem(object, Py_ssize_t, object) except -1
5959
object EdgeObject_GetRecordDesc(object)
6060

61+
object EdgeRecord_InitType()
62+
object EdgeRecord_New(object);
63+
int EdgeRecord_SetItem(object, Py_ssize_t, object) except -1
64+
object EdgeRecord_GetRecordDesc(object)
6165

6266
cdef record_desc_new(object names, object flags, object cards)
6367
cdef record_desc_pointer_name(object desc, Py_ssize_t pos)
@@ -69,5 +73,7 @@ cdef namedtuple_new(object namedtuple_type)
6973
cdef namedtuple_type_new(object desc)
7074
cdef object_new(object desc)
7175
cdef object_set(object tuple, Py_ssize_t pos, object elem)
76+
cdef record_new(object desc)
77+
cdef record_set(object obj, Py_ssize_t pos, object elem)
7278

7379
cdef extern cpython.PyObject* at_sign_ptr

gel/datatypes/datatypes.pyx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ _RecordDescriptor = EdgeRecordDesc_InitType()
3030
Tuple = tuple
3131
NamedTuple = EdgeNamedTuple_InitType()
3232
Object = EdgeObject_InitType()
33+
Record = EdgeRecord_InitType()
3334
Set = list
3435
Array = list
3536

@@ -132,6 +133,11 @@ cdef namedtuple_type_new(object desc):
132133
cdef object_new(object desc):
133134
return EdgeObject_New(desc)
134135

135-
136136
cdef object_set(object obj, Py_ssize_t pos, object elem):
137137
EdgeObject_SetItem(obj, pos, elem)
138+
139+
cdef record_new(object desc):
140+
return EdgeRecord_New(desc)
141+
142+
cdef record_set(object obj, Py_ssize_t pos, object elem):
143+
EdgeRecord_SetItem(obj, pos, elem)

gel/datatypes/internal.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,21 @@ PyObject * _EdgeGeneric_RenderObject(PyObject *obj);
3737
int _EdgeGeneric_RenderValues(
3838
_PyUnicodeWriter *, PyObject *, PyObject **, Py_ssize_t);
3939

40-
int _EdgeGeneric_RenderItems(_PyUnicodeWriter *,
41-
PyObject *, PyObject *,
42-
PyObject **, Py_ssize_t, int, int);
43-
4440
PyObject * _EdgeGeneric_RichCompareValues(PyObject **, Py_ssize_t,
4541
PyObject **, Py_ssize_t,
4642
int);
4743

4844

45+
#define EDGE_RENDER_NAMES 0x1
46+
#define EDGE_RENDER_LINK_PROPS 0x2
47+
#define EDGE_RENDER_IMPLICIT 0x4
48+
#define EDGE_RENDER_DEFAULT 0
49+
50+
int _EdgeGeneric_RenderItems(_PyUnicodeWriter *,
51+
PyObject *, PyObject *,
52+
PyObject **, Py_ssize_t,
53+
int);
54+
4955
#ifndef _PyList_CAST
5056
# define _PyList_CAST(op) (assert(PyList_Check(op)), (PyListObject *)(op))
5157
#endif

gel/datatypes/namedtuple.c

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ PyObject *
4343
EdgeNamedTuple_New(PyObject *type)
4444
{
4545
assert(init_type_called);
46+
assert(PyType_Check(type));
4647

4748
PyObject *desc = EdgeNamedTuple_Type_DESC(type);
4849
if (desc == NULL || !EdgeRecordDesc_Check(desc)) {
@@ -77,7 +78,7 @@ EdgeNamedTuple_New(PyObject *type)
7778
_EDGE_NAMED_TUPLE_FL_NUM_FREE[size]--;
7879
_Py_NewReference((PyObject *)nt);
7980
Py_INCREF(type);
80-
Py_SET_TYPE(nt, type);
81+
Py_SET_TYPE(nt, (PyTypeObject*)type);
8182
}
8283
} else {
8384
if (
@@ -88,7 +89,7 @@ EdgeNamedTuple_New(PyObject *type)
8889
PyErr_NoMemory();
8990
return NULL;
9091
}
91-
nt = PyObject_GC_NewVar(PyTupleObject, type, size);
92+
nt = PyObject_GC_NewVar(PyTupleObject, (PyTypeObject*)type, size);
9293
if (nt == NULL) {
9394
return NULL;
9495
}
@@ -197,9 +198,9 @@ namedtuple_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) {
197198
goto fail;
198199
}
199200

200-
type = EdgeNamedTuple_Type_New(desc);
201-
o = (PyTupleObject *)EdgeNamedTuple_New(type);
202-
Py_CLEAR(type); // the type is now referenced by the object
201+
PyObject *new_type = EdgeNamedTuple_Type_New(desc);
202+
o = (PyTupleObject *)EdgeNamedTuple_New(new_type);
203+
Py_CLEAR(new_type); // the type is now referenced by the object
203204

204205
if (o == NULL) {
205206
goto fail;
@@ -236,7 +237,7 @@ namedtuple_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) {
236237

237238
static PyObject *
238239
namedtuple_derived_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) {
239-
PyTupleObject *o = (PyTupleObject *)EdgeNamedTuple_New(type);
240+
PyTupleObject *o = (PyTupleObject *)EdgeNamedTuple_New((PyObject*)type);
240241
if (o == NULL) {
241242
goto fail;
242243
}
@@ -356,7 +357,8 @@ namedtuple_repr(PyTupleObject *o)
356357
if (_EdgeGeneric_RenderItems(&writer,
357358
(PyObject *)o,
358359
EdgeNamedTuple_Type_DESC(Py_TYPE(o)),
359-
o->ob_item, Py_SIZE(o), 0, 0) < 0)
360+
o->ob_item, Py_SIZE(o),
361+
EDGE_RENDER_NAMES) < 0)
360362
{
361363
goto error;
362364
}
@@ -458,7 +460,7 @@ EdgeNamedTuple_Type_New(PyObject *desc)
458460

459461
// store `_fields` for collections.namedtuple duck-typing
460462
Py_ssize_t size = EdgeRecordDesc_GetSize(desc);
461-
PyTupleObject *fields = PyTuple_New(size);
463+
PyTupleObject *fields = (PyTupleObject *)PyTuple_New(size);
462464
if (fields == NULL) {
463465
goto fail;
464466
}
@@ -470,11 +472,11 @@ EdgeNamedTuple_Type_New(PyObject *desc)
470472
}
471473
PyTuple_SET_ITEM(fields, i, name);
472474
}
473-
if (PyDict_SetItemString(rv->tp_dict, "_fields", fields) < 0) {
475+
if (PyDict_SetItemString(rv->tp_dict, "_fields", (PyObject*)fields) < 0) {
474476
goto fail;
475477
}
476478

477-
return rv;
479+
return (PyObject*)rv;
478480

479481
fail:
480482
Py_DECREF(rv);

gel/datatypes/object.c

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -124,25 +124,6 @@ EdgeObject_GetItem(PyObject *ob, Py_ssize_t i)
124124
}
125125

126126

127-
PyObject *
128-
EdgeObject_GetID(PyObject *ob)
129-
{
130-
assert(EdgeObject_Check(ob));
131-
EdgeObject *o = (EdgeObject *)ob;
132-
Py_ssize_t i = EdgeRecordDesc_IDPos(o->desc);
133-
if (i < 0) {
134-
Py_RETURN_NONE;
135-
}
136-
if (i >= Py_SIZE(o)) {
137-
PyErr_BadInternalCall();
138-
return NULL;
139-
}
140-
PyObject *el = EdgeObject_GET_ITEM(o, i);
141-
Py_INCREF(el);
142-
return el;
143-
}
144-
145-
146127
static void
147128
object_dealloc(EdgeObject *o)
148129
{
@@ -282,9 +263,11 @@ object_repr(EdgeObject *o)
282263
goto error;
283264
}
284265

285-
if (_EdgeGeneric_RenderItems(&writer,
286-
(PyObject *)o, o->desc,
287-
o->ob_item, Py_SIZE(o), 1, 0) < 0)
266+
if (_EdgeGeneric_RenderItems(
267+
&writer,
268+
(PyObject *)o, o->desc,
269+
o->ob_item, Py_SIZE(o),
270+
EDGE_RENDER_NAMES | EDGE_RENDER_LINK_PROPS) < 0)
288271
{
289272
goto error;
290273
}

0 commit comments

Comments
 (0)