source: trunk/pglarge.c

Last change on this file was 1021, checked in by cito, 3 weeks ago

Fix deprecation warning issue with Python 3.8

File size: 12.8 KB
Line 
1/*
2 * $Id: pglarge.c 985 2019-04-22 22:07:43Z cito $
3 *
4 * PyGreSQL - a Python interface for the PostgreSQL database.
5 *
6 * Large object support - 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 large object. */
15static void
16large_dealloc(largeObject *self)
17{
18    if (self->lo_fd >= 0 && self->pgcnx->valid)
19        lo_close(self->pgcnx->cnx, self->lo_fd);
20
21    Py_XDECREF(self->pgcnx);
22    PyObject_Del(self);
23}
24
25/* Return large object as string in human readable form. */
26static PyObject *
27large_str(largeObject *self)
28{
29    char str[80];
30    sprintf(str, self->lo_fd >= 0 ?
31            "Opened large object, oid %ld" :
32            "Closed large object, oid %ld", (long) self->lo_oid);
33    return PyStr_FromString(str);
34}
35
36/* Check validity of large object. */
37static int
38_check_lo_obj(largeObject *self, int level)
39{
40    if (!_check_cnx_obj(self->pgcnx))
41        return 0;
42
43    if (!self->lo_oid) {
44        set_error_msg(IntegrityError, "Object is not valid (null oid)");
45        return 0;
46    }
47
48    if (level & CHECK_OPEN) {
49        if (self->lo_fd < 0) {
50            PyErr_SetString(PyExc_IOError, "Object is not opened");
51            return 0;
52        }
53    }
54
55    if (level & CHECK_CLOSE) {
56        if (self->lo_fd >= 0) {
57            PyErr_SetString(PyExc_IOError, "Object is already opened");
58            return 0;
59        }
60    }
61
62    return 1;
63}
64
65/* Get large object attributes. */
66static PyObject *
67large_getattr(largeObject *self, PyObject *nameobj)
68{
69    const char *name = PyStr_AsString(nameobj);
70
71    /* list postgreSQL large object fields */
72
73    /* associated pg connection object */
74    if (!strcmp(name, "pgcnx")) {
75        if (_check_lo_obj(self, 0)) {
76            Py_INCREF(self->pgcnx);
77            return (PyObject *) (self->pgcnx);
78        }
79        PyErr_Clear();
80        Py_INCREF(Py_None);
81        return Py_None;
82    }
83
84    /* large object oid */
85    if (!strcmp(name, "oid")) {
86        if (_check_lo_obj(self, 0))
87            return PyInt_FromLong(self->lo_oid);
88        PyErr_Clear();
89        Py_INCREF(Py_None);
90        return Py_None;
91    }
92
93    /* error (status) message */
94    if (!strcmp(name, "error"))
95        return PyStr_FromString(PQerrorMessage(self->pgcnx->cnx));
96
97    /* seeks name in methods (fallback) */
98    return PyObject_GenericGetAttr((PyObject *) self, nameobj);
99}
100
101/* Get the list of large object attributes. */
102static PyObject *
103large_dir(largeObject *self, PyObject *noargs)
104{
105    PyObject *attrs;
106
107    attrs = PyObject_Dir(PyObject_Type((PyObject *) self));
108    PyObject_CallMethod(
109        attrs, "extend", "[sss]", "oid", "pgcnx", "error");
110
111    return attrs;
112}
113
114/* Open large object. */
115static char large_open__doc__[] =
116"open(mode) -- open access to large object with specified mode\n\n"
117"The mode must be one of INV_READ, INV_WRITE (module level constants).\n";
118
119static PyObject *
120large_open(largeObject *self, PyObject *args)
121{
122    int mode, fd;
123
124    /* gets arguments */
125    if (!PyArg_ParseTuple(args, "i", &mode)) {
126        PyErr_SetString(PyExc_TypeError,
127                        "The open() method takes an integer argument");
128        return NULL;
129    }
130
131    /* check validity */
132    if (!_check_lo_obj(self, CHECK_CLOSE)) {
133        return NULL;
134    }
135
136    /* opens large object */
137    if ((fd = lo_open(self->pgcnx->cnx, self->lo_oid, mode)) == -1) {
138        PyErr_SetString(PyExc_IOError, "Can't open large object");
139        return NULL;
140    }
141    self->lo_fd = fd;
142
143    /* no error : returns Py_None */
144    Py_INCREF(Py_None);
145    return Py_None;
146}
147
148/* Close large object. */
149static char large_close__doc__[] =
150"close() -- close access to large object data";
151
152static PyObject *
153large_close(largeObject *self, PyObject *noargs)
154{
155    /* checks validity */
156    if (!_check_lo_obj(self, CHECK_OPEN)) {
157        return NULL;
158    }
159
160    /* closes large object */
161    if (lo_close(self->pgcnx->cnx, self->lo_fd)) {
162        PyErr_SetString(PyExc_IOError, "Error while closing large object fd");
163        return NULL;
164    }
165    self->lo_fd = -1;
166
167    /* no error : returns Py_None */
168    Py_INCREF(Py_None);
169    return Py_None;
170}
171
172/* Read from large object. */
173static char large_read__doc__[] =
174"read(size) -- read from large object to sized string\n\n"
175"Object must be opened in read mode before calling this method.\n";
176
177static PyObject *
178large_read(largeObject *self, PyObject *args)
179{
180    int size;
181    PyObject *buffer;
182
183    /* gets arguments */
184    if (!PyArg_ParseTuple(args, "i", &size)) {
185        PyErr_SetString(PyExc_TypeError,
186                        "Method read() takes an integer argument");
187        return NULL;
188    }
189
190    if (size <= 0) {
191        PyErr_SetString(PyExc_ValueError,
192                        "Method read() takes a positive integer as argument");
193        return NULL;
194    }
195
196    /* checks validity */
197    if (!_check_lo_obj(self, CHECK_OPEN)) {
198        return NULL;
199    }
200
201    /* allocate buffer and runs read */
202    buffer = PyBytes_FromStringAndSize((char *) NULL, size);
203
204    if ((size = lo_read(self->pgcnx->cnx, self->lo_fd,
205        PyBytes_AS_STRING((PyBytesObject *) (buffer)), size)) == -1)
206    {
207        PyErr_SetString(PyExc_IOError, "Error while reading");
208        Py_XDECREF(buffer);
209        return NULL;
210    }
211
212    /* resize buffer and returns it */
213    _PyBytes_Resize(&buffer, size);
214    return buffer;
215}
216
217/* Write to large object. */
218static char large_write__doc__[] =
219"write(string) -- write sized string to large object\n\n"
220"Object must be opened in read mode before calling this method.\n";
221
222static PyObject *
223large_write(largeObject *self, PyObject *args)
224{
225    char *buffer;
226    int size;
227    Py_ssize_t bufsize;
228
229    /* gets arguments */
230    if (!PyArg_ParseTuple(args, "s#", &buffer, &bufsize)) {
231        PyErr_SetString(PyExc_TypeError,
232                        "Method write() expects a sized string as argument");
233        return NULL;
234    }
235
236    /* checks validity */
237    if (!_check_lo_obj(self, CHECK_OPEN)) {
238        return NULL;
239    }
240
241    /* sends query */
242    if ((size = lo_write(self->pgcnx->cnx, self->lo_fd, buffer,
243                         (int)bufsize)) != bufsize)
244    {
245        PyErr_SetString(PyExc_IOError, "Buffer truncated during write");
246        return NULL;
247    }
248
249    /* no error : returns Py_None */
250    Py_INCREF(Py_None);
251    return Py_None;
252}
253
254/* Go to position in large object. */
255static char large_seek__doc__[] =
256"seek(offset, whence) -- move to specified position\n\n"
257"Object must be opened before calling this method. The whence option\n"
258"can be SEEK_SET, SEEK_CUR or SEEK_END (module level constants).\n";
259
260static PyObject *
261large_seek(largeObject *self, PyObject *args)
262{
263    /* offset and whence are initialized to keep compiler happy */
264    int ret, offset = 0, whence = 0;
265
266    /* gets arguments */
267    if (!PyArg_ParseTuple(args, "ii", &offset, &whence)) {
268        PyErr_SetString(PyExc_TypeError,
269                        "Method lseek() expects two integer arguments");
270        return NULL;
271    }
272
273    /* checks validity */
274    if (!_check_lo_obj(self, CHECK_OPEN)) {
275        return NULL;
276    }
277
278    /* sends query */
279    if ((ret = lo_lseek(
280        self->pgcnx->cnx, self->lo_fd, offset, whence)) == -1)
281    {
282        PyErr_SetString(PyExc_IOError, "Error while moving cursor");
283        return NULL;
284    }
285
286    /* returns position */
287    return PyInt_FromLong(ret);
288}
289
290/* Get large object size. */
291static char large_size__doc__[] =
292"size() -- return large object size\n\n"
293"The object must be opened before calling this method.\n";
294
295static PyObject *
296large_size(largeObject *self, PyObject *noargs)
297{
298    int start, end;
299
300    /* checks validity */
301    if (!_check_lo_obj(self, CHECK_OPEN)) {
302        return NULL;
303    }
304
305    /* gets current position */
306    if ((start = lo_tell(self->pgcnx->cnx, self->lo_fd)) == -1) {
307        PyErr_SetString(PyExc_IOError, "Error while getting current position");
308        return NULL;
309    }
310
311    /* gets end position */
312    if ((end = lo_lseek(self->pgcnx->cnx, self->lo_fd, 0, SEEK_END)) == -1) {
313        PyErr_SetString(PyExc_IOError, "Error while getting end position");
314        return NULL;
315    }
316
317    /* move back to start position */
318    if ((start = lo_lseek(
319        self->pgcnx->cnx, self->lo_fd, start, SEEK_SET)) == -1)
320    {
321        PyErr_SetString(PyExc_IOError,
322                        "Error while moving back to first position");
323        return NULL;
324    }
325
326    /* returns size */
327    return PyInt_FromLong(end);
328}
329
330/* Get large object cursor position. */
331static char large_tell__doc__[] =
332"tell() -- give current position in large object\n\n"
333"The object must be opened before calling this method.\n";
334
335static PyObject *
336large_tell(largeObject *self, PyObject *noargs)
337{
338    int start;
339
340    /* checks validity */
341    if (!_check_lo_obj(self, CHECK_OPEN)) {
342        return NULL;
343    }
344
345    /* gets current position */
346    if ((start = lo_tell(self->pgcnx->cnx, self->lo_fd)) == -1) {
347        PyErr_SetString(PyExc_IOError, "Error while getting position");
348        return NULL;
349    }
350
351    /* returns size */
352    return PyInt_FromLong(start);
353}
354
355/* Export large object as unix file. */
356static char large_export__doc__[] =
357"export(filename) -- export large object data to specified file\n\n"
358"The object must be closed when calling this method.\n";
359
360static PyObject *
361large_export(largeObject *self, PyObject *args)
362{
363    char *name;
364
365    /* checks validity */
366    if (!_check_lo_obj(self, CHECK_CLOSE)) {
367        return NULL;
368    }
369
370    /* gets arguments */
371    if (!PyArg_ParseTuple(args, "s", &name)) {
372        PyErr_SetString(PyExc_TypeError,
373                        "The method export() takes a filename as argument");
374        return NULL;
375    }
376
377    /* runs command */
378    if (lo_export(self->pgcnx->cnx, self->lo_oid, name) != 1) {
379        PyErr_SetString(PyExc_IOError, "Error while exporting large object");
380        return NULL;
381    }
382
383    Py_INCREF(Py_None);
384    return Py_None;
385}
386
387/* Delete a large object. */
388static char large_unlink__doc__[] =
389"unlink() -- destroy large object\n\n"
390"The object must be closed when calling this method.\n";
391
392static PyObject *
393large_unlink(largeObject *self, PyObject *noargs)
394{
395    /* checks validity */
396    if (!_check_lo_obj(self, CHECK_CLOSE)) {
397        return NULL;
398    }
399
400    /* deletes the object, invalidate it on success */
401    if (lo_unlink(self->pgcnx->cnx, self->lo_oid) != 1) {
402        PyErr_SetString(PyExc_IOError, "Error while unlinking large object");
403        return NULL;
404    }
405    self->lo_oid = 0;
406
407    Py_INCREF(Py_None);
408    return Py_None;
409}
410
411/* Large object methods */
412static struct PyMethodDef large_methods[] = {
413    {"__dir__", (PyCFunction) large_dir,  METH_NOARGS, NULL},
414    {"open", (PyCFunction) large_open, METH_VARARGS, large_open__doc__},
415    {"close", (PyCFunction) large_close, METH_NOARGS, large_close__doc__},
416    {"read", (PyCFunction) large_read, METH_VARARGS, large_read__doc__},
417    {"write", (PyCFunction) large_write, METH_VARARGS, large_write__doc__},
418    {"seek", (PyCFunction) large_seek, METH_VARARGS, large_seek__doc__},
419    {"size", (PyCFunction) large_size, METH_NOARGS, large_size__doc__},
420    {"tell", (PyCFunction) large_tell, METH_NOARGS, large_tell__doc__},
421    {"export",(PyCFunction) large_export, METH_VARARGS, large_export__doc__},
422    {"unlink",(PyCFunction) large_unlink, METH_NOARGS, large_unlink__doc__},
423    {NULL, NULL}
424};
425
426static char large__doc__[] = "PostgreSQL large object";
427
428/* Large object type definition */
429static PyTypeObject largeType = {
430    PyVarObject_HEAD_INIT(NULL, 0)
431    "pg.LargeObject",              /* tp_name */
432    sizeof(largeObject),           /* tp_basicsize */
433    0,                             /* tp_itemsize */
434
435    /* methods */
436    (destructor) large_dealloc,    /* tp_dealloc */
437    0,                             /* tp_print */
438    0,                             /* tp_getattr */
439    0,                             /* tp_setattr */
440    0,                             /* tp_compare */
441    0,                             /* tp_repr */
442    0,                             /* tp_as_number */
443    0,                             /* tp_as_sequence */
444    0,                             /* tp_as_mapping */
445    0,                             /* tp_hash */
446    0,                             /* tp_call */
447    (reprfunc) large_str,          /* tp_str */
448    (getattrofunc) large_getattr,  /* tp_getattro */
449    0,                             /* tp_setattro */
450    0,                             /* tp_as_buffer */
451    Py_TPFLAGS_DEFAULT,            /* tp_flags */
452    large__doc__,                  /* tp_doc */
453    0,                             /* tp_traverse */
454    0,                             /* tp_clear */
455    0,                             /* tp_richcompare */
456    0,                             /* tp_weaklistoffset */
457    0,                             /* tp_iter */
458    0,                             /* tp_iternext */
459    large_methods,                 /* tp_methods */
460};
Note: See TracBrowser for help on using the repository browser.