Skip to content

Commit

Permalink
Fix for CONPY-191:
Browse files Browse the repository at this point in the history
When connected to a database server which doesn't support bulk
execution, we need to set bind.is_null instead of changing the buffer
type to MYSQL_TYPE_NULL. This will keep the original buffer type.
  • Loading branch information
9EOR9 committed Feb 8, 2022
1 parent 7b63daa commit 4855a7b
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 101 deletions.
1 change: 1 addition & 0 deletions include/mariadb_python.h
Expand Up @@ -236,6 +236,7 @@ typedef struct {
void *buffer;
unsigned char num[8];
MYSQL_TIME tm;
uint8_t is_null;
} MrdbParamValue;

typedef struct {
Expand Down
203 changes: 102 additions & 101 deletions mariadb/mariadb_codecs.c
Expand Up @@ -1072,7 +1072,7 @@ mariadb_get_parameter_info(MrdbCursor *self,
}

if (!param->buffer_type ||
param->buffer_type == MYSQL_TYPE_NULL)
param->buffer_type == MYSQL_TYPE_NULL)
{
param->buffer_type= pinfo.type;
}
Expand Down Expand Up @@ -1298,11 +1298,12 @@ mariadb_param_to_bind(MrdbCursor *self,
bind->u.indicator[0]= value->indicator;
goto end;
}

