Dropped PSYCOPG_DISPLAY_SIZE build parameter

Big and complex _pq_fetch_tuples simplified by moving per-column
calculation to a separate function.
This commit is contained in:
Daniele Varrazzo 2019-01-23 14:09:59 +00:00
parent 00cb2636f5
commit 4298718978
5 changed files with 160 additions and 153 deletions

1
NEWS
View File

@ -38,6 +38,7 @@ Other changes:
- Dropped deprecated `!register_tstz_w_secs()` (was previously a no-op).
- Dropped deprecated `!PersistentConnectionPool`. This pool class was mostly
designed to interact with Zope. Use `!ZPsycopgDA.pool` instead.
- Dropped `!PSYCOPG_DISPLAY_SIZE` build parameter.
- No longer use 2to3 during installation for Python 2 & 3 compatability. All
source files are now compatible with Python 2 & 3 as is.
- The `!psycopg2.test` package is no longer installed by ``python setup.py

View File

@ -216,10 +216,12 @@ introspection etc.
.. attribute:: display_size
The actual length of the column in bytes. Obtaining this value is
computationally intensive, so it is always `!None` unless the
:envvar:`PSYCOPG_DISPLAY_SIZE` parameter is set at compile time. See
also PQgetlength_.
Supposed to be the actual length of the column in bytes. Obtaining
this value is computationally intensive, so it is always `!None`.
.. versionchanged:: 2.8
It was previously possible to obtain this value using a compiler
flag at builtin.
.. attribute:: internal_size

View File

@ -46,8 +46,7 @@ static const char type_code_doc[] =
static const char display_size_doc[] =
"The actual length of the column in bytes.\n\n"
"Obtaining this value is computationally intensive, so it is always None\n"
"unless the PSYCOPG_DISPLAY_SIZE parameter is set at compile time.";
"Obtaining this value is computationally intensive, so it is always None";
static const char internal_size_doc[] =
"The size in bytes of the column associated to this column on the server.\n\n"

View File

