From: Daniele Varrazzo Date: Mon, 21 Dec 2020 00:27:10 +0000 (+0100) Subject: Other psycopg3_c.pq_cython objects split in modules X-Git-Tag: 3.0.dev0~252^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c8c2ee22c17736641bae1ffe846878390b021015;p=thirdparty%2Fpsycopg.git Other psycopg3_c.pq_cython objects split in modules --- diff --git a/psycopg3_c/psycopg3_c/pq/conninfo.pyx b/psycopg3_c/psycopg3_c/pq/conninfo.pyx new file mode 100644 index 000000000..fbb5ff4d9 --- /dev/null +++ b/psycopg3_c/psycopg3_c/pq/conninfo.pyx @@ -0,0 +1,35 @@ +""" +psycopg3_c.pq_cython.Conninfo object implementation. +""" + +# Copyright (C) 2020 The Psycopg Team + + +class Conninfo: + @classmethod + def get_defaults(cls) -> List[ConninfoOption]: + cdef impl.PQconninfoOption *opts = impl.PQconndefaults() + if opts is NULL : + raise MemoryError("couldn't allocate connection defaults") + rv = _options_from_array(opts) + impl.PQconninfoFree(opts) + return rv + + @classmethod + def parse(cls, conninfo: bytes) -> List[ConninfoOption]: + cdef char *errmsg = NULL + cdef impl.PQconninfoOption *opts = impl.PQconninfoParse(conninfo, &errmsg) + if opts is NULL: + if errmsg is NULL: + raise MemoryError("couldn't allocate on conninfo parse") + else: + exc = PQerror(errmsg.decode("utf8", "replace")) + impl.PQfreemem(errmsg) + raise exc + + rv = _options_from_array(opts) + impl.PQconninfoFree(opts) + return rv + + def __repr__(self): + return f"<{type(self).__name__} ({self.keyword.decode('ascii')})>" diff --git a/psycopg3_c/psycopg3_c/pq/pgcancel.pyx b/psycopg3_c/psycopg3_c/pq/pgcancel.pyx new file mode 100644 index 000000000..8046aa119 --- /dev/null +++ b/psycopg3_c/psycopg3_c/pq/pgcancel.pyx @@ -0,0 +1,32 @@ +""" +psycopg3_c.pq_cython.PGcancel object implementation. +""" + +# Copyright (C) 2020 The Psycopg Team + + +cdef class PGcancel: + def __cinit__(self): + self.pgcancel_ptr = NULL + + @staticmethod + cdef PGcancel _from_ptr(impl.PGcancel *ptr): + cdef PGcancel rv = PGcancel.__new__(PGcancel) + rv.pgcancel_ptr = ptr + return rv + + def __dealloc__(self) -> None: + self.free() + + def free(self) -> None: + if self.pgcancel_ptr is not NULL: + impl.PQfreeCancel(self.pgcancel_ptr) + self.pgcancel_ptr = NULL + + def cancel(self) -> None: + cdef char buf[256] + cdef int res = impl.PQcancel(self.pgcancel_ptr, buf, sizeof(buf)) + if not res: + raise PQerror( + f"cancel failed: {buf.decode('utf8', 'ignore')}" + ) diff --git a/psycopg3_c/psycopg3_c/pq/pgresult.pyx b/psycopg3_c/psycopg3_c/pq/pgresult.pyx new file mode 100644 index 000000000..470df1efd --- /dev/null +++ b/psycopg3_c/psycopg3_c/pq/pgresult.pyx @@ -0,0 +1,148 @@ +""" +psycopg3_c.pq_cython.PGresult object implementation. +""" + +# Copyright (C) 2020 The Psycopg Team + + +cdef class PGresult: + def __cinit__(self): + self.pgresult_ptr = NULL + + @staticmethod + cdef PGresult _from_ptr(impl.PGresult *ptr): + cdef PGresult rv = PGresult.__new__(PGresult) + rv.pgresult_ptr = ptr + return rv + + def __dealloc__(self) -> None: + self.clear() + + def clear(self) -> None: + if self.pgresult_ptr is not NULL: + impl.PQclear(self.pgresult_ptr) + self.pgresult_ptr = NULL + + @property + def pgresult_ptr(self) -> Optional[int]: + if self.pgresult_ptr: + return self.pgresult_ptr + else: + return None + + @property + def status(self) -> ExecStatus: + cdef int rv = impl.PQresultStatus(self.pgresult_ptr) + return ExecStatus(rv) + + @property + def error_message(self) -> bytes: + return impl.PQresultErrorMessage(self.pgresult_ptr) + + def error_field(self, fieldcode: DiagnosticField) -> Optional[bytes]: + cdef char * rv = impl.PQresultErrorField(self.pgresult_ptr, fieldcode) + if rv is not NULL: + return rv + else: + return None + + @property + def ntuples(self) -> int: + return impl.PQntuples(self.pgresult_ptr) + + @property + def nfields(self) -> int: + return impl.PQnfields(self.pgresult_ptr) + + def fname(self, column_number: int) -> Optional[bytes]: + cdef char *rv = impl.PQfname(self.pgresult_ptr, column_number) + if rv is not NULL: + return rv + else: + return None + + def ftable(self, column_number: int) -> int: + return impl.PQftable(self.pgresult_ptr, column_number) + + def ftablecol(self, column_number: int) -> int: + return impl.PQftablecol(self.pgresult_ptr, column_number) + + def fformat(self, column_number: int) -> Format: + return Format(impl.PQfformat(self.pgresult_ptr, column_number)) + + def ftype(self, column_number: int) -> int: + return impl.PQftype(self.pgresult_ptr, column_number) + + def fmod(self, column_number: int) -> int: + return impl.PQfmod(self.pgresult_ptr, column_number) + + def fsize(self, column_number: int) -> int: + return impl.PQfsize(self.pgresult_ptr, column_number) + + @property + def binary_tuples(self) -> Format: + return Format(impl.PQbinaryTuples(self.pgresult_ptr)) + + def get_value( + self, row_number: int, column_number: int + ) -> Optional[bytes]: + cdef int crow = row_number + cdef int ccol = column_number + cdef int length = impl.PQgetlength(self.pgresult_ptr, crow, ccol) + cdef char *v; + if length: + v = impl.PQgetvalue(self.pgresult_ptr, crow, ccol) + # TODO: avoid copy + return v[:length] + else: + if impl.PQgetisnull(self.pgresult_ptr, crow, ccol): + return None + else: + return b"" + + @property + def nparams(self) -> int: + return impl.PQnparams(self.pgresult_ptr) + + def param_type(self, param_number: int) -> int: + return impl.PQparamtype(self.pgresult_ptr, param_number) + + @property + def command_status(self) -> Optional[bytes]: + cdef char *rv = impl.PQcmdStatus(self.pgresult_ptr) + if rv is not NULL: + return rv + else: + return None + + @property + def command_tuples(self) -> Optional[int]: + cdef char *rv = impl.PQcmdTuples(self.pgresult_ptr) + if rv is NULL: + return None + cdef bytes brv = rv + return int(brv) if brv else None + + @property + def oid_value(self) -> int: + return impl.PQoidValue(self.pgresult_ptr) + + def set_attributes(self, descriptions: List[PGresAttDesc]): + cdef int num = len(descriptions) + cdef impl.PGresAttDesc *attrs = PyMem_Malloc( + num * sizeof(impl.PGresAttDesc)) + + for i in range(num): + descr = descriptions[i] + attrs[i].name = descr.name + attrs[i].tableid = descr.tableid + attrs[i].columnid = descr.columnid + attrs[i].format = descr.format + attrs[i].typid = descr.typid + attrs[i].typlen = descr.typlen + attrs[i].atttypmod = descr.atttypmod + + cdef int res = impl.PQsetResultAttrs(self.pgresult_ptr, num, attrs); + PyMem_Free(attrs) + if (res == 0): + raise PQerror("PQsetResultAttrs failed") diff --git a/psycopg3_c/psycopg3_c/pq/pqbuffer.pyx b/psycopg3_c/psycopg3_c/pq/pqbuffer.pyx new file mode 100644 index 000000000..b4048a3df --- /dev/null +++ b/psycopg3_c/psycopg3_c/pq/pqbuffer.pyx @@ -0,0 +1,64 @@ +""" +PQbuffer object implementation. +""" + +# Copyright (C) 2020 The Psycopg Team + + +cdef class PQBuffer: + """ + Wrap a chunk of memory allocated by the libpq and expose it as memoryview. + """ + @staticmethod + cdef PQBuffer _from_buffer(unsigned char *buf, Py_ssize_t len): + cdef PQBuffer rv = PQBuffer.__new__(PQBuffer) + rv.buf = buf + rv.len = len + return rv + + def __cinit__(self): + self.buf = NULL + self.len = 0 + + def __dealloc__(self): + if self.buf: + impl.PQfreemem(self.buf) + + def __repr__(self): + return ( + f"{self.__class__.__module__}.{self.__class__.__qualname__}" + f"({bytes(self)})" + ) + + def __getbuffer__(self, Py_buffer *buffer, int flags): + buffer.buf = self.buf + buffer.obj = self + buffer.len = self.len + buffer.itemsize = sizeof(unsigned char) + buffer.readonly = 1 + buffer.ndim = 1 + buffer.format = NULL # unsigned char + buffer.shape = &self.len + buffer.strides = NULL + buffer.suboffsets = NULL + buffer.internal = NULL + + def __releasebuffer__(self, Py_buffer *buffer): + pass + + +cdef int _buffer_as_string_and_size( + data: "Buffer", char **ptr, Py_ssize_t *length +) except -1: + cdef Py_buffer buf + + if isinstance(data, bytes): + PyBytes_AsStringAndSize(data, ptr, length) + elif PyObject_CheckBuffer(data): + PyObject_GetBuffer(data, &buf, PyBUF_SIMPLE) + ptr[0] = buf.buf + length[0] = buf.len + PyBuffer_Release(&buf) + else: + raise TypeError(f"bytes or buffer expected, got {type(data)}") + diff --git a/psycopg3_c/psycopg3_c/pq_cython.pyx b/psycopg3_c/psycopg3_c/pq_cython.pyx index 4252cd873..5d1dfe44c 100644 --- a/psycopg3_c/psycopg3_c/pq_cython.pyx +++ b/psycopg3_c/psycopg3_c/pq_cython.pyx @@ -42,264 +42,8 @@ def version(): include "pq/pgconn.pyx" - - -cdef class PGresult: - def __cinit__(self): - self.pgresult_ptr = NULL - - @staticmethod - cdef PGresult _from_ptr(impl.PGresult *ptr): - cdef PGresult rv = PGresult.__new__(PGresult) - rv.pgresult_ptr = ptr - return rv - - def __dealloc__(self) -> None: - self.clear() - - def clear(self) -> None: - if self.pgresult_ptr is not NULL: - impl.PQclear(self.pgresult_ptr) - self.pgresult_ptr = NULL - - @property - def pgresult_ptr(self) -> Optional[int]: - if self.pgresult_ptr: - return self.pgresult_ptr - else: - return None - - @property - def status(self) -> ExecStatus: - cdef int rv = impl.PQresultStatus(self.pgresult_ptr) - return ExecStatus(rv) - - @property - def error_message(self) -> bytes: - return impl.PQresultErrorMessage(self.pgresult_ptr) - - def error_field(self, fieldcode: DiagnosticField) -> Optional[bytes]: - cdef char * rv = impl.PQresultErrorField(self.pgresult_ptr, fieldcode) - if rv is not NULL: - return rv - else: - return None - - @property - def ntuples(self) -> int: - return impl.PQntuples(self.pgresult_ptr) - - @property - def nfields(self) -> int: - return impl.PQnfields(self.pgresult_ptr) - - def fname(self, column_number: int) -> Optional[bytes]: - cdef char *rv = impl.PQfname(self.pgresult_ptr, column_number) - if rv is not NULL: - return rv - else: - return None - - def ftable(self, column_number: int) -> int: - return impl.PQftable(self.pgresult_ptr, column_number) - - def ftablecol(self, column_number: int) -> int: - return impl.PQftablecol(self.pgresult_ptr, column_number) - - def fformat(self, column_number: int) -> Format: - return Format(impl.PQfformat(self.pgresult_ptr, column_number)) - - def ftype(self, column_number: int) -> int: - return impl.PQftype(self.pgresult_ptr, column_number) - - def fmod(self, column_number: int) -> int: - return impl.PQfmod(self.pgresult_ptr, column_number) - - def fsize(self, column_number: int) -> int: - return impl.PQfsize(self.pgresult_ptr, column_number) - - @property - def binary_tuples(self) -> Format: - return Format(impl.PQbinaryTuples(self.pgresult_ptr)) - - def get_value( - self, row_number: int, column_number: int - ) -> Optional[bytes]: - cdef int crow = row_number - cdef int ccol = column_number - cdef int length = impl.PQgetlength(self.pgresult_ptr, crow, ccol) - cdef char *v; - if length: - v = impl.PQgetvalue(self.pgresult_ptr, crow, ccol) - # TODO: avoid copy - return v[:length] - else: - if impl.PQgetisnull(self.pgresult_ptr, crow, ccol): - return None - else: - return b"" - - @property - def nparams(self) -> int: - return impl.PQnparams(self.pgresult_ptr) - - def param_type(self, param_number: int) -> int: - return impl.PQparamtype(self.pgresult_ptr, param_number) - - @property - def command_status(self) -> Optional[bytes]: - cdef char *rv = impl.PQcmdStatus(self.pgresult_ptr) - if rv is not NULL: - return rv - else: - return None - - @property - def command_tuples(self) -> Optional[int]: - cdef char *rv = impl.PQcmdTuples(self.pgresult_ptr) - if rv is NULL: - return None - cdef bytes brv = rv - return int(brv) if brv else None - - @property - def oid_value(self) -> int: - return impl.PQoidValue(self.pgresult_ptr) - - def set_attributes(self, descriptions: List[PGresAttDesc]): - cdef int num = len(descriptions) - cdef impl.PGresAttDesc *attrs = PyMem_Malloc( - num * sizeof(impl.PGresAttDesc)) - - for i in range(num): - descr = descriptions[i] - attrs[i].name = descr.name - attrs[i].tableid = descr.tableid - attrs[i].columnid = descr.columnid - attrs[i].format = descr.format - attrs[i].typid = descr.typid - attrs[i].typlen = descr.typlen - attrs[i].atttypmod = descr.atttypmod - - cdef int res = impl.PQsetResultAttrs(self.pgresult_ptr, num, attrs); - PyMem_Free(attrs) - if (res == 0): - raise PQerror("PQsetResultAttrs failed") - - -cdef class PGcancel: - def __cinit__(self): - self.pgcancel_ptr = NULL - - @staticmethod - cdef PGcancel _from_ptr(impl.PGcancel *ptr): - cdef PGcancel rv = PGcancel.__new__(PGcancel) - rv.pgcancel_ptr = ptr - return rv - - def __dealloc__(self) -> None: - self.free() - - def free(self) -> None: - if self.pgcancel_ptr is not NULL: - impl.PQfreeCancel(self.pgcancel_ptr) - self.pgcancel_ptr = NULL - - def cancel(self) -> None: - cdef char buf[256] - cdef int res = impl.PQcancel(self.pgcancel_ptr, buf, sizeof(buf)) - if not res: - raise PQerror( - f"cancel failed: {buf.decode('utf8', 'ignore')}" - ) - - -class Conninfo: - @classmethod - def get_defaults(cls) -> List[ConninfoOption]: - cdef impl.PQconninfoOption *opts = impl.PQconndefaults() - if opts is NULL : - raise MemoryError("couldn't allocate connection defaults") - rv = _options_from_array(opts) - impl.PQconninfoFree(opts) - return rv - - @classmethod - def parse(cls, conninfo: bytes) -> List[ConninfoOption]: - cdef char *errmsg = NULL - cdef impl.PQconninfoOption *opts = impl.PQconninfoParse(conninfo, &errmsg) - if opts is NULL: - if errmsg is NULL: - raise MemoryError("couldn't allocate on conninfo parse") - else: - exc = PQerror(errmsg.decode("utf8", "replace")) - impl.PQfreemem(errmsg) - raise exc - - rv = _options_from_array(opts) - impl.PQconninfoFree(opts) - return rv - - def __repr__(self): - return f"<{type(self).__name__} ({self.keyword.decode('ascii')})>" - - +include "pq/pgresult.pyx" +include "pq/pgcancel.pyx" +include "pq/conninfo.pyx" include "pq/escaping.pyx" - - -cdef class PQBuffer: - """ - Wrap a chunk of memory allocated by the libpq and expose it as memoryview. - """ - @staticmethod - cdef PQBuffer _from_buffer(unsigned char *buf, Py_ssize_t len): - cdef PQBuffer rv = PQBuffer.__new__(PQBuffer) - rv.buf = buf - rv.len = len - return rv - - def __cinit__(self): - self.buf = NULL - self.len = 0 - - def __dealloc__(self): - if self.buf: - impl.PQfreemem(self.buf) - - def __repr__(self): - return ( - f"{self.__class__.__module__}.{self.__class__.__qualname__}" - f"({bytes(self)})" - ) - - def __getbuffer__(self, Py_buffer *buffer, int flags): - buffer.buf = self.buf - buffer.obj = self - buffer.len = self.len - buffer.itemsize = sizeof(unsigned char) - buffer.readonly = 1 - buffer.ndim = 1 - buffer.format = NULL # unsigned char - buffer.shape = &self.len - buffer.strides = NULL - buffer.suboffsets = NULL - buffer.internal = NULL - - def __releasebuffer__(self, Py_buffer *buffer): - pass - - -cdef int _buffer_as_string_and_size( - data: "Buffer", char **ptr, Py_ssize_t *length -) except -1: - cdef Py_buffer buf - - if isinstance(data, bytes): - PyBytes_AsStringAndSize(data, ptr, length) - elif PyObject_CheckBuffer(data): - PyObject_GetBuffer(data, &buf, PyBUF_SIMPLE) - ptr[0] = buf.buf - length[0] = buf.len - PyBuffer_Release(&buf) - else: - raise TypeError(f"bytes or buffer expected, got {type(data)}") +include "pq/pqbuffer.pyx"