From 65a4b86fa2facb766b33f52813a341661dedd643 Mon Sep 17 00:00:00 2001 From: Federico Di Gregorio Date: Mon, 9 May 2005 08:11:43 +0000 Subject: [PATCH] chunk/buffer patch applied. --- ChangeLog | 4 + psycopg/psycopgmodule.c | 4 + psycopg/typecast.c | 1 + psycopg/typecast_basic.c | 87 ----------------- psycopg/typecast_binary.c | 191 ++++++++++++++++++++++++++++++++++++++ psycopg/typecast_binary.h | 47 ++++++++++ 6 files changed, 247 insertions(+), 87 deletions(-) create mode 100644 psycopg/typecast_binary.c create mode 100644 psycopg/typecast_binary.h diff --git a/ChangeLog b/ChangeLog index ed78c2fc..c8835163 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2005-05-09 Federico Di Gregorio + * 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 4e5f9ced..1c3966a4 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 @@ -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 cafebb37..a07df2cb 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 38101fa3..c5d55e90 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 -#include - /** 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 00000000..bc2ad606 --- /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 + * + * 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 +#include + + +/* 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("", + 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 */ + 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 00000000..cf985bb0 --- /dev/null +++ b/psycopg/typecast_binary.h @@ -0,0 +1,47 @@ +/* typecast_binary.h - definitions for binary typecaster + * + * Copyright (C) 2003 Federico Di Gregorio + * + * 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 + +#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) */