summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--psycopg/psycopgmodule.c4
-rw-r--r--psycopg/typecast.c1
-rw-r--r--psycopg/typecast_basic.c87
-rw-r--r--psycopg/typecast_binary.c191
-rw-r--r--psycopg/typecast_binary.h47
6 files changed, 247 insertions, 87 deletions
diff --git a/ChangeLog b/ChangeLog
index ed78c2f..c883516 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
2005-05-09 Federico Di Gregorio <fog@debian.org>
+ * psycopg/typecast_binary.*: applied slightly modified
+ chunk/buffer object patch to allow round-trip of buffer objects
+ (BYTEA columns.)
+
* psycopg/cursor_type.c (psyco_curs_executemany): applied slightly
fixed patch from wrobell to allow iterators in .executemany().
diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c
index 4e5f9ce..1c3966a 100644
--- a/psycopg/psycopgmodule.c
+++ b/psycopg/psycopgmodule.c
@@ -36,6 +36,7 @@
#include "psycopg/adapter_pboolean.h"
#include "psycopg/adapter_asis.h"
#include "psycopg/adapter_list.h"
+#include "psycopg/typecast_binary.h"
#ifdef HAVE_MXDATETIME
#include <mxDateTime.h>
@@ -406,6 +407,7 @@ init_psycopg(void)
isqlquoteType.ob_type = &PyType_Type;
asisType.ob_type = &PyType_Type;
listType.ob_type = &PyType_Type;
+ chunkType.ob_type = &PyType_Type;
if (PyType_Ready(&connectionType) == -1) return;
if (PyType_Ready(&cursorType) == -1) return;
@@ -415,6 +417,7 @@ init_psycopg(void)
if (PyType_Ready(&isqlquoteType) == -1) return;
if (PyType_Ready(&asisType) == -1) return;
if (PyType_Ready(&listType) == -1) return;
+ if (PyType_Ready(&chunkType) == -1) return;
#ifdef HAVE_PYBOOL
pbooleanType.ob_type = &PyType_Type;
@@ -501,6 +504,7 @@ init_psycopg(void)
asisType.tp_alloc = PyType_GenericAlloc;
qstringType.tp_alloc = PyType_GenericAlloc;
listType.tp_alloc = PyType_GenericAlloc;
+ chunkType.tp_alloc = PyType_GenericAlloc;
Dprintf("initpsycopg: module initialization complete");
}
diff --git a/psycopg/typecast.c b/psycopg/typecast.c
index cafebb3..a07df2c 100644
--- a/psycopg/typecast.c
+++ b/psycopg/typecast.c
@@ -41,6 +41,7 @@ skip_until_space(char *s)
/** include casting objects **/
#include "psycopg/typecast_basic.c"
+#include "psycopg/typecast_binary.c"
#ifdef HAVE_MXDATETIME
#include "psycopg/typecast_mxdatetime.c"
diff --git a/psycopg/typecast_basic.c b/psycopg/typecast_basic.c
index 38101fa..c5d55e9 100644
--- a/psycopg/typecast_basic.c
+++ b/psycopg/typecast_basic.c
@@ -19,9 +19,6 @@
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#include <libpq-fe.h>
-#include <stdlib.h>
-
/** INTEGER - cast normal integers (4 bytes) to python int **/
static PyObject *
@@ -99,90 +96,6 @@ typecast_UNICODE_cast(unsigned char *s, int len, PyObject *curs)
}
}
-/** BINARY - cast a binary string into a python string **/
-
-/* the function typecast_BINARY_cast_unescape is used when libpq does not
- provide PQunescapeBytea: it convert all the \xxx octal sequences to the
- proper byte value */
-
-#ifdef PSYCOPG_OWN_QUOTING
-static unsigned char *
-typecast_BINARY_cast_unescape(unsigned char *str, size_t *to_length)
-{
- char *dstptr, *dststr;
- int len, i;
-
- len = strlen(str);
- dststr = (char*)calloc(len, sizeof(char));
- dstptr = dststr;
-
- if (dststr == NULL) return NULL;
-
- Py_BEGIN_ALLOW_THREADS;
-
- for (i = 0; i < len; i++) {
- if (str[i] == '\\') {
- if ( ++i < len) {
- if (str[i] == '\\') {
- *dstptr = '\\';
- }
- else {
- *dstptr = 0;
- *dstptr |= (str[i++] & 7) << 6;
- *dstptr |= (str[i++] & 7) << 3;
- *dstptr |= (str[i] & 7);
- }
- }
- }
- else {
- *dstptr = str[i];
- }
- dstptr++;
- }
-
- Py_END_ALLOW_THREADS;
-
- *to_length = (size_t)(dstptr-dststr);
-
- return dststr;
-}
-
-#define PQunescapeBytea typecast_BINARY_cast_unescape
-#endif
-
-static PyObject *
-typecast_BINARY_cast(unsigned char *s, int l, PyObject *curs)
-{
- PyObject *res;
- unsigned char *str, *buffer = NULL;
- size_t len;
-
- if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
-
- /* PQunescapeBytea absolutely wants a 0-terminated string and we don't
- want to copy the whole buffer, right? Wrong, but there isn't any other
- way :/ */
- if (s[l] != '\0') {
- if ((buffer = PyMem_Malloc(l+1)) == NULL)
- PyErr_NoMemory();
- strncpy(buffer, s, l); buffer[l] = '\0';
- s = buffer;
- }
- str = PQunescapeBytea(s, &len);
- Dprintf("typecast_BINARY_cast: unescaped %d bytes", len);
- if (buffer) PyMem_Free(buffer);
-
- /* TODO: using a PyBuffer would make this a zero-copy operation but we'll
- need to define our own buffer-derived object to keep a reference to the
- memory area: does it buy it?
-
- res = PyBuffer_FromMemory((void*)str, len); */
- res = PyString_FromStringAndSize(str, len);
- free(str);
-
- return res;
-}
-
/** BOOLEAN - cast boolean value into right python object **/
static PyObject *
diff --git a/psycopg/typecast_binary.c b/psycopg/typecast_binary.c
new file mode 100644
index 0000000..bc2ad60
--- /dev/null
+++ b/psycopg/typecast_binary.c
@@ -0,0 +1,191 @@
+/* typecast_binary.c - binary typecasting functions to python types
+ *
+ * Copyright (C) 2001-2003 Federico Di Gregorio <fog@debian.org>
+ *
+ * This file is part of the psycopg module.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "typecast_binary.h"
+
+#include <libpq-fe.h>
+#include <stdlib.h>
+
+
+/* Python object holding a memory chunk. The memory is deallocated when
+ the object is destroyed. This type is used to let users directly access
+ memory chunks holding unescaped binary data through the buffer interface.
+ */
+
+static void
+chunk_dealloc(chunkObject *self)
+{
+ Dprintf("chunk_dealloc: deallocating memory at %p, size %d",
+ self->base, self->len);
+ free(self->base);
+ self->ob_type->tp_free((PyObject *) self);
+}
+
+static PyObject *
+chunk_repr(chunkObject *self)
+{
+ return PyString_FromFormat("<memory chunk at %p size %d>",
+ self->base, self->len);
+}
+
+static int
+chunk_getreadbuffer(chunkObject *self, int segment, void **ptr)
+{
+ if (segment != 0)
+ {
+ PyErr_SetString(PyExc_SystemError,
+ "acessing non-existant buffer segment");
+ return -1;
+ }
+ *ptr = self->base;
+ return self->len;
+}
+
+static int
+chunk_getsegcount(chunkObject *self, int *lenp)
+{
+ if (lenp != NULL)
+ *lenp = self->len;
+ return 1;
+}
+
+static PyBufferProcs chunk_as_buffer =
+{
+ (getreadbufferproc) chunk_getreadbuffer,
+ (getwritebufferproc) NULL,
+ (getsegcountproc) chunk_getsegcount,
+ (getcharbufferproc) NULL
+};
+
+#define chunk_doc "memory chunk"
+
+PyTypeObject chunkType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "psycopg._psycopg.chunk", /* tp_name */
+ sizeof(chunkObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor) chunk_dealloc, /* tp_dealloc*/
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc) chunk_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ &chunk_as_buffer, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
+ chunk_doc /* tp_doc */
+};
+
+/* the function typecast_BINARY_cast_unescape is used when libpq does not
+ provide PQunescapeBytea: it convert all the \xxx octal sequences to the
+ proper byte value */
+
+#ifdef PSYCOPG_OWN_QUOTING
+static unsigned char *
+typecast_BINARY_cast_unescape(unsigned char *str, size_t *to_length)
+{
+ char *dstptr, *dststr;
+ int len, i;
+
+ len = strlen(str);
+ dststr = (char*)calloc(len, sizeof(char));
+ dstptr = dststr;
+
+ if (dststr == NULL) return NULL;
+
+ Py_BEGIN_ALLOW_THREADS;
+
+ for (i = 0; i < len; i++) {
+ if (str[i] == '\\') {
+ if ( ++i < len) {
+ if (str[i] == '\\') {
+ *dstptr = '\\';
+ }
+ else {
+ *dstptr = 0;
+ *dstptr |= (str[i++] & 7) << 6;
+ *dstptr |= (str[i++] & 7) << 3;
+ *dstptr |= (str[i] & 7);
+ }
+ }
+ }
+ else {
+ *dstptr = str[i];
+ }
+ dstptr++;
+ }
+
+ Py_END_ALLOW_THREADS;
+
+ *to_length = (size_t)(dstptr-dststr);
+
+ return dststr;
+}
+
+#define PQunescapeBytea typecast_BINARY_cast_unescape
+#endif
+
+static PyObject *
+typecast_BINARY_cast(unsigned char *s, int l, PyObject *curs)
+{
+ chunkObject *chunk;
+ PyObject *res;
+ unsigned char *str, *buffer = NULL;
+ size_t len;
+
+ if (s == NULL) {Py_INCREF(Py_None); return Py_None;}
+
+ /* PQunescapeBytea absolutely wants a 0-terminated string and we don't
+ want to copy the whole buffer, right? Wrong, but there isn't any other
+ way <g> */
+ if (s[l] != '\0') {
+ if ((buffer = PyMem_Malloc(l+1)) == NULL)
+ PyErr_NoMemory();
+ strncpy(buffer, s, l);
+ buffer[l] = '\0';
+ s = buffer;
+ }
+ str = PQunescapeBytea(s, &len);
+ Dprintf("typecast_BINARY_cast: unescaped %d bytes", len);
+ if (buffer) PyMem_Free(buffer);
+
+ chunk = (chunkObject *) PyObject_New(chunkObject, &chunkType);
+ if (chunk == NULL) return NULL;
+
+ chunk->base = str;
+ chunk->len = len;
+ if ((res = PyBuffer_FromObject((PyObject *)chunk, 0, len)) == NULL)
+ return NULL;
+
+ /* PyBuffer_FromObject() created a new reference. Release our reference so
+ that the memory can be freed once the buffer is garbage collected. */
+ Py_DECREF(chunk);
+
+ return res;
+}
diff --git a/psycopg/typecast_binary.h b/psycopg/typecast_binary.h
new file mode 100644
index 0000000..cf985bb
--- /dev/null
+++ b/psycopg/typecast_binary.h
@@ -0,0 +1,47 @@
+/* typecast_binary.h - definitions for binary typecaster
+ *
+ * Copyright (C) 2003 Federico Di Gregorio <fog@debian.org>
+ *
+ * This file is part of psycopg.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PSYCOPG_TYPECAST_BINARY_H
+#define PSYCOPG_TYPECAST_BINARY_H 1
+
+#include <Python.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** chunk type **/
+
+extern PyTypeObject chunkType;
+
+typedef struct {
+ PyObject_HEAD
+
+ void *base; /* Pointer to the memory chunk. */
+ int len; /* Size in bytes of the memory chunk. */
+
+} chunkObject;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !defined(PSYCOPG_TYPECAST_BINARY_H) */