@ -1229,12 +1229,147 @@ pq_get_last_result(connectionObject *conn)
1 - result from backend (possibly data is ready)
*/
static PyObject *
_get_cast(cursorObject *curs, PGresult *pgres, int i)
{
/* fill the right cast function by accessing three different dictionaries:
- the per-cursor dictionary, if available (can be NULL or None)
- the per-connection dictionary (always exists but can be null)
- the global dictionary (at module level)
if we get no defined cast use the default one */
PyObject *type = NULL;
PyObject *cast = NULL;
PyObject *rv = NULL;
Oid ftype = PQftype(pgres, i);
if (!(type = PyInt_FromLong(ftype))) { goto exit; }
Dprintf("_pq_fetch_tuples: looking for cast %d:", ftype);
if (!(cast = curs_get_cast(curs, type))) { goto exit; }
/* else if we got binary tuples and if we got a field that
is binary use the default cast
FIXME: what the hell am I trying to do here? This just can't work..
*/
if (cast == psyco_default_binary_cast && PQbinaryTuples(pgres)) {
Dprintf("_pq_fetch_tuples: Binary cursor and "
"binary field: %i using default cast", ftype);
cast = psyco_default_cast;
}
Dprintf("_pq_fetch_tuples: using cast at %p for type %d", cast, ftype);
/* success */
Py_INCREF(cast);
rv = cast;
exit:
Py_XDECREF(type);
return rv;
}
static PyObject *
_make_column(connectionObject *conn, PGresult *pgres, int i)
{
Oid ftype = PQftype(pgres, i);
int fsize = PQfsize(pgres, i);
int fmod = PQfmod(pgres, i);
Oid ftable = PQftable(pgres, i);
int ftablecol = PQftablecol(pgres, i);
columnObject *column = NULL;
PyObject *rv = NULL;
if (!(column = (columnObject *)PyObject_CallObject(
(PyObject *)&columnType, NULL))) {
goto exit;
}
/* fill the type and name fields */
{
PyObject *tmp;
if (!(tmp = PyInt_FromLong(ftype))) {
goto exit;
}
column->type_code = tmp;
}
{
PyObject *tmp;
if (!(tmp = conn_text_from_chars(conn, PQfname(pgres, i)))) {
goto exit;
}
column->name = tmp;
}
/* display size is the maximum size of this field result tuples. */
Py_INCREF(Py_None);
column->display_size = Py_None;
/* size on the backend */
if (fmod > 0) {
fmod = fmod - sizeof(int);
}
if (fsize == -1) {
if (ftype == NUMERICOID) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong((fmod >> 16)))) { goto exit; }
column->internal_size = tmp;
}
else { /* If variable length record, return maximum size */
PyObject *tmp;
if (!(tmp = PyInt_FromLong(fmod))) { goto exit; }
column->internal_size = tmp;
}
}
else {
PyObject *tmp;
if (!(tmp = PyInt_FromLong(fsize))) { goto exit; }
column->internal_size = tmp;
}
/* scale and precision */
if (ftype == NUMERICOID) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong((fmod >> 16) & 0xFFFF))) {
goto exit;
}
column->precision = tmp;
if (!(tmp = PyInt_FromLong(fmod & 0xFFFF))) {
goto exit;
}
column->scale = tmp;
}
/* table_oid, table_column */
if (ftable != InvalidOid) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong((long)ftable))) { goto exit; }
column->table_oid = tmp;
}
if (ftablecol > 0) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong((long)ftablecol))) { goto exit; }
column->table_column = tmp;
}
/* success */
rv = (PyObject *)column;
column = NULL;
exit:
Py_XDECREF(column);
return rv;
}
RAISES_NEG static int
_pq_fetch_tuples(cursorObject *curs)
{
int i, *dsize = NULL;
int i;
int pgnfields;
int pgbintuples;
int rv = -1;
PyObject *description = NULL;
PyObject *casts = NULL;
@ -1244,7 +1379,6 @@ _pq_fetch_tuples(cursorObject *curs)
Py_END_ALLOW_THREADS;
pgnfields = PQnfields(curs->pgres);
pgbintuples = PQbinaryTuples(curs->pgres);
curs->notuples = 0;
@ -1255,158 +1389,31 @@ _pq_fetch_tuples(cursorObject *curs)
if (!(casts = PyTuple_New(pgnfields))) { goto exit; }
curs->columns = pgnfields;
/* calculate the display size for each column (cpu intensive, can be
switched off at configuration time) */
#ifdef PSYCOPG_DISPLAY_SIZE
if (!(dsize = PyMem_New(int, pgnfields))) {
PyErr_NoMemory();
goto exit;
}
Py_BEGIN_ALLOW_THREADS;
if (dsize != NULL) {
int j, len;
for (i=0; i < pgnfields; i++) {
dsize[i] = -1;
}
for (j = 0; j < curs->rowcount; j++) {
/* calculate each field's parameters and typecasters */
for (i = 0; i < pgnfields; i++) {
len = PQgetlength(curs->pgres, j, i);
if (len > dsize[i]) dsize[i] = len;
}
}
}
Py_END_ALLOW_THREADS;
#endif
/* calculate various parameters and typecasters */
for (i = 0; i < pgnfields; i++) {
Oid ftype = PQftype(curs->pgres, i);
int fsize = PQfsize(curs->pgres, i);
int fmod = PQfmod(curs->pgres, i);
Oid ftable = PQftable(curs->pgres, i);
int ftablecol = PQftablecol(curs->pgres, i);
columnObject *column = NULL;
PyObject *type = NULL;
PyObject *column = NULL;
PyObject *cast = NULL;
if (!(column = (columnObject *)PyObject_CallObject(
(PyObject *)&columnType, NULL))) {
if (!(column = _make_column(curs->conn, curs->pgres, i))) {
goto exit;
}
/* fill the right cast function by accessing three different dictionaries:
- the per-cursor dictionary, if available (can be NULL or None)
- the per-connection dictionary (always exists but can be null)
- the global dictionary (at module level)
if we get no defined cast use the default one */
if (!(type = PyInt_FromLong(ftype))) {
goto err_for;
}
Dprintf("_pq_fetch_tuples: looking for cast %d:", ftype);
cast = curs_get_cast(curs, type);
/* else if we got binary tuples and if we got a field that
is binary use the default cast
FIXME: what the hell am I trying to do here? This just can't work..
*/
if (pgbintuples && cast == psyco_default_binary_cast) {
Dprintf("_pq_fetch_tuples: Binary cursor and "
"binary field: %i using default cast",
PQftype(curs->pgres,i));
cast = psyco_default_cast;
}
Dprintf("_pq_fetch_tuples: using cast at %p for type %d",
cast, PQftype(curs->pgres,i));
Py_INCREF(cast);
PyTuple_SET_ITEM(casts, i, cast);
/* 1/ fill the other fields */
{
PyObject *tmp;
if (!(tmp = conn_text_from_chars(
curs->conn, PQfname(curs->pgres, i)))) {
goto err_for;
}
column->name = tmp;
}
column->type_code = type;
type = NULL;
/* 2/ display size is the maximum size of this field result tuples. */
if (dsize && dsize[i] >= 0) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong(dsize[i]))) { goto err_for; }
column->display_size = tmp;
}
/* 3/ size on the backend */
if (fmod > 0) fmod = fmod - sizeof(int);
if (fsize == -1) {
if (ftype == NUMERICOID) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong((fmod >> 16)))) { goto err_for; }
column->internal_size = tmp;
}
else { /* If variable length record, return maximum size */
PyObject *tmp;
if (!(tmp = PyInt_FromLong(fmod))) { goto err_for; }
column->internal_size = tmp;
}
}
else {
PyObject *tmp;
if (!(tmp = PyInt_FromLong(fsize))) { goto err_for; }
column->internal_size = tmp;
}
/* 4,5/ scale and precision */
if (ftype == NUMERICOID) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong((fmod >> 16) & 0xFFFF))) {
goto err_for;
}
column->precision = tmp;
if (!(tmp = PyInt_FromLong(fmod & 0xFFFF))) {
goto err_for;
}
column->scale = tmp;
}
/* table_oid, table_column */
if (ftable != InvalidOid) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong((long)ftable))) { goto err_for; }
column->table_oid = tmp;
}
if (ftablecol > 0) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong((long)ftablecol))) { goto err_for; }
column->table_column = tmp;
}
PyTuple_SET_ITEM(description, i, (PyObject *)column);
column = NULL;
continue;
err_for:
Py_XDECREF(type);
Py_XDECREF(column);
if (!(cast = _get_cast(curs, curs->pgres, i))) {
goto exit;
}
PyTuple_SET_ITEM(casts, i, cast);
}
curs->description = description;
description = NULL;
curs->casts = casts;
casts = NULL;
curs->description = description; description = NULL;
curs->casts = casts; casts = NULL;
rv = 0;
exit:
PyMem_Free(dsize);
Py_XDECREF(description);
Py_XDECREF(casts);
@ -1417,6 +1424,7 @@ exit:
return rv;
}
void
_read_rowcount(cursorObject *curs)
{

View File

@ -1,9 +1,6 @@
[build_ext]
define=
# PSYCOPG_DISPLAY_SIZE enable display size calculation (a little slower)
# HAVE_PQFREEMEM should be defined on PostgreSQL >= 7.4
# PSYCOPG_DEBUG can be added to enable verbose debug information
define=
# "pg_config" is required to locate PostgreSQL headers and libraries needed to
# build psycopg2. If pg_config is not in the path or is installed under a