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