From 5948b860ad5ce90b47de2fde970d7e796a41505e Mon Sep 17 00:00:00 2001 From: kennykb Date: Fri, 13 Jul 2012 01:35:10 +0000 Subject: [PATCH] Correct a problem where SELECT operations returning bytearrays yield corrupted data from PostgreSQL servers at version 9.0 and beyond. --- tdbcpostgres/ChangeLog | 8 ++++ tdbcpostgres/generic/tdbcpostgres.c | 70 +++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/tdbcpostgres/ChangeLog b/tdbcpostgres/ChangeLog index 8d8ce88..12b3d9d 100644 --- a/tdbcpostgres/ChangeLog +++ b/tdbcpostgres/ChangeLog @@ -1,3 +1,11 @@ +2012-07-13 Kevin B. Kenny + + * generic/tdbcpostgres.c: Corrected a problem where PostgreSQL 9.0 + and beyond return byte arrays in an + incompatible format, yielding silent + data corruption in SELECT operations. + (Bug [4357c31d89]) + 2012-07-10 Kevin B. Kenny * generic/tdbcpostgres.c: Fixed a non-static table argument to diff --git a/tdbcpostgres/generic/tdbcpostgres.c b/tdbcpostgres/generic/tdbcpostgres.c index 0a64a7e..a033403 100644 --- a/tdbcpostgres/generic/tdbcpostgres.c +++ b/tdbcpostgres/generic/tdbcpostgres.c @@ -393,6 +393,9 @@ enum IsolationLevel { /* Static functions defined within this file */ +static int DeterminePostgresMajorVersion(Tcl_Interp* interp, + ConnectionData* cdata, + int* versionPtr); static void DummyNoticeProcessor(void*, const PGresult*); static int ExecSimpleQuery(Tcl_Interp* interp, PGconn * pgPtr, const char * query, PGresult** resOut); @@ -846,6 +849,53 @@ static int TransferResultError( } } +/* + *----------------------------------------------------------------------------- + * + * DeterminePostgresMajorVersion -- + * + * Determine the major version of the PostgreSQL server at the + * other end of a connection. + * + * Results: + * Returns a standard Tcl error code. + * + * Side effects: + * Stores the version number in '*versionPtr' if successful. + * + *----------------------------------------------------------------------------- + */ + +static int +DeterminePostgresMajorVersion(Tcl_Interp* interp, + /* Tcl interpreter */ + ConnectionData* cdata, + /* Connection data */ + int* versionPtr) + /* OUTPUT: PostgreSQL server version */ +{ + PGresult* res; /* Result of a Postgres query */ + int status = TCL_ERROR; /* Status return */ + char* versionStr; /* Version information from server */ + + if (ExecSimpleQuery(interp, cdata->pgPtr, + "SELECT version()", &res) == TCL_OK) { + versionStr = PQgetvalue(res, 0, 0); + if (sscanf(versionStr, " PostgreSQL %d", versionPtr) == 1) { + status = TCL_OK; + } else { + Tcl_Obj* result = Tcl_NewStringObj("unable to parse PostgreSQL " + "version: \"", -1); + Tcl_AppendToObj(result, versionStr, -1); + Tcl_AppendToObj(result, "\"", -1); + Tcl_SetErrorCode(interp, "TDBC", "GENERAL_ERROR", "HY000", + "POSTGRES", "-1", NULL); + } + PQclear(res); + } + return status; +} + /* *----------------------------------------------------------------------------- * @@ -991,6 +1041,7 @@ ConfigureConnection( Tcl_Obj* retval; Tcl_Obj* optval; + int vers; /* PostgreSQL major version */ if (cdata->pgPtr != NULL) { @@ -1176,6 +1227,25 @@ ConfigureConnection( } cdata->readOnly = readOnly; } + + /* Determine the PostgreSQL version in use */ + + if (DeterminePostgresMajorVersion(interp, cdata, &vers) != TCL_OK) { + return TCL_ERROR; + } + + /* + * On PostgreSQL 9.0 and later, change 'bytea_output' to the + * backward-compatible 'escape' setting, so that the code in + * ResultSetNextrowMethod will retrieve byte array values correctly + * on either 8.x or 9.x servers. + */ + if (vers >= 9) { + if (ExecSimpleQuery(interp, cdata->pgPtr, + "SET bytea_output = 'escape'", NULL) != TCL_OK) { + return TCL_ERROR; + } + } return TCL_OK; }