Author: cito
Date: Sat Feb 6 07:17:34 2016
New Revision: 824
Log:
Decode error message properly when using Python 3
The error message can contain non-ASCII characters and must be properly decoded,
using either the client encoding or the current locale if the error happened
during
connection and the client encoding is not yet set.
Modified:
trunk/pgdb.py
trunk/pgmodule.c
Modified: trunk/pgdb.py
==============================================================================
--- trunk/pgdb.py Fri Feb 5 16:35:34 2016 (r823)
+++ trunk/pgdb.py Sat Feb 6 07:17:34 2016 (r824)
@@ -1374,7 +1374,7 @@
### Module Interface
-_connect_ = connect
+_connect = connect
def connect(dsn=None,
user=None, password=None,
@@ -1419,7 +1419,7 @@
dbuser = None
# open the connection
- cnx = _connect_(dbbase, dbhost, dbport, dbopt, dbuser, dbpasswd)
+ cnx = _connect(dbbase, dbhost, dbport, dbopt, dbuser, dbpasswd)
return Connection(cnx)
Modified: trunk/pgmodule.c
==============================================================================
--- trunk/pgmodule.c Fri Feb 5 16:35:34 2016 (r823)
+++ trunk/pgmodule.c Sat Feb 6 07:17:34 2016 (r824)
@@ -214,7 +214,7 @@
/* shared function for encoding and decoding strings */
static PyObject *
-get_decoded_string(char *str, Py_ssize_t size, int encoding)
+get_decoded_string(const char *str, Py_ssize_t size, int encoding)
{
if (encoding == pg_encoding_utf8)
return PyUnicode_DecodeUTF8(str, size, "strict");
@@ -1366,28 +1366,31 @@
return DatabaseError;
}
-/* sets database error with sqlstate attribute */
-/* This should be used when raising a subclass of DatabaseError */
+/* sets database error message and sqlstate attribute */
static void
-set_dberror(PyObject *type, const char *msg, PGresult *result)
+set_error_msg_and_state(PyObject *type,
+ const char *msg, int encoding, const char *sqlstate)
{
PyObject *err_obj, *msg_obj, *sql_obj = NULL;
- if (result)
+#if IS_PY3
+ if (encoding == -1) /* unknown */
{
- char *sqlstate = PQresultErrorField(result, PG_DIAG_SQLSTATE);
- if (sqlstate)
- {
- sql_obj = PyStr_FromStringAndSize(sqlstate, 5);
- type = get_error_type(sqlstate);
- }
+ msg_obj = PyUnicode_DecodeLocale(msg, NULL);
}
- if (!sql_obj)
+ else
+ msg_obj = get_decoded_string(msg, strlen(msg), encoding);
+ if (!msg_obj) /* cannot decode */
+#endif
+ msg_obj = PyBytes_FromString(msg);
+
+ if (sqlstate)
+ sql_obj = PyStr_FromStringAndSize(sqlstate, 5);
+ else
{
- Py_INCREF(Py_None);
- sql_obj = Py_None;
+ Py_INCREF(Py_None); sql_obj = Py_None;
}
- msg_obj = PyStr_FromString(msg);
+
err_obj = PyObject_CallFunctionObjArgs(type, msg_obj, NULL);
if (err_obj)
{
@@ -1403,13 +1406,44 @@
}
}
+/* sets given database error message */
+static void
+set_error_msg(PyObject *type, const char *msg)
+{
+ set_error_msg_and_state(type, msg, pg_encoding_ascii, NULL);
+}
+
+/* sets database error from connection and/or result */
+static void
+set_error(PyObject *type, const char * msg, PGconn *cnx, PGresult *result)
+{
+ char *sqlstate = NULL; int encoding = pg_encoding_ascii;
+
+ if (cnx)
+ {
+ char *err_msg = PQerrorMessage(cnx);
+ if (err_msg)
+ {
+ msg = err_msg;
+ encoding = PQclientEncoding(cnx);
+ }
+ }
+ if (result)
+ {
+ sqlstate = PQresultErrorField(result, PG_DIAG_SQLSTATE);
+ if (sqlstate) type = get_error_type(sqlstate);
+ }
+
+ set_error_msg_and_state(type, msg, encoding, sqlstate);
+}
+
/* checks connection validity */
static int
check_cnx_obj(connObject *self)
{
if (!self->valid)
{
- set_dberror(OperationalError, "Connection has been closed",
NULL);
+ set_error_msg(OperationalError, "Connection has been closed");
return 0;
}
return 1;
@@ -1583,7 +1617,7 @@
if (!self->lo_oid)
{
- set_dberror(IntegrityError, "Object is not valid (null oid)",
NULL);
+ set_error_msg(IntegrityError, "Object is not valid (null oid)");
return 0;
}
@@ -2287,8 +2321,8 @@
case PGRES_BAD_RESPONSE:
case PGRES_FATAL_ERROR:
case PGRES_NONFATAL_ERROR:
- set_dberror(ProgrammingError,
- PQerrorMessage(self->cnx), result);
+ set_error(ProgrammingError, "Cannot execute
query",
+ self->cnx, result);
break;
case PGRES_COMMAND_OK:
{
/* INSERT, UPDATE, DELETE */
@@ -2315,7 +2349,7 @@
Py_INCREF(Py_None);
return Py_None;
default:
- set_dberror(InternalError, "Unknown result
status", result);
+ set_error_msg(InternalError, "Unknown result
status");
}
PQclear(result);
@@ -3041,7 +3075,7 @@
lo_oid = lo_creat(self->cnx, mode);
if (lo_oid == 0)
{
- set_dberror(OperationalError, "Can't create large object",
NULL);
+ set_error_msg(OperationalError, "Can't create large object");
return NULL;
}
@@ -3105,7 +3139,7 @@
lo_oid = lo_import(self->cnx, name);
if (lo_oid == 0)
{
- set_dberror(OperationalError, "Can't create large object",
NULL);
+ set_error_msg(OperationalError, "Can't create large object");
return NULL;
}
@@ -3274,7 +3308,7 @@
/* connection object cannot already be closed */
if (!self->cnx)
{
- set_dberror(InternalError, "Connection already closed", NULL);
+ set_error_msg(InternalError, "Connection already closed");
return NULL;
}
@@ -3521,20 +3555,19 @@
{
if (!self->valid)
{
- set_dberror(OperationalError, "Object has been closed", NULL);
+ set_error_msg(OperationalError, "Object has been closed");
return 0;
}
if ((level & CHECK_RESULT) && !self->result)
{
- set_dberror(DatabaseError, "No result", NULL);
+ set_error_msg(DatabaseError, "No result");
return 0;
}
if ((level & CHECK_DQL) && self->result_type != RESULT_DQL)
{
- set_dberror(DatabaseError,
- "Last query did not return tuples", self->result);
+ set_error_msg(DatabaseError, "Last query did not return
tuples");
return 0;
}
@@ -3682,12 +3715,12 @@
case PGRES_BAD_RESPONSE:
case PGRES_FATAL_ERROR:
case PGRES_NONFATAL_ERROR:
- set_dberror(ProgrammingError,
- PQerrorMessage(self->pgcnx->cnx), self->result);
+ set_error(ProgrammingError, "Cannot execute command",
+ self->pgcnx->cnx, self->result);
break;
default:
- set_dberror(InternalError, "Internal error: "
- "unknown result status", self->result);
+ set_error_msg(InternalError, "Internal error: "
+ "unknown result status");
}
/* frees result and returns error */
@@ -4425,7 +4458,7 @@
if (!(npgobj = PyObject_NEW(connObject, &connType)))
{
- set_dberror(InternalError, "Can't create new connection
object", NULL);
+ set_error_msg(InternalError, "Can't create new connection
object");
return NULL;
}
@@ -4448,7 +4481,7 @@
if (PQstatus(npgobj->cnx) == CONNECTION_BAD)
{
- set_dberror(InternalError, PQerrorMessage(npgobj->cnx), NULL);
+ set_error(InternalError, "Cannot connect", npgobj->cnx, NULL);
Py_XDECREF(npgobj);
return NULL;
}
_______________________________________________
PyGreSQL mailing list
[email protected]
https://mail.vex.net/mailman/listinfo.cgi/pygresql