psycopg2/psycopg/typecast_binary.c
Daniele Varrazzo cb6b52945b The library can be compiled with Python 3.
Just compiled! No test run yet and many points to review, marked in the
code.

The patch is largely Martin von Löwis work, simplified after refactoring
in the previous commits and adapted to the new code (as the patch was
originally for Psycopg 2.0.9)
2010-12-21 04:24:36 +00:00

211 lines
6.3 KiB
C

/* typecast_binary.c - binary typecasting functions to python types
*
* Copyright (C) 2001-2010 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 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 Lesser General Public
* License for more details.
*/
#include "typecast_binary.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 "
FORMAT_CODE_PY_SSIZE_T,
self->base, self->len
);
PQfreemem(self->base);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject *
chunk_repr(chunkObject *self)
{
return PyString_FromFormat(
"<memory chunk at %p size " FORMAT_CODE_PY_SSIZE_T ">",
self->base, self->len
);
}
#if PY_MAJOR_VERSION < 3
/* XXX support 3.0 buffer protocol */
static Py_ssize_t
chunk_getreadbuffer(chunkObject *self, Py_ssize_t segment, void **ptr)
{
if (segment != 0)
{
PyErr_SetString(PyExc_SystemError,
"acessing non-existant buffer segment");
return -1;
}
*ptr = self->base;
return self->len;
}
static Py_ssize_t
chunk_getsegcount(chunkObject *self, Py_ssize_t *lenp)
{
if (lenp != NULL)
*lenp = self->len;
return 1;
}
static PyBufferProcs chunk_as_buffer =
{
(readbufferproc) chunk_getreadbuffer,
(writebufferproc) NULL,
(segcountproc) chunk_getsegcount,
(charbufferproc) NULL
};
#else
/* 3.0 buffer interface */
int chunk_getbuffer(PyObject *_self, Py_buffer *view, int flags)
{
chunkObject *self = (chunkObject*)_self;
return PyBuffer_FillInfo(view, _self, self->base, self->len, 1, flags);
}
static PyBufferProcs chunk_as_buffer =
{
chunk_getbuffer,
NULL,
};
#endif
#define chunk_doc "memory chunk"
PyTypeObject chunkType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._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 */
};
static PyObject *
typecast_BINARY_cast(const char *s, Py_ssize_t l, PyObject *curs)
{
chunkObject *chunk = NULL;
PyObject *res = NULL;
char *str = NULL, *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();
goto fail;
}
/* Py_ssize_t->size_t cast is safe, as long as the Py_ssize_t is
* >= 0: */
assert (l >= 0);
strncpy(buffer, s, (size_t) l);
buffer[l] = '\0';
s = buffer;
}
str = (char*)PQunescapeBytea((unsigned char*)s, &len);
Dprintf("typecast_BINARY_cast: unescaped " FORMAT_CODE_SIZE_T " bytes",
len);
/* The type of the second parameter to PQunescapeBytea is size_t *, so it's
* possible (especially with Python < 2.5) to get a return value too large
* to fit into a Python container. */
if (len > (size_t) PY_SSIZE_T_MAX) {
PyErr_SetString(PyExc_IndexError, "PG buffer too large to fit in Python"
" buffer.");
goto fail;
}
chunk = (chunkObject *) PyObject_New(chunkObject, &chunkType);
if (chunk == NULL) goto fail;
/* **Transfer** ownership of str's memory to the chunkObject: */
chunk->base = str;
str = NULL;
/* size_t->Py_ssize_t cast was validated above: */
chunk->len = (Py_ssize_t) len;
#if PY_MAJOR_VERSION < 3
if ((res = PyBuffer_FromObject((PyObject *)chunk, 0, chunk->len)) == NULL)
goto fail;
#else
if ((res = PyMemoryView_FromObject((PyObject*)chunk)) == NULL)
goto fail;
#endif
/* PyBuffer_FromObject() created a new reference. We'll release our
* reference held in 'chunk' in the 'cleanup' clause. */
goto cleanup;
fail:
assert (PyErr_Occurred());
if (res != NULL) {
Py_DECREF(res);
res = NULL;
}
/* Fall through to cleanup: */
cleanup:
if (chunk != NULL) {
Py_DECREF((PyObject *) chunk);
}
if (str != NULL) {
/* str's mem was allocated by PQunescapeBytea; must use PQfreemem: */
PQfreemem(str);
}
if (buffer != NULL) {
/* We allocated buffer with PyMem_Malloc; must use PyMem_Free: */
PyMem_Free(buffer);
}
return res;
}