source: trunk/pgquery.c

Last change on this file was 1002, checked in by cito, 6 months ago

Use same prefix for all C files

So that they are not scattered among the other files
in the root directory.

File size: 22.0 KB
Line 
1/*
2 * $Id: pgquery.c 985 2019-04-22 22:07:43Z cito $
3 *
4 * PyGreSQL - a Python interface for the PostgreSQL database.
5 *
6 * The query object - this file is part a of the C extension module.
7 *
8 * Copyright (c) 2019 by the PyGreSQL Development Team
9 *
10 * Please see the LICENSE.TXT file for specific restrictions.
11 *
12 */
13
14/* Deallocate the query object. */
15static void
16query_dealloc(queryObject *self)
17{
18    Py_XDECREF(self->pgcnx);
19    if (self->col_types) {
20        PyMem_Free(self->col_types);
21    }
22    if (self->result) {
23        PQclear(self->result);
24    }
25
26    PyObject_Del(self);
27}
28
29/* Return query as string in human readable form. */
30static PyObject *
31query_str(queryObject *self)
32{
33    return format_result(self->result);
34}
35
36/* Return length of a query object. */
37static Py_ssize_t
38query_len(PyObject *self)
39{
40    PyObject *tmp;
41    Py_ssize_t len;
42
43    tmp = PyLong_FromLong(((queryObject*) self)->max_row);
44    len = PyLong_AsSsize_t(tmp);
45    Py_DECREF(tmp);
46    return len;
47}
48
49/* Return the value in the given column of the current row. */
50static PyObject *
51_query_value_in_column(queryObject *self, int column)
52{
53    char *s;
54    int type;
55
56    if (PQgetisnull(self->result, self->current_row, column)) {
57        Py_INCREF(Py_None);
58        return Py_None;
59    }
60
61    /* get the string representation of the value */
62    /* note: this is always null-terminated text format */
63    s = PQgetvalue(self->result, self->current_row, column);
64    /* get the PyGreSQL type of the column */
65    type = self->col_types[column];
66
67    /* cast the string representation into a Python object */
68    if (type & PYGRES_ARRAY)
69        return cast_array(s,
70            PQgetlength(self->result, self->current_row, column),
71            self->encoding, type, NULL, 0);
72    if (type == PYGRES_BYTEA)
73        return cast_bytea_text(s);
74    if (type == PYGRES_OTHER)
75        return cast_other(s,
76            PQgetlength(self->result, self->current_row, column),
77            self->encoding,
78            PQftype(self->result, column), self->pgcnx->cast_hook);
79    if (type & PYGRES_TEXT)
80        return cast_sized_text(s,
81            PQgetlength(self->result, self->current_row, column),
82            self->encoding, type);
83    return cast_unsized_simple(s, type);
84}
85
86/* Return the current row as a tuple. */
87static PyObject *
88_query_row_as_tuple(queryObject *self)
89{
90    PyObject *row_tuple = NULL;
91    int j;
92
93    if (!(row_tuple = PyTuple_New(self->num_fields))) {
94        return NULL;
95    }
96
97    for (j = 0; j < self->num_fields; ++j) {
98        PyObject *val = _query_value_in_column(self, j);
99        if (!val) {
100            Py_DECREF(row_tuple); return NULL;
101        }
102        PyTuple_SET_ITEM(row_tuple, j, val);
103    }
104
105    return row_tuple;
106}
107
108/* Return given item from a query object. */
109static PyObject *
110query_getitem(PyObject *self, Py_ssize_t i)
111{
112    queryObject *q = (queryObject *) self;
113    PyObject *tmp;
114    long row;
115
116    tmp = PyLong_FromSize_t(i);
117    row = PyLong_AsLong(tmp);
118    Py_DECREF(tmp);
119
120    if (row < 0 || row >= q->max_row) {
121        PyErr_SetNone(PyExc_IndexError);
122        return NULL;
123    }
124
125    q->current_row = row;
126    return _query_row_as_tuple(q);
127}
128
129/* __iter__() method of the queryObject:
130   Returns the default iterator yielding rows as tuples. */
131static PyObject* query_iter(queryObject *self)
132{
133    self->current_row = 0;
134    Py_INCREF(self);
135    return (PyObject*) self;
136}
137
138/* __next__() method of the queryObject:
139   Returns the current current row as a tuple and moves to the next one. */
140static PyObject *
141query_next(queryObject *self, PyObject *noargs)
142{
143    PyObject *row_tuple = NULL;
144
145    if (self->current_row >= self->max_row) {
146        PyErr_SetNone(PyExc_StopIteration);
147        return NULL;
148    }
149
150    row_tuple = _query_row_as_tuple(self);
151    if (row_tuple) ++self->current_row;
152    return row_tuple;
153}
154
155/* Get number of rows. */
156static char query_ntuples__doc__[] =
157"ntuples() -- return number of tuples returned by query";
158
159static PyObject *
160query_ntuples(queryObject *self, PyObject *noargs)
161{
162    return PyInt_FromLong(self->max_row);
163}
164
165/* List field names from query result. */
166static char query_listfields__doc__[] =
167"listfields() -- List field names from result";
168
169static PyObject *
170query_listfields(queryObject *self, PyObject *noargs)
171{
172    int i;
173    char *name;
174    PyObject *fieldstuple, *str;
175
176    /* builds tuple */
177    fieldstuple = PyTuple_New(self->num_fields);
178    if (fieldstuple) {
179        for (i = 0; i < self->num_fields; ++i) {
180            name = PQfname(self->result, i);
181            str = PyStr_FromString(name);
182            PyTuple_SET_ITEM(fieldstuple, i, str);
183        }
184    }
185    return fieldstuple;
186}
187
188/* Get field name from number in last result. */
189static char query_fieldname__doc__[] =
190"fieldname(num) -- return name of field from result from its position";
191
192static PyObject *
193query_fieldname(queryObject *self, PyObject *args)
194{
195    int i;
196    char *name;
197
198    /* gets args */
199    if (!PyArg_ParseTuple(args, "i", &i)) {
200        PyErr_SetString(PyExc_TypeError,
201                        "Method fieldname() takes an integer as argument");
202        return NULL;
203    }
204
205    /* checks number validity */
206    if (i >= self->num_fields) {
207        PyErr_SetString(PyExc_ValueError, "Invalid field number");
208        return NULL;
209    }
210
211    /* gets fields name and builds object */
212    name = PQfname(self->result, i);
213    return PyStr_FromString(name);
214}
215
216/* Get field number from name in last result. */
217static char query_fieldnum__doc__[] =
218"fieldnum(name) -- return position in query for field from its name";
219
220static PyObject *
221query_fieldnum(queryObject *self, PyObject *args)
222{
223    int num;
224    char *name;
225
226    /* gets args */
227    if (!PyArg_ParseTuple(args, "s", &name)) {
228        PyErr_SetString(PyExc_TypeError,
229                        "Method fieldnum() takes a string as argument");
230        return NULL;
231    }
232
233    /* gets field number */
234    if ((num = PQfnumber(self->result, name)) == -1) {
235        PyErr_SetString(PyExc_ValueError, "Unknown field");
236        return NULL;
237    }
238
239    return PyInt_FromLong(num);
240}
241
242/* Retrieve one row from the result as a tuple. */
243static char query_one__doc__[] =
244"one() -- Get one row from the result of a query\n\n"
245"Only one row from the result is returned as a tuple of fields.\n"
246"This method can be called multiple times to return more rows.\n"
247"It returns None if the result does not contain one more row.\n";
248
249static PyObject *
250query_one(queryObject *self, PyObject *noargs)
251{
252    PyObject *row_tuple;
253
254    if (self->current_row >= self->max_row) {
255        Py_INCREF(Py_None); return Py_None;
256    }
257
258    row_tuple = _query_row_as_tuple(self);
259    if (row_tuple) ++self->current_row;
260    return row_tuple;
261}
262
263/* Retrieve the single row from the result as a tuple. */
264static char query_single__doc__[] =
265"single() -- Get the result of a query as single row\n\n"
266"The single row from the query result is returned as a tuple of fields.\n"
267"This method returns the same single row when called multiple times.\n"
268"It raises an InvalidResultError if the result doesn't have exactly one row,\n"
269"which will be of type NoResultError or MultipleResultsError specifically.\n";
270
271static PyObject *
272query_single(queryObject *self, PyObject *noargs)
273{
274    PyObject *row_tuple;
275
276    if (self->max_row != 1) {
277        if (self->max_row)
278            set_error_msg(MultipleResultsError, "Multiple results found");
279        else
280            set_error_msg(NoResultError, "No result found");
281        return NULL;
282    }
283
284    self->current_row = 0;
285    row_tuple = _query_row_as_tuple(self);
286    if (row_tuple) ++self->current_row;
287    return row_tuple;
288}
289
290/* Retrieve the last query result as a list of tuples. */
291static char query_getresult__doc__[] =
292"getresult() -- Get the result of a query\n\n"
293"The result is returned as a list of rows, each one a tuple of fields\n"
294"in the order returned by the server.\n";
295
296static PyObject *
297query_getresult(queryObject *self, PyObject *noargs)
298{
299    PyObject *result_list;
300    int i;
301
302    if (!(result_list = PyList_New(self->max_row))) {
303        return NULL;
304    }
305
306    for (i = self->current_row = 0; i < self->max_row; ++i) {
307        PyObject *row_tuple = query_next(self, noargs);
308
309        if (!row_tuple) {
310            Py_DECREF(result_list); return NULL;
311        }
312        PyList_SET_ITEM(result_list, i, row_tuple);
313    }
314
315    return result_list;
316}
317
318/* Return the current row as a dict. */
319static PyObject *
320_query_row_as_dict(queryObject *self)
321{
322    PyObject *row_dict = NULL;
323    int j;
324
325    if (!(row_dict = PyDict_New())) {
326        return NULL;
327    }
328
329    for (j = 0; j < self->num_fields; ++j) {
330        PyObject *val = _query_value_in_column(self, j);
331
332        if (!val) {
333            Py_DECREF(row_dict); return NULL;
334        }
335        PyDict_SetItemString(row_dict, PQfname(self->result, j), val);
336        Py_DECREF(val);
337    }
338
339    return row_dict;
340}
341
342/* Return the current current row as a dict and move to the next one. */
343static PyObject *
344query_next_dict(queryObject *self, PyObject *noargs)
345{
346    PyObject *row_dict = NULL;
347
348    if (self->current_row >= self->max_row) {
349        PyErr_SetNone(PyExc_StopIteration);
350        return NULL;
351    }
352
353    row_dict = _query_row_as_dict(self);
354    if (row_dict) ++self->current_row;
355    return row_dict;
356}
357
358/* Retrieve one row from the result as a dictionary. */
359static char query_onedict__doc__[] =
360"onedict() -- Get one row from the result of a query\n\n"
361"Only one row from the result is returned as a dictionary with\n"
362"the field names used as the keys.\n"
363"This method can be called multiple times to return more rows.\n"
364"It returns None if the result does not contain one more row.\n";
365
366static PyObject *
367query_onedict(queryObject *self, PyObject *noargs)
368{
369    PyObject *row_dict;
370
371    if (self->current_row >= self->max_row) {
372        Py_INCREF(Py_None); return Py_None;
373    }
374
375    row_dict = _query_row_as_dict(self);
376    if (row_dict) ++self->current_row;
377    return row_dict;
378}
379
380/* Retrieve the single row from the result as a dictionary. */
381static char query_singledict__doc__[] =
382"singledict() -- Get the result of a query as single row\n\n"
383"The single row from the query result is returned as a dictionary with\n"
384"the field names used as the keys.\n"
385"This method returns the same single row when called multiple times.\n"
386"It raises an InvalidResultError if the result doesn't have exactly one row,\n"
387"which will be of type NoResultError or MultipleResultsError specifically.\n";
388
389static PyObject *
390query_singledict(queryObject *self, PyObject *noargs)
391{
392    PyObject *row_dict;
393
394    if (self->max_row != 1) {
395        if (self->max_row)
396            set_error_msg(MultipleResultsError, "Multiple results found");
397        else
398            set_error_msg(NoResultError, "No result found");
399        return NULL;
400    }
401
402    self->current_row = 0;
403    row_dict = _query_row_as_dict(self);
404    if (row_dict) ++self->current_row;
405    return row_dict;
406}
407
408/* Retrieve the last query result as a list of dictionaries. */
409static char query_dictresult__doc__[] =
410"dictresult() -- Get the result of a query\n\n"
411"The result is returned as a list of rows, each one a dictionary with\n"
412"the field names used as the keys.\n";
413
414static PyObject *
415query_dictresult(queryObject *self, PyObject *noargs)
416{
417    PyObject *result_list;
418    int i;
419
420    if (!(result_list = PyList_New(self->max_row))) {
421        return NULL;
422    }
423
424    for (i = self->current_row = 0; i < self->max_row; ++i) {
425        PyObject *row_dict = query_next_dict(self, noargs);
426
427        if (!row_dict) {
428            Py_DECREF(result_list); return NULL;
429        }
430        PyList_SET_ITEM(result_list, i, row_dict);
431    }
432
433    return result_list;
434}
435
436/* Retrieve last result as iterator of dictionaries. */
437static char query_dictiter__doc__[] =
438"dictiter() -- Get the result of a query\n\n"
439"The result is returned as an iterator of rows, each one a a dictionary\n"
440"with the field names used as the keys.\n";
441
442static PyObject *
443query_dictiter(queryObject *self, PyObject *noargs)
444{
445    if (!dictiter) {
446        return query_dictresult(self, noargs);
447    }
448
449    return PyObject_CallFunction(dictiter, "(O)", self);
450}
451
452/* Retrieve one row from the result as a named tuple. */
453static char query_onenamed__doc__[] =
454"onenamed() -- Get one row from the result of a query\n\n"
455"Only one row from the result is returned as a named tuple of fields.\n"
456"This method can be called multiple times to return more rows.\n"
457"It returns None if the result does not contain one more row.\n";
458
459static PyObject *
460query_onenamed(queryObject *self, PyObject *noargs)
461{
462    if (!namednext) {
463        return query_one(self, noargs);
464    }
465
466    if (self->current_row >= self->max_row) {
467        Py_INCREF(Py_None); return Py_None;
468    }
469
470    return PyObject_CallFunction(namednext, "(O)", self);
471}
472
473/* Retrieve the single row from the result as a tuple. */
474static char query_singlenamed__doc__[] =
475"singlenamed() -- Get the result of a query as single row\n\n"
476"The single row from the query result is returned as named tuple of fields.\n"
477"This method returns the same single row when called multiple times.\n"
478"It raises an InvalidResultError if the result doesn't have exactly one row,\n"
479"which will be of type NoResultError or MultipleResultsError specifically.\n";
480
481static PyObject *
482query_singlenamed(queryObject *self, PyObject *noargs)
483{
484    if (!namednext) {
485        return query_single(self, noargs);
486    }
487
488    if (self->max_row != 1) {
489        if (self->max_row)
490            set_error_msg(MultipleResultsError, "Multiple results found");
491        else
492            set_error_msg(NoResultError, "No result found");
493        return NULL;
494    }
495
496    self->current_row = 0;
497    return PyObject_CallFunction(namednext, "(O)", self);
498}
499
500/* Retrieve last result as list of named tuples. */
501static char query_namedresult__doc__[] =
502"namedresult() -- Get the result of a query\n\n"
503"The result is returned as a list of rows, each one a named tuple of fields\n"
504"in the order returned by the server.\n";
505
506static PyObject *
507query_namedresult(queryObject *self, PyObject *noargs)
508{
509    PyObject *res, *res_list;
510
511    if (!namediter) {
512        return query_getresult(self, noargs);
513    }
514
515    res = PyObject_CallFunction(namediter, "(O)", self);
516    if (!res) return NULL;
517    if (PyList_Check(res)) return res;
518    res_list = PySequence_List(res);
519    Py_DECREF(res);
520    return res_list;
521}
522
523/* Retrieve last result as iterator of named tuples. */
524static char query_namediter__doc__[] =
525"namediter() -- Get the result of a query\n\n"
526"The result is returned as an iterator of rows, each one a named tuple\n"
527"of fields in the order returned by the server.\n";
528
529static PyObject *
530query_namediter(queryObject *self, PyObject *noargs)
531{
532    PyObject *res, *res_iter;
533
534    if (!namediter) {
535        return query_iter(self);
536    }
537
538    res = PyObject_CallFunction(namediter, "(O)", self);
539    if (!res) return NULL;
540    if (!PyList_Check(res)) return res;
541    res_iter = (Py_TYPE(res)->tp_iter)((PyObject *) self);
542    Py_DECREF(res);
543    return res_iter;
544}
545
546/* Retrieve the last query result as a list of scalar values. */
547static char query_scalarresult__doc__[] =
548"scalarresult() -- Get query result as scalars\n\n"
549"The result is returned as a list of scalar values where the values\n"
550"are the first fields of the rows in the order returned by the server.\n";
551
552static PyObject *
553query_scalarresult(queryObject *self, PyObject *noargs)
554{
555    PyObject *result_list;
556
557    if (!self->num_fields) {
558        set_error_msg(ProgrammingError, "No fields in result");
559        return NULL;
560    }
561
562    if (!(result_list = PyList_New(self->max_row))) {
563        return NULL;
564    }
565
566    for (self->current_row = 0;
567         self->current_row < self->max_row;
568         ++self->current_row)
569    {
570        PyObject *value = _query_value_in_column(self, 0);
571
572        if (!value) {
573            Py_DECREF(result_list); return NULL;
574        }
575        PyList_SET_ITEM(result_list, self->current_row, value);
576    }
577
578    return result_list;
579}
580
581/* Retrieve the last query result as iterator of scalar values. */
582static char query_scalariter__doc__[] =
583"scalariter() -- Get query result as scalars\n\n"
584"The result is returned as an iterator of scalar values where the values\n"
585"are the first fields of the rows in the order returned by the server.\n";
586
587static PyObject *
588query_scalariter(queryObject *self, PyObject *noargs)
589{
590    if (!scalariter) {
591        return query_scalarresult(self, noargs);
592    }
593
594    if (!self->num_fields) {
595        set_error_msg(ProgrammingError, "No fields in result");
596        return NULL;
597    }
598
599    return PyObject_CallFunction(scalariter, "(O)", self);
600}
601
602/* Retrieve one result as scalar value. */
603static char query_onescalar__doc__[] =
604"onescalar() -- Get one scalar value from the result of a query\n\n"
605"Returns the first field of the next row from the result as a scalar value.\n"
606"This method can be called multiple times to return more rows as scalars.\n"
607"It returns None if the result does not contain one more row.\n";
608
609static PyObject *
610query_onescalar(queryObject *self, PyObject *noargs)
611{
612    PyObject *value;
613
614    if (!self->num_fields) {
615        set_error_msg(ProgrammingError, "No fields in result");
616        return NULL;
617    }
618
619    if (self->current_row >= self->max_row) {
620        Py_INCREF(Py_None); return Py_None;
621    }
622
623    value = _query_value_in_column(self, 0);
624    if (value) ++self->current_row;
625    return value;
626}
627
628/* Retrieves the single row from the result as a tuple. */
629static char query_singlescalar__doc__[] =
630"singlescalar() -- Get scalar value from single result of a query\n\n"
631"Returns the first field of the next row from the result as a scalar value.\n"
632"This method returns the same single row when called multiple times.\n"
633"It raises an InvalidResultError if the result doesn't have exactly one row,\n"
634"which will be of type NoResultError or MultipleResultsError specifically.\n";
635
636static PyObject *
637query_singlescalar(queryObject *self, PyObject *noargs)
638{
639    PyObject *value;
640
641    if (!self->num_fields) {
642        set_error_msg(ProgrammingError, "No fields in result");
643        return NULL;
644    }
645
646    if (self->max_row != 1) {
647        if (self->max_row)
648            set_error_msg(MultipleResultsError, "Multiple results found");
649        else
650            set_error_msg(NoResultError, "No result found");
651        return NULL;
652    }
653
654    self->current_row = 0;
655    value = _query_value_in_column(self, 0);
656    if (value) ++self->current_row;
657    return value;
658}
659
660/* Query sequence protocol methods */
661static PySequenceMethods query_sequence_methods = {
662    (lenfunc) query_len,           /* sq_length */
663    0,                             /* sq_concat */
664    0,                             /* sq_repeat */
665    (ssizeargfunc) query_getitem,  /* sq_item */
666    0,                             /* sq_ass_item */
667    0,                             /* sq_contains */
668    0,                             /* sq_inplace_concat */
669    0,                             /* sq_inplace_repeat */
670};
671
672/* Query object methods */
673static struct PyMethodDef query_methods[] = {
674    {"getresult", (PyCFunction) query_getresult,
675        METH_NOARGS, query_getresult__doc__},
676    {"dictresult", (PyCFunction) query_dictresult,
677        METH_NOARGS, query_dictresult__doc__},
678    {"dictiter", (PyCFunction) query_dictiter,
679        METH_NOARGS, query_dictiter__doc__},
680    {"namedresult", (PyCFunction) query_namedresult,
681        METH_NOARGS, query_namedresult__doc__},
682    {"namediter", (PyCFunction) query_namediter,
683        METH_NOARGS, query_namediter__doc__},
684    {"one", (PyCFunction) query_one,
685        METH_NOARGS, query_one__doc__},
686    {"single", (PyCFunction) query_single,
687        METH_NOARGS, query_single__doc__},
688    {"onedict", (PyCFunction) query_onedict,
689        METH_NOARGS, query_onedict__doc__},
690    {"singledict", (PyCFunction) query_singledict,
691        METH_NOARGS, query_singledict__doc__},
692    {"onenamed", (PyCFunction) query_onenamed,
693        METH_NOARGS, query_onenamed__doc__},
694    {"singlenamed", (PyCFunction) query_singlenamed,
695        METH_NOARGS, query_singlenamed__doc__},
696    {"scalarresult", (PyCFunction) query_scalarresult,
697        METH_NOARGS, query_scalarresult__doc__},
698    {"scalariter", (PyCFunction) query_scalariter,
699        METH_NOARGS, query_scalariter__doc__},
700    {"onescalar", (PyCFunction) query_onescalar,
701        METH_NOARGS, query_onescalar__doc__},
702    {"singlescalar", (PyCFunction) query_singlescalar,
703        METH_NOARGS, query_singlescalar__doc__},
704    {"fieldname", (PyCFunction) query_fieldname,
705        METH_VARARGS, query_fieldname__doc__},
706    {"fieldnum", (PyCFunction) query_fieldnum,
707        METH_VARARGS, query_fieldnum__doc__},
708    {"listfields", (PyCFunction) query_listfields,
709        METH_NOARGS, query_listfields__doc__},
710    {"ntuples", (PyCFunction) query_ntuples,
711        METH_NOARGS, query_ntuples__doc__},
712    {NULL, NULL}
713};
714
715static char query__doc__[] = "PyGreSQL query object";
716
717/* Query type definition */
718static PyTypeObject queryType = {
719    PyVarObject_HEAD_INIT(NULL, 0)
720    "pg.Query",                  /* tp_name */
721    sizeof(queryObject),         /* tp_basicsize */
722    0,                           /* tp_itemsize */
723    /* methods */
724    (destructor) query_dealloc,  /* tp_dealloc */
725    0,                           /* tp_print */
726    0,                           /* tp_getattr */
727    0,                           /* tp_setattr */
728    0,                           /* tp_compare */
729    0,                           /* tp_repr */
730    0,                           /* tp_as_number */
731    &query_sequence_methods,     /* tp_as_sequence */
732    0,                           /* tp_as_mapping */
733    0,                           /* tp_hash */
734    0,                           /* tp_call */
735    (reprfunc) query_str,        /* tp_str */
736    PyObject_GenericGetAttr,     /* tp_getattro */
737    0,                           /* tp_setattro */
738    0,                           /* tp_as_buffer */
739    Py_TPFLAGS_DEFAULT
740        |Py_TPFLAGS_HAVE_ITER,   /* tp_flags */
741    query__doc__,               /* tp_doc */
742    0,                           /* tp_traverse */
743    0,                           /* tp_clear */
744    0,                           /* tp_richcompare */
745    0,                           /* tp_weaklistoffset */
746    (getiterfunc) query_iter,    /* tp_iter */
747    (iternextfunc) query_next,   /* tp_iternext */
748    query_methods,               /* tp_methods */
749};
Note: See TracBrowser for help on using the repository browser.