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