source: trunk/pgsource.c

Last change on this file was 1002, checked in by cito, 3 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: 23.5 KB
Line 
1/*
2 * $Id: pgsource.c 985 2019-04-22 22:07:43Z cito $
3 *
4 * PyGreSQL - a Python interface for the PostgreSQL database.
5 *
6 * The source 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 source object. */
15static void
16source_dealloc(sourceObject *self)
17{
18    if (self->result)
19        PQclear(self->result);
20
21    Py_XDECREF(self->pgcnx);
22    PyObject_Del(self);
23}
24
25/* Return source object as string in human readable form. */
26static PyObject *
27source_str(sourceObject *self)
28{
29    switch (self->result_type) {
30        case RESULT_DQL:
31            return format_result(self->result);
32        case RESULT_DDL:
33        case RESULT_DML:
34            return PyStr_FromString(PQcmdStatus(self->result));
35        case RESULT_EMPTY:
36        default:
37            return PyStr_FromString("(empty PostgreSQL source object)");
38    }
39}
40
41/* Check source object validity. */
42static int
43_check_source_obj(sourceObject *self, int level)
44{
45    if (!self->valid) {
46        set_error_msg(OperationalError, "Object has been closed");
47        return 0;
48    }
49
50    if ((level & CHECK_RESULT) && !self->result) {
51        set_error_msg(DatabaseError, "No result");
52        return 0;
53    }
54
55    if ((level & CHECK_DQL) && self->result_type != RESULT_DQL) {
56        set_error_msg(DatabaseError, "Last query did not return tuples");
57        return 0;
58    }
59
60    if ((level & CHECK_CNX) && !_check_cnx_obj(self->pgcnx)) {
61        return 0;
62    }
63
64    return 1;
65}
66
67/* Get source object attributes. */
68static PyObject *
69source_getattr(sourceObject *self, PyObject *nameobj)
70{
71    const char *name = PyStr_AsString(nameobj);
72
73    /* pg connection object */
74    if (!strcmp(name, "pgcnx")) {
75        if (_check_source_obj(self, 0)) {
76            Py_INCREF(self->pgcnx);
77            return (PyObject *) (self->pgcnx);
78        }
79        Py_INCREF(Py_None);
80        return Py_None;
81    }
82
83    /* arraysize */
84    if (!strcmp(name, "arraysize"))
85        return PyInt_FromLong(self->arraysize);
86
87    /* resulttype */
88    if (!strcmp(name, "resulttype"))
89        return PyInt_FromLong(self->result_type);
90
91    /* ntuples */
92    if (!strcmp(name, "ntuples"))
93        return PyInt_FromLong(self->max_row);
94
95    /* nfields */
96    if (!strcmp(name, "nfields"))
97        return PyInt_FromLong(self->num_fields);
98
99    /* seeks name in methods (fallback) */
100    return PyObject_GenericGetAttr((PyObject *) self, nameobj);
101}
102
103/* Set source object attributes. */
104static int
105source_setattr(sourceObject *self, char *name, PyObject *v)
106{
107    /* arraysize */
108    if (!strcmp(name, "arraysize")) {
109        if (!PyInt_Check(v)) {
110            PyErr_SetString(PyExc_TypeError, "arraysize must be integer");
111            return -1;
112        }
113
114        self->arraysize = PyInt_AsLong(v);
115        return 0;
116    }
117
118    /* unknown attribute */
119    PyErr_SetString(PyExc_TypeError, "Not a writable attribute");
120    return -1;
121}
122
123/* Close object. */
124static char source_close__doc__[] =
125"close() -- close query object without deleting it\n\n"
126"All instances of the query object can no longer be used after this call.\n";
127
128static PyObject *
129source_close(sourceObject *self, PyObject *noargs)
130{
131    /* frees result if necessary and invalidates object */
132    if (self->result) {
133        PQclear(self->result);
134        self->result_type = RESULT_EMPTY;
135        self->result = NULL;
136    }
137
138    self->valid = 0;
139
140    /* return None */
141    Py_INCREF(Py_None);
142    return Py_None;
143}
144
145/* Database query. */
146static char source_execute__doc__[] =
147"execute(sql) -- execute a SQL statement (string)\n\n"
148"On success, this call returns the number of affected rows, or None\n"
149"for DQL (SELECT, ...) statements.  The fetch (fetch(), fetchone()\n"
150"and fetchall()) methods can be used to get result rows.\n";
151
152static PyObject *
153source_execute(sourceObject *self, PyObject *sql)
154{
155    PyObject *tmp_obj = NULL;  /* auxiliary string object */
156    char *query;
157    int encoding;
158
159    /* checks validity */
160    if (!_check_source_obj(self, CHECK_CNX)) {
161        return NULL;
162    }
163
164    encoding = PQclientEncoding(self->pgcnx->cnx);
165
166    if (PyBytes_Check(sql)) {
167        query = PyBytes_AsString(sql);
168    }
169    else if (PyUnicode_Check(sql)) {
170        tmp_obj = get_encoded_string(sql, encoding);
171        if (!tmp_obj) return NULL; /* pass the UnicodeEncodeError */
172        query = PyBytes_AsString(tmp_obj);
173    }
174    else {
175        PyErr_SetString(PyExc_TypeError,
176                        "Method execute() expects a string as argument");
177        return NULL;
178    }
179
180    /* frees previous result */
181    if (self->result) {
182        PQclear(self->result);
183        self->result = NULL;
184    }
185    self->max_row = 0;
186    self->current_row = 0;
187    self->num_fields = 0;
188    self->encoding = encoding;
189
190    /* gets result */
191    Py_BEGIN_ALLOW_THREADS
192    self->result = PQexec(self->pgcnx->cnx, query);
193    Py_END_ALLOW_THREADS
194
195    /* we don't need the auxiliary string any more */
196    Py_XDECREF(tmp_obj);
197
198    /* checks result validity */
199    if (!self->result) {
200        PyErr_SetString(PyExc_ValueError, PQerrorMessage(self->pgcnx->cnx));
201        return NULL;
202    }
203
204    /* this may have changed the datestyle, so we reset the date format
205       in order to force fetching it newly when next time requested */
206    self->pgcnx->date_format = date_format; /* this is normally NULL */
207
208    /* checks result status */
209    switch (PQresultStatus(self->result)) {
210        /* query succeeded */
211        case PGRES_TUPLES_OK:   /* DQL: returns None (DB-SIG compliant) */
212            self->result_type = RESULT_DQL;
213            self->max_row = PQntuples(self->result);
214            self->num_fields = PQnfields(self->result);
215            Py_INCREF(Py_None);
216            return Py_None;
217        case PGRES_COMMAND_OK:  /* other requests */
218        case PGRES_COPY_OUT:
219        case PGRES_COPY_IN:
220            {
221                long num_rows;
222                char *tmp;
223
224                tmp = PQcmdTuples(self->result);
225                if (tmp[0]) {
226                    self->result_type = RESULT_DML;
227                    num_rows = atol(tmp);
228                }
229                else {
230                    self->result_type = RESULT_DDL;
231                    num_rows = -1;
232                }
233                return PyInt_FromLong(num_rows);
234            }
235
236        /* query failed */
237        case PGRES_EMPTY_QUERY:
238            PyErr_SetString(PyExc_ValueError, "Empty query");
239            break;
240        case PGRES_BAD_RESPONSE:
241        case PGRES_FATAL_ERROR:
242        case PGRES_NONFATAL_ERROR:
243            set_error(ProgrammingError, "Cannot execute command",
244                self->pgcnx->cnx, self->result);
245            break;
246        default:
247            set_error_msg(InternalError,
248                          "Internal error: unknown result status");
249    }
250
251    /* frees result and returns error */
252    PQclear(self->result);
253    self->result = NULL;
254    self->result_type = RESULT_EMPTY;
255    return NULL;
256}
257
258/* Get oid status for last query (valid for INSERTs, 0 for other). */
259static char source_oidstatus__doc__[] =
260"oidstatus() -- return oid of last inserted row (if available)";
261
262static PyObject *
263source_oidstatus(sourceObject *self, PyObject *noargs)
264{
265    Oid oid;
266
267    /* checks validity */
268    if (!_check_source_obj(self, CHECK_RESULT)) {
269        return NULL;
270    }
271
272    /* retrieves oid status */
273    if ((oid = PQoidValue(self->result)) == InvalidOid) {
274        Py_INCREF(Py_None);
275        return Py_None;
276    }
277
278    return PyInt_FromLong(oid);
279}
280
281/* Fetch rows from last result. */
282static char source_fetch__doc__[] =
283"fetch(num) -- return the next num rows from the last result in a list\n\n"
284"If num parameter is omitted arraysize attribute value is used.\n"
285"If size equals -1, all rows are fetched.\n";
286
287static PyObject *
288source_fetch(sourceObject *self, PyObject *args)
289{
290    PyObject *res_list;
291    int i, k;
292    long size;
293#if IS_PY3
294    int encoding;
295#endif
296
297    /* checks validity */
298    if (!_check_source_obj(self, CHECK_RESULT | CHECK_DQL | CHECK_CNX)) {
299        return NULL;
300    }
301
302    /* checks args */
303    size = self->arraysize;
304    if (!PyArg_ParseTuple(args, "|l", &size)) {
305        PyErr_SetString(PyExc_TypeError,
306                        "fetch(num), with num (integer, optional)");
307        return NULL;
308    }
309
310    /* seeks last line */
311    /* limit size to be within the amount of data we actually have */
312    if (size == -1 || (self->max_row - self->current_row) < size) {
313        size = self->max_row - self->current_row;
314    }
315
316    /* allocate list for result */
317    if (!(res_list = PyList_New(0))) return NULL;
318
319#if IS_PY3
320    encoding = self->encoding;
321#endif
322
323    /* builds result */
324    for (i = 0, k = self->current_row; i < size; ++i, ++k) {
325        PyObject *rowtuple;
326        int j;
327
328        if (!(rowtuple = PyTuple_New(self->num_fields))) {
329            Py_DECREF(res_list); return NULL;
330        }
331
332        for (j = 0; j < self->num_fields; ++j) {
333            PyObject *str;
334
335            if (PQgetisnull(self->result, k, j)) {
336                Py_INCREF(Py_None);
337                str = Py_None;
338            }
339            else {
340                char *s = PQgetvalue(self->result, k, j);
341                Py_ssize_t size = PQgetlength(self->result, k, j);
342#if IS_PY3
343                if (PQfformat(self->result, j) == 0) { /* textual format */
344                    str = get_decoded_string(s, size, encoding);
345                    if (!str) /* cannot decode */
346                        str = PyBytes_FromStringAndSize(s, size);
347                }
348                else
349#endif
350                str = PyBytes_FromStringAndSize(s, size);
351            }
352            PyTuple_SET_ITEM(rowtuple, j, str);
353        }
354
355        if (PyList_Append(res_list, rowtuple)) {
356            Py_DECREF(rowtuple); Py_DECREF(res_list); return NULL;
357        }
358        Py_DECREF(rowtuple);
359    }
360
361    self->current_row = k;
362    return res_list;
363}
364
365/* Change current row (internal wrapper for all "move" methods). */
366static PyObject *
367_source_move(sourceObject *self, int move)
368{
369    /* checks validity */
370    if (!_check_source_obj(self, CHECK_RESULT | CHECK_DQL)) {
371        return NULL;
372    }
373
374    /* changes the current row */
375    switch (move) {
376        case QUERY_MOVEFIRST:
377            self->current_row = 0;
378            break;
379        case QUERY_MOVELAST:
380            self->current_row = self->max_row - 1;
381            break;
382        case QUERY_MOVENEXT:
383            if (self->current_row != self->max_row)
384                ++self->current_row;
385            break;
386        case QUERY_MOVEPREV:
387            if (self->current_row > 0)
388                self->current_row--;
389            break;
390    }
391
392    Py_INCREF(Py_None);
393    return Py_None;
394}
395
396/* Move to first result row. */
397static char source_movefirst__doc__[] =
398"movefirst() -- move to first result row";
399
400static PyObject *
401source_movefirst(sourceObject *self, PyObject *noargs)
402{
403    return _source_move(self, QUERY_MOVEFIRST);
404}
405
406/* Move to last result row. */
407static char source_movelast__doc__[] =
408"movelast() -- move to last valid result row";
409
410static PyObject *
411source_movelast(sourceObject *self, PyObject *noargs)
412{
413    return _source_move(self, QUERY_MOVELAST);
414}
415
416/* Move to next result row. */
417static char source_movenext__doc__[] =
418"movenext() -- move to next result row";
419
420static PyObject *
421source_movenext(sourceObject *self, PyObject *noargs)
422{
423    return _source_move(self, QUERY_MOVENEXT);
424}
425
426/* Move to previous result row. */
427static char source_moveprev__doc__[] =
428"moveprev() -- move to previous result row";
429
430static PyObject *
431source_moveprev(sourceObject *self, PyObject *noargs)
432{
433    return _source_move(self, QUERY_MOVEPREV);
434}
435
436/* Put copy data. */
437static char source_putdata__doc__[] =
438"putdata(buffer) -- send data to server during copy from stdin";
439
440static PyObject *
441source_putdata(sourceObject *self, PyObject *buffer)
442{
443    PyObject *tmp_obj = NULL;  /* an auxiliary object */
444    char *buf;                 /* the buffer as encoded string */
445    Py_ssize_t nbytes;         /* length of string */
446    char *errormsg = NULL;     /* error message */
447    int res;                   /* direct result of the operation */
448    PyObject *ret;             /* return value */
449
450    /* checks validity */
451    if (!_check_source_obj(self, CHECK_CNX)) {
452        return NULL;
453    }
454
455    /* make sure that the connection object is valid */
456    if (!self->pgcnx->cnx) {
457        return NULL;
458    }
459
460    if (buffer == Py_None) {
461        /* pass None for terminating the operation */
462        buf = errormsg = NULL;
463    }
464    else if (PyBytes_Check(buffer)) {
465        /* or pass a byte string */
466        PyBytes_AsStringAndSize(buffer, &buf, &nbytes);
467    }
468    else if (PyUnicode_Check(buffer)) {
469        /* or pass a unicode string */
470        tmp_obj = get_encoded_string(
471            buffer, PQclientEncoding(self->pgcnx->cnx));
472        if (!tmp_obj) return NULL; /* pass the UnicodeEncodeError */
473        PyBytes_AsStringAndSize(tmp_obj, &buf, &nbytes);
474    }
475    else if (PyErr_GivenExceptionMatches(buffer, PyExc_BaseException)) {
476        /* or pass a Python exception for sending an error message */
477        tmp_obj = PyObject_Str(buffer);
478        if (PyUnicode_Check(tmp_obj)) {
479            PyObject *obj = tmp_obj;
480
481            tmp_obj = get_encoded_string(
482                obj, PQclientEncoding(self->pgcnx->cnx));
483            Py_DECREF(obj);
484            if (!tmp_obj) return NULL; /* pass the UnicodeEncodeError */
485        }
486        errormsg = PyBytes_AsString(tmp_obj);
487        buf = NULL;
488    }
489    else {
490        PyErr_SetString(PyExc_TypeError,
491                        "Method putdata() expects a buffer, None"
492                        " or an exception as argument");
493        return NULL;
494    }
495
496    /* checks validity */
497    if (!_check_source_obj(self, CHECK_CNX | CHECK_RESULT) ||
498        PQresultStatus(self->result) != PGRES_COPY_IN)
499    {
500        PyErr_SetString(PyExc_IOError,
501                        "Connection is invalid or not in copy_in state");
502        Py_XDECREF(tmp_obj);
503        return NULL;
504    }
505
506    if (buf) {
507        res = nbytes ? PQputCopyData(self->pgcnx->cnx, buf, (int) nbytes) : 1;
508    }
509    else {
510        res = PQputCopyEnd(self->pgcnx->cnx, errormsg);
511    }
512
513    Py_XDECREF(tmp_obj);
514
515    if (res != 1) {
516        PyErr_SetString(PyExc_IOError, PQerrorMessage(self->pgcnx->cnx));
517        return NULL;
518    }
519
520    if (buf) { /* buffer has been sent */
521        ret = Py_None;
522        Py_INCREF(ret);
523    }
524    else { /* copy is done */
525        PGresult *result; /* final result of the operation */
526
527        Py_BEGIN_ALLOW_THREADS;
528        result = PQgetResult(self->pgcnx->cnx);
529        Py_END_ALLOW_THREADS;
530
531        if (PQresultStatus(result) == PGRES_COMMAND_OK) {
532            char *tmp;
533            long num_rows;
534
535            tmp = PQcmdTuples(result);
536            num_rows = tmp[0] ? atol(tmp) : -1;
537            ret = PyInt_FromLong(num_rows);
538        }
539        else {
540            if (!errormsg) errormsg = PQerrorMessage(self->pgcnx->cnx);
541            PyErr_SetString(PyExc_IOError, errormsg);
542            ret = NULL;
543        }
544
545        PQclear(self->result);
546        self->result = NULL;
547        self->result_type = RESULT_EMPTY;
548    }
549
550    return ret; /* None or number of rows */
551}
552
553/* Get copy data. */
554static char source_getdata__doc__[] =
555"getdata(decode) -- receive data to server during copy to stdout";
556
557static PyObject *
558source_getdata(sourceObject *self, PyObject *args)
559{
560    int *decode = 0;    /* decode flag */
561    char *buffer;       /* the copied buffer as encoded byte string */
562    Py_ssize_t nbytes;  /* length of the byte string */
563    PyObject *ret;      /* return value */
564
565    /* checks validity */
566    if (!_check_source_obj(self, CHECK_CNX)) {
567        return NULL;
568    }
569
570    /* make sure that the connection object is valid */
571    if (!self->pgcnx->cnx) {
572        return NULL;
573    }
574
575    if (!PyArg_ParseTuple(args, "|i", &decode)) {
576        return NULL;
577    }
578
579    /* checks validity */
580    if (!_check_source_obj(self, CHECK_CNX | CHECK_RESULT) ||
581        PQresultStatus(self->result) != PGRES_COPY_OUT)
582    {
583        PyErr_SetString(PyExc_IOError,
584                        "Connection is invalid or not in copy_out state");
585        return NULL;
586    }
587
588    nbytes = PQgetCopyData(self->pgcnx->cnx, &buffer, 0);
589
590    if (!nbytes || nbytes < -1) { /* an error occurred */
591        PyErr_SetString(PyExc_IOError, PQerrorMessage(self->pgcnx->cnx));
592        return NULL;
593    }
594
595    if (nbytes == -1) { /* copy is done */
596        PGresult *result; /* final result of the operation */
597
598        Py_BEGIN_ALLOW_THREADS;
599        result = PQgetResult(self->pgcnx->cnx);
600        Py_END_ALLOW_THREADS;
601
602        if (PQresultStatus(result) == PGRES_COMMAND_OK) {
603            char *tmp;
604            long num_rows;
605
606            tmp = PQcmdTuples(result);
607            num_rows = tmp[0] ? atol(tmp) : -1;
608            ret = PyInt_FromLong(num_rows);
609        }
610        else {
611            PyErr_SetString(PyExc_IOError, PQerrorMessage(self->pgcnx->cnx));
612            ret = NULL;
613        }
614
615        PQclear(self->result);
616        self->result = NULL;
617        self->result_type = RESULT_EMPTY;
618    }
619    else { /* a row has been returned */
620        ret = decode ? get_decoded_string(
621                buffer, nbytes, PQclientEncoding(self->pgcnx->cnx)) :
622            PyBytes_FromStringAndSize(buffer, nbytes);
623        PQfreemem(buffer);
624    }
625
626    return ret; /* buffer or number of rows */
627}
628
629/* Find field number from string/integer (internal use only). */
630static int
631_source_fieldindex(sourceObject *self, PyObject *param, const char *usage)
632{
633    int num;
634
635    /* checks validity */
636    if (!_check_source_obj(self, CHECK_RESULT | CHECK_DQL))
637        return -1;
638
639    /* gets field number */
640    if (PyStr_Check(param)) {
641        num = PQfnumber(self->result, PyBytes_AsString(param));
642    }
643    else if (PyInt_Check(param)) {
644        num = (int) PyInt_AsLong(param);
645    }
646    else {
647        PyErr_SetString(PyExc_TypeError, usage);
648        return -1;
649    }
650
651    /* checks field validity */
652    if (num < 0 || num >= self->num_fields) {
653        PyErr_SetString(PyExc_ValueError, "Unknown field");
654        return -1;
655    }
656
657    return num;
658}
659
660/* Build field information from position (internal use only). */
661static PyObject *
662_source_buildinfo(sourceObject *self, int num)
663{
664    PyObject *result;
665
666    /* allocates tuple */
667    result = PyTuple_New(5);
668    if (!result) {
669        return NULL;
670    }
671
672    /* affects field information */
673    PyTuple_SET_ITEM(result, 0, PyInt_FromLong(num));
674    PyTuple_SET_ITEM(result, 1,
675        PyStr_FromString(PQfname(self->result, num)));
676    PyTuple_SET_ITEM(result, 2,
677        PyInt_FromLong(PQftype(self->result, num)));
678    PyTuple_SET_ITEM(result, 3,
679        PyInt_FromLong(PQfsize(self->result, num)));
680    PyTuple_SET_ITEM(result, 4,
681        PyInt_FromLong(PQfmod(self->result, num)));
682
683    return result;
684}
685
686/* Lists fields info. */
687static char source_listinfo__doc__[] =
688"listinfo() -- get information for all fields (position, name, type oid)";
689
690static PyObject *
691source_listInfo(sourceObject *self, PyObject *noargs)
692{
693    PyObject *result, *info;
694    int i;
695
696    /* checks validity */
697    if (!_check_source_obj(self, CHECK_RESULT | CHECK_DQL)) {
698        return NULL;
699    }
700
701    /* builds result */
702    if (!(result = PyTuple_New(self->num_fields))) {
703        return NULL;
704    }
705
706    for (i = 0; i < self->num_fields; ++i) {
707        info = _source_buildinfo(self, i);
708        if (!info) {
709            Py_DECREF(result);
710            return NULL;
711        }
712        PyTuple_SET_ITEM(result, i, info);
713    }
714
715    /* returns result */
716    return result;
717};
718
719/* List fields information for last result. */
720static char source_fieldinfo__doc__[] =
721"fieldinfo(desc) -- get specified field info (position, name, type oid)";
722
723static PyObject *
724source_fieldinfo(sourceObject *self, PyObject *desc)
725{
726    int num;
727
728    /* checks args and validity */
729    if ((num = _source_fieldindex(
730        self, desc,
731        "Method fieldinfo() needs a string or integer as argument")) == -1)
732    {
733        return NULL;
734    }
735
736    /* returns result */
737    return _source_buildinfo(self, num);
738};
739
740/* Retrieve field value. */
741static char source_field__doc__[] =
742"field(desc) -- return specified field value";
743
744static PyObject *
745source_field(sourceObject *self, PyObject *desc)
746{
747    int num;
748
749    /* checks args and validity */
750    if ((num = _source_fieldindex(
751        self, desc,
752        "Method field() needs a string or integer as argument")) == -1)
753    {
754        return NULL;
755    }
756
757    return PyStr_FromString(
758        PQgetvalue(self->result, self->current_row, num));
759}
760
761/* Get the list of source object attributes. */
762static PyObject *
763source_dir(connObject *self, PyObject *noargs)
764{
765    PyObject *attrs;
766
767    attrs = PyObject_Dir(PyObject_Type((PyObject *) self));
768    PyObject_CallMethod(
769        attrs, "extend", "[sssss]",
770        "pgcnx", "arraysize", "resulttype", "ntuples", "nfields");
771
772    return attrs;
773}
774
775/* Source object methods */
776static PyMethodDef source_methods[] = {
777    {"__dir__", (PyCFunction) source_dir, METH_NOARGS, NULL},
778
779    {"close", (PyCFunction) source_close,
780        METH_NOARGS, source_close__doc__},
781    {"execute", (PyCFunction) source_execute,
782        METH_O, source_execute__doc__},
783    {"oidstatus", (PyCFunction) source_oidstatus,
784        METH_NOARGS, source_oidstatus__doc__},
785    {"fetch", (PyCFunction) source_fetch,
786        METH_VARARGS, source_fetch__doc__},
787    {"movefirst", (PyCFunction) source_movefirst,
788        METH_NOARGS, source_movefirst__doc__},
789    {"movelast", (PyCFunction) source_movelast,
790        METH_NOARGS, source_movelast__doc__},
791    {"movenext", (PyCFunction) source_movenext,
792        METH_NOARGS, source_movenext__doc__},
793    {"moveprev", (PyCFunction) source_moveprev,
794        METH_NOARGS, source_moveprev__doc__},
795    {"putdata", (PyCFunction) source_putdata,
796        METH_O, source_putdata__doc__},
797    {"getdata", (PyCFunction) source_getdata,
798        METH_VARARGS, source_getdata__doc__},
799    {"field", (PyCFunction) source_field,
800        METH_O, source_field__doc__},
801    {"fieldinfo", (PyCFunction) source_fieldinfo,
802        METH_O, source_fieldinfo__doc__},
803    {"listinfo", (PyCFunction) source_listInfo,
804        METH_NOARGS, source_listinfo__doc__},
805    {NULL, NULL}
806};
807
808static char source__doc__[] = "PyGreSQL source object";
809
810/* Source type definition */
811static PyTypeObject sourceType = {
812    PyVarObject_HEAD_INIT(NULL, 0)
813    "pgdb.Source",                  /* tp_name */
814    sizeof(sourceObject),           /* tp_basicsize */
815    0,                              /* tp_itemsize */
816    /* methods */
817    (destructor) source_dealloc,    /* tp_dealloc */
818    0,                              /* tp_print */
819    0,                              /* tp_getattr */
820    (setattrfunc) source_setattr,   /* tp_setattr */
821    0,                              /* tp_compare */
822    0,                              /* tp_repr */
823    0,                              /* tp_as_number */
824    0,                              /* tp_as_sequence */
825    0,                              /* tp_as_mapping */
826    0,                              /* tp_hash */
827    0,                              /* tp_call */
828    (reprfunc) source_str,          /* tp_str */
829    (getattrofunc) source_getattr,  /* tp_getattro */
830    0,                              /* tp_setattro */
831    0,                              /* tp_as_buffer */
832    Py_TPFLAGS_DEFAULT,             /* tp_flags */
833    source__doc__,                  /* tp_doc */
834    0,                              /* tp_traverse */
835    0,                              /* tp_clear */
836    0,                              /* tp_richcompare */
837    0,                              /* tp_weaklistoffset */
838    0,                              /* tp_iter */
839    0,                              /* tp_iternext */
840    source_methods,                 /* tp_methods */
841};
Note: See TracBrowser for help on using the repository browser.