source: trunk/pglarge.c

Last change on this file was 1002, checked in by cito, 5 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: 12.7 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, bufsize;
227
228    /* gets arguments */
229    if (!PyArg_ParseTuple(args, "s#", &buffer, &bufsize)) {
230        PyErr_SetString(PyExc_TypeError,
231                        "Method write() expects a sized string as argument");
232        return NULL;
233    }
234
235    /* checks validity */
236    if (!_check_lo_obj(self, CHECK_OPEN)) {
237        return NULL;
238    }
239
240    /* sends query */
241    if ((size = lo_write(self->pgcnx->cnx, self->lo_fd, buffer,
242                         bufsize)) != bufsize)
243    {
244        PyErr_SetString(PyExc_IOError, "Buffer truncated during write");
245        return NULL;
246    }
247
248    /* no error : returns Py_None */
249    Py_INCREF(Py_None);
250    return Py_None;
251}
252
253/* Go to position in large object. */
254static char large_seek__doc__[] =
255"seek(offset, whence) -- move to specified position\n\n"
256"Object must be opened before calling this method. The whence option\n"
257"can be SEEK_SET, SEEK_CUR or SEEK_END (module level constants).\n";
258
259static PyObject *
260large_seek(largeObject *self, PyObject *args)
261{
262    /* offset and whence are initialized to keep compiler happy */
263    int ret, offset = 0, whence = 0;
264
265    /* gets arguments */
266    if (!PyArg_ParseTuple(args, "ii", &offset, &whence)) {
267        PyErr_SetString(PyExc_TypeError,
268                        "Method lseek() expects two integer arguments");
269        return NULL;
270    }
271
272    /* checks validity */
273    if (!_check_lo_obj(self, CHECK_OPEN)) {
274        return NULL;
275    }
276
277    /* sends query */
278    if ((ret = lo_lseek(
279        self->pgcnx->cnx, self->lo_fd, offset, whence)) == -1)
280    {
281        PyErr_SetString(PyExc_IOError, "Error while moving cursor");
282        return NULL;
283    }
284
285    /* returns position */
286    return PyInt_FromLong(ret);
287}
288
289/* Get large object size. */
290static char large_size__doc__[] =
291"size() -- return large object size\n\n"
292"The object must be opened before calling this method.\n";
293
294static PyObject *
295large_size(largeObject *self, PyObject *noargs)
296{
297    int start, end;
298
299    /* checks validity */
300    if (!_check_lo_obj(self, CHECK_OPEN)) {
301        return NULL;
302    }
303
304    /* gets current position */
305    if ((start = lo_tell(self->pgcnx->cnx, self->lo_fd)) == -1) {
306        PyErr_SetString(PyExc_IOError, "Error while getting current position");
307        return NULL;
308    }
309
310    /* gets end position */
311    if ((end = lo_lseek(self->pgcnx->cnx, self->lo_fd, 0, SEEK_END)) == -1) {
312        PyErr_SetString(PyExc_IOError, "Error while getting end position");
313        return NULL;
314    }
315
316    /* move back to start position */
317    if ((start = lo_lseek(
318        self->pgcnx->cnx, self->lo_fd, start, SEEK_SET)) == -1)
319    {
320        PyErr_SetString(PyExc_IOError,
321                        "Error while moving back to first position");
322        return NULL;
323    }
324
325    /* returns size */
326    return PyInt_FromLong(end);
327}
328
329/* Get large object cursor position. */
330static char large_tell__doc__[] =
331"tell() -- give current position in large object\n\n"
332"The object must be opened before calling this method.\n";
333
334static PyObject *
335large_tell(largeObject *self, PyObject *noargs)
336{
337    int start;
338
339    /* checks validity */
340    if (!_check_lo_obj(self, CHECK_OPEN)) {
341        return NULL;
342    }
343
344    /* gets current position */
345    if ((start = lo_tell(self->pgcnx->cnx, self->lo_fd)) == -1) {
346        PyErr_SetString(PyExc_IOError, "Error while getting position");
347        return NULL;
348    }
349
350    /* returns size */
351    return PyInt_FromLong(start);
352}
353
354/* Export large object as unix file. */
355static char large_export__doc__[] =
356"export(filename) -- export large object data to specified file\n\n"
357"The object must be closed when calling this method.\n";
358
359static PyObject *
360large_export(largeObject *self, PyObject *args)
361{
362    char *name;
363
364    /* checks validity */
365    if (!_check_lo_obj(self, CHECK_CLOSE)) {
366        return NULL;
367    }
368
369    /* gets arguments */
370    if (!PyArg_ParseTuple(args, "s", &name)) {
371        PyErr_SetString(PyExc_TypeError,
372                        "The method export() takes a filename as argument");
373        return NULL;
374    }
375
376    /* runs command */
377    if (lo_export(self->pgcnx->cnx, self->lo_oid, name) != 1) {
378        PyErr_SetString(PyExc_IOError, "Error while exporting large object");
379        return NULL;
380    }
381
382    Py_INCREF(Py_None);
383    return Py_None;
384}
385
386/* Delete a large object. */
387static char large_unlink__doc__[] =
388"unlink() -- destroy large object\n\n"
389"The object must be closed when calling this method.\n";
390
391static PyObject *
392large_unlink(largeObject *self, PyObject *noargs)
393{
394    /* checks validity */
395    if (!_check_lo_obj(self, CHECK_CLOSE)) {
396        return NULL;
397    }
398
399    /* deletes the object, invalidate it on success */
400    if (lo_unlink(self->pgcnx->cnx, self->lo_oid) != 1) {
401        PyErr_SetString(PyExc_IOError, "Error while unlinking large object");
402        return NULL;
403    }
404    self->lo_oid = 0;
405
406    Py_INCREF(Py_None);
407    return Py_None;
408}
409
410/* Large object methods */
411static struct PyMethodDef large_methods[] = {
412    {"__dir__", (PyCFunction) large_dir,  METH_NOARGS, NULL},
413    {"open", (PyCFunction) large_open, METH_VARARGS, large_open__doc__},
414    {"close", (PyCFunction) large_close, METH_NOARGS, large_close__doc__},
415    {"read", (PyCFunction) large_read, METH_VARARGS, large_read__doc__},
416    {"write", (PyCFunction) large_write, METH_VARARGS, large_write__doc__},
417    {"seek", (PyCFunction) large_seek, METH_VARARGS, large_seek__doc__},
418    {"size", (PyCFunction) large_size, METH_NOARGS, large_size__doc__},
419    {"tell", (PyCFunction) large_tell, METH_NOARGS, large_tell__doc__},
420    {"export",(PyCFunction) large_export, METH_VARARGS, large_export__doc__},
421    {"unlink",(PyCFunction) large_unlink, METH_NOARGS, large_unlink__doc__},
422    {NULL, NULL}
423};
424
425static char large__doc__[] = "PostgreSQL large object";
426
427/* Large object type definition */
428static PyTypeObject largeType = {
429    PyVarObject_HEAD_INIT(NULL, 0)
430    "pg.LargeObject",              /* tp_name */
431    sizeof(largeObject),           /* tp_basicsize */
432    0,                             /* tp_itemsize */
433
434    /* methods */
435    (destructor) large_dealloc,    /* tp_dealloc */
436    0,                             /* tp_print */
437    0,                             /* tp_getattr */
438    0,                             /* tp_setattr */
439    0,                             /* tp_compare */
440    0,                             /* tp_repr */
441    0,                             /* tp_as_number */
442    0,                             /* tp_as_sequence */
443    0,                             /* tp_as_mapping */
444    0,                             /* tp_hash */
445    0,                             /* tp_call */
446    (reprfunc) large_str,          /* tp_str */
447    (getattrofunc) large_getattr,  /* tp_getattro */
448    0,                             /* tp_setattro */
449    0,                             /* tp_as_buffer */
450    Py_TPFLAGS_DEFAULT,            /* tp_flags */
451    large__doc__,                  /* tp_doc */
452    0,                             /* tp_traverse */
453    0,                             /* tp_clear */
454    0,                             /* tp_richcompare */
455    0,                             /* tp_weaklistoffset */
456    0,                             /* tp_iter */
457    0,                             /* tp_iternext */
458    large_methods,                 /* tp_methods */
459};
Note: See TracBrowser for help on using the repository browser.