fix: use timedelta instead of time to cast TIME column in B compatibility mode(#IAIE7I)

In mysql compatibility mode, we should convert the TIME column to a python datetime.timedelta type,
just like the behavior of PyMySQL and mysql-connector-python.

See also:
- PyMySQL: 95635f587b/pymysql/converters.py (L344)
- mysql-connector-python: 59817f3de4/mysql-connector-python/lib/mysql/connector/conversion.py (L593)
This commit is contained in:
vimiix
2024-08-21 12:23:47 +08:00
parent 544701b4d8
commit 6eb67283df
5 changed files with 47 additions and 7 deletions

View File

@ -49,7 +49,7 @@ extern "C" {
/* sql_compatibility values */ /* sql_compatibility values */
#define SQL_COMPATIBILITY_A 1 #define SQL_COMPATIBILITY_A 1
#define SQL_COMPATIBILITY_OTHER 5 #define SQL_COMPATIBILITY_OTHER 5
// #define SQL_COMPATIBILITY_B 2 #define SQL_COMPATIBILITY_B 2
// #define SQL_COMPATIBILITY_C 3 // #define SQL_COMPATIBILITY_C 3
// #define SQL_COMPATIBILITY_PG 4 // #define SQL_COMPATIBILITY_PG 4

View File

@ -1364,6 +1364,24 @@ end:
} }
static int set_sql_compatibility(connectionObject *self, char *value)
{
switch (value[0]) {
case 'A':
case 'a':
self->sql_compatibility = SQL_COMPATIBILITY_A;
break;
case 'B':
case 'b':
self->sql_compatibility = SQL_COMPATIBILITY_B;
break;
default:
self->sql_compatibility = SQL_COMPATIBILITY_OTHER;
break;
}
return 0;
}
/* initialization and finalization methods */ /* initialization and finalization methods */
static int static int
@ -1414,16 +1432,11 @@ connection_setup(connectionObject *self, const char *dsn, long int async)
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
pthread_mutex_lock(&self->lock); pthread_mutex_lock(&self->lock);
sql_compatibility_value = pq_get_guc_locked(self, "sql_compatibility", &_save); sql_compatibility_value = pq_get_guc_locked(self, "sql_compatibility", &_save);
set_sql_compatibility(self, sql_compatibility_value);
if (register_type_uint(self, &_save)) { goto exit; } if (register_type_uint(self, &_save)) { goto exit; }
pthread_mutex_unlock(&self->lock); pthread_mutex_unlock(&self->lock);
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;
if (strcmp(sql_compatibility_value, "A") == 0) {
self->sql_compatibility = SQL_COMPATIBILITY_A;
} else {
self->sql_compatibility = SQL_COMPATIBILITY_OTHER;
}
exit: exit:
if (sql_compatibility_value){ if (sql_compatibility_value){
free(sql_compatibility_value); free(sql_compatibility_value);

View File

@ -57,6 +57,16 @@ curs_get_cast(cursorObject *self, PyObject *oid)
if (cast) { return cast; } if (cast) { return cast; }
/* global lookup */ /* global lookup */
if (self->conn->sql_compatibility == SQL_COMPATIBILITY_B) {
/*
In mysql compatibility mode, we should convert the TIME column to a python datetime.timedelta type,
just like the behavior of PyMySQL and mysql-connector-python.
*/
if (PyLong_Check(oid)) {
long value = convert_time_oid_to_interval_oid(PyLong_AsLong(oid));
oid = PyLong_FromLong(value);
}
}
cast = PyDict_GetItem(psyco_types, oid); cast = PyDict_GetItem(psyco_types, oid);
Dprintf("curs_get_cast: global dict: %p", cast); Dprintf("curs_get_cast: global dict: %p", cast);
if (cast) { return cast; } if (cast) { return cast; }

View File

@ -88,5 +88,7 @@ HIDDEN PyObject *typecast_array_from_python(
HIDDEN PyObject *typecast_cast( HIDDEN PyObject *typecast_cast(
PyObject *self, const char *str, Py_ssize_t len, PyObject *curs); PyObject *self, const char *str, Py_ssize_t len, PyObject *curs);
HIDDEN long convert_time_oid_to_interval_oid(long value);
#endif /* !defined(PSYCOPG_TYPECAST_H) */ #endif /* !defined(PSYCOPG_TYPECAST_H) */
PyObject *typecast_new(PyObject *name, PyObject *values, PyObject *cast, PyObject *base); PyObject *typecast_new(PyObject *name, PyObject *values, PyObject *cast, PyObject *base);

View File

@ -31,6 +31,21 @@ static long int typecast_MACADDRARRAY_types[] = {1040, 0};
static long int typecast_UNKNOWN_types[] = {705, 0}; static long int typecast_UNKNOWN_types[] = {705, 0};
long convert_time_oid_to_interval_oid(long value)
{
int i = 0;
while (typecast_TIME_types[i] != 0) {
if (typecast_TIME_types[i] == value) {
Dprintf("convert time oid to interval oid");
value = typecast_INTERVAL_types[0];
break;
}
i++;
}
return value;
}
static typecastObject_initlist typecast_builtins[] = { static typecastObject_initlist typecast_builtins[] = {
{"NUMBER", typecast_NUMBER_types, typecast_NUMBER_cast, NULL}, {"NUMBER", typecast_NUMBER_types, typecast_NUMBER_cast, NULL},
{"LONGINTEGER", typecast_LONGINTEGER_types, typecast_LONGINTEGER_cast, NULL}, {"LONGINTEGER", typecast_LONGINTEGER_types, typecast_LONGINTEGER_cast, NULL},