if (!value->value)
value->is_null= value->value == NULL;
if (value->is_null)
{
bind->buffer_type= MYSQL_TYPE_NULL;
bind->is_null= (my_bool *)&value->is_null;
} else {
bind->is_null= NULL;
if (IS_NUM(bind->buffer_type))
{
bind->buffer= value->num;
Expand All @@ -1313,104 +1314,104 @@ mariadb_param_to_bind(MrdbCursor *self,
if (_PyLong_Sign(value->value) < 0)
is_negative= 1;
}
}

switch(bind->buffer_type)
{
case MYSQL_TYPE_TINY:
if (!is_negative)
{
if ((value->num[0]= (uint8_t)PyLong_AsUnsignedLong(value->value)) > 0x7F)
bind->is_unsigned= 1;
}
else {
value->num[0]= (int8_t)PyLong_AsLong(value->value);
}
break;
case MYSQL_TYPE_SHORT:
if (!is_negative)
{
if ((*(uint16_t *)&value->num= (uint16_t)PyLong_AsUnsignedLong(value->value)) > 0x7FFF)
bind->is_unsigned= 1;
}
else {
*(int16_t *)&value->num= (int16_t)PyLong_AsLong(value->value);
}
break;
case MYSQL_TYPE_LONG:
if (!is_negative)
{
if ((*(uint32_t *)&value->num= (uint32_t)PyLong_AsUnsignedLong(value->value)) > 0x7FFFFFFF)
bind->is_unsigned= 1;
}
else {
*(int32_t *)&value->num= (int32_t)PyLong_AsLong(value->value);
}
break;
case MYSQL_TYPE_LONGLONG:
if (!is_negative)
{
if ((*(uint64_t *)value->num= (uint64_t)PyLong_AsUnsignedLongLong(value->value)) > 0x7FFFFFFFFFFFFFFF)
bind->is_unsigned= 1;
}
else {
*(int64_t *)value->num= (int64_t)PyLong_AsLongLong(value->value);
}
break;
case MYSQL_TYPE_DOUBLE:
*(double *)value->num= (double)PyFloat_AsDouble(value->value);
break;
case MYSQL_TYPE_LONG_BLOB:
bind->buffer_length= (unsigned long)PyBytes_GET_SIZE(value->value);
bind->buffer= (void *) PyBytes_AS_STRING(value->value);
break;
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_DATETIME:
bind->buffer= &value->tm;
if (PyDelta_CheckExact(value->value))
mariadb_pydelta_to_tm(value->value, &value->tm);
else
mariadb_pydate_to_tm(bind->buffer_type, value->value, &value->tm);
break;
case MYSQL_TYPE_NEWDECIMAL:
{
Py_ssize_t len;
PyObject *obj= NULL;
char *p;

if (value->free_me)
MARIADB_FREE_MEM(value->buffer);
if (!strcmp(Py_TYPE(value->value)->tp_name, "decimal.Decimal") ||
!strcmp(Py_TYPE(value->value)->tp_name, "Decimal"))
{
obj= PyObject_Str(value->value);
p= (void *)PyUnicode_AsUTF8AndSize(obj, &len);
}
else
{
obj= PyObject_Str(value->value);
p= (void *)PyUnicode_AsUTF8AndSize(obj, &len);
}
bind->buffer= value->buffer= PyMem_RawCalloc(1, len);
memcpy(value->buffer, p, len);
value->free_me= 1;
bind->buffer_length= (unsigned long)len;
Py_DECREF(obj);
}
break;
case MYSQL_TYPE_VAR_STRING:
{
Py_ssize_t len;

bind->buffer= (void *)PyUnicode_AsUTF8AndSize(value->value, &len);
bind->buffer_length= (unsigned long)len;
break;
}
case MYSQL_TYPE_NULL:
break;
default:
rc= 1;
switch(bind->buffer_type)
{
case MYSQL_TYPE_TINY:
if (!is_negative)
{
if ((value->num[0]= (uint8_t)PyLong_AsUnsignedLong(value->value)) > 0x7F)
bind->is_unsigned= 1;
}
else {
value->num[0]= (int8_t)PyLong_AsLong(value->value);
}
break;
case MYSQL_TYPE_SHORT:
if (!is_negative)
{
if ((*(uint16_t *)&value->num= (uint16_t)PyLong_AsUnsignedLong(value->value)) > 0x7FFF)
bind->is_unsigned= 1;
}
else {
*(int16_t *)&value->num= (int16_t)PyLong_AsLong(value->value);
}
break;
case MYSQL_TYPE_LONG:
if (!is_negative)
{
if ((*(uint32_t *)&value->num= (uint32_t)PyLong_AsUnsignedLong(value->value)) > 0x7FFFFFFF)
bind->is_unsigned= 1;
}
else {
*(int32_t *)&value->num= (int32_t)PyLong_AsLong(value->value);
}
break;
case MYSQL_TYPE_LONGLONG:
if (!is_negative)
{
if ((*(uint64_t *)value->num= (uint64_t)PyLong_AsUnsignedLongLong(value->value)) > 0x7FFFFFFFFFFFFFFF)
bind->is_unsigned= 1;
}
else {
*(int64_t *)value->num= (int64_t)PyLong_AsLongLong(value->value);
}
break;
case MYSQL_TYPE_DOUBLE:
*(double *)value->num= (double)PyFloat_AsDouble(value->value);
break;
case MYSQL_TYPE_LONG_BLOB:
bind->buffer_length= (unsigned long)PyBytes_GET_SIZE(value->value);
bind->buffer= (void *) PyBytes_AS_STRING(value->value);
break;
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_DATETIME:
bind->buffer= &value->tm;
if (PyDelta_CheckExact(value->value))
mariadb_pydelta_to_tm(value->value, &value->tm);
else
mariadb_pydate_to_tm(bind->buffer_type, value->value, &value->tm);
break;
case MYSQL_TYPE_NEWDECIMAL:
{
Py_ssize_t len;
PyObject *obj= NULL;
char *p;

if (value->free_me)
MARIADB_FREE_MEM(value->buffer);
if (!strcmp(Py_TYPE(value->value)->tp_name, "decimal.Decimal") ||
!strcmp(Py_TYPE(value->value)->tp_name, "Decimal"))
{
obj= PyObject_Str(value->value);
p= (void *)PyUnicode_AsUTF8AndSize(obj, &len);
}
else
{
obj= PyObject_Str(value->value);
p= (void *)PyUnicode_AsUTF8AndSize(obj, &len);
}
bind->buffer= value->buffer= PyMem_RawCalloc(1, len);
memcpy(value->buffer, p, len);
value->free_me= 1;
bind->buffer_length= (unsigned long)len;
Py_DECREF(obj);
}
break;
case MYSQL_TYPE_VAR_STRING:
{
Py_ssize_t len;

bind->buffer= (void *)PyUnicode_AsUTF8AndSize(value->value, &len);
bind->buffer_length= (unsigned long)len;
break;
}
case MYSQL_TYPE_NULL:
break;
default:
rc= 1;
}
}
end:
MARIADB_BLOCK_THREADS(self);
Expand Down
14 changes: 14 additions & 0 deletions testing/test/integration/test_cursor.py
Expand Up @@ -1163,6 +1163,20 @@ def test_conpy150(self):
del cursor
connection.close()

def test_conpy191(self):
connection= create_connection()
cursor= connection.cursor()
cursor.execute("create temporary table t1 (a int, b int, c int)")
data= [(None,1,1),(2,None,2),(3,3,None)]

cursor.executemany("INSERT INTO t1 VALUES (?,?,?)", data);

cursor.execute("SELECT a,b,c FROM t1")
result= cursor.fetchall()
self.assertEqual(data, result)
del cursor
connection.close()

def test_conpy91(self):
with create_connection() as connection:
with connection.cursor() as cursor:
Expand Down

0 comments on commit 4855a7b

Please sign in to comment.