Follow up for CONPY-299

- Added byteswap function for vector (bigendian)
- Use buffer protocol instead of calling array's
  methods
This commit is contained in:
Georg Richter
2025-02-10 13:59:43 +01:00
parent 9508904911
commit befe7000c9
2 changed files with 183 additions and 84 deletions

View File

@ -24,6 +24,53 @@
#define IS_DECIMAL_TYPE(type) \ #define IS_DECIMAL_TYPE(type) \
((type) == MYSQL_TYPE_NEWDECIMAL || (type) == MYSQL_TYPE_DOUBLE || (type) == MYSQL_TYPE_FLOAT) ((type) == MYSQL_TYPE_NEWDECIMAL || (type) == MYSQL_TYPE_DOUBLE || (type) == MYSQL_TYPE_FLOAT)
static char *ma_byteswap(char *buf, size_t itemsize, size_t len)
{
char *p;
Py_ssize_t i;
switch (itemsize) {
case 1:
return buf;
case 2:
for (p = buf, i = len; --i >= 0; p += itemsize) {
char p0 = p[0];
p[0] = p[1];
p[1] = p0;
}
break;
case 4:
for (p = buf, i = len; --i >= 0; p += itemsize) {
char p0 = p[0];
char p1 = p[1];
p[0] = p[3];
p[1] = p[2];
p[2] = p1;
p[3] = p0;
}
break;
case 8:
for (p = buf, i = len; --i >= 0; p += itemsize) {
char p0 = p[0];
char p1 = p[1];
char p2 = p[2];
char p3 = p[3];
p[0] = p[7];
p[1] = p[6];
p[2] = p[5];
p[3] = p[4];
p[4] = p3;
p[5] = p2;
p[6] = p1;
p[7] = p0;
}
break;
default:
return 0;
}
return buf;
}
long MrdbIndicator_AsLong(PyObject *column) long MrdbIndicator_AsLong(PyObject *column)
{ {
PyObject *pyLong= PyObject_GetAttrString(column, "indicator"); PyObject *pyLong= PyObject_GetAttrString(column, "indicator");
@ -299,7 +346,7 @@ static uint8_t check_time(MYSQL_TIME *tm)
Year must be < 10000, month < 12, day < 32 Year must be < 10000, month < 12, day < 32
Years with 2 digits, are converted to values 1970-2069 according to Years with 2 digits, are converted to values 1970-2069 according to
usual rules: usual rules:
00-69 is converted to 2000-2069. 00-69 is converted to 2000-2069.
@ -382,7 +429,7 @@ int Py_str_to_TIME(const char *str, size_t length, MYSQL_TIME *tm)
{ {
if (parse_time(p, end - p, &p, tm)) if (parse_time(p, end - p, &p, tm))
goto error; goto error;
tm->year = tm->month = tm->day = 0; tm->year = tm->month = tm->day = 0;
tm->time_type = MYSQL_TIMESTAMP_TIME; tm->time_type = MYSQL_TIMESTAMP_TIME;
return 0; return 0;
@ -419,7 +466,7 @@ error:
static PyObject *Mrdb_GetTimeDelta(MYSQL_TIME *tm) static PyObject *Mrdb_GetTimeDelta(MYSQL_TIME *tm)
{ {
int days, hour, minute, second, second_part; int days, hour, minute, second, second_part;
hour= (tm->neg) ? -1 * tm->hour : tm->hour; hour= (tm->neg) ? -1 * tm->hour : tm->hour;
minute= (tm->neg) ? -1 * tm->minute : tm->minute; minute= (tm->neg) ? -1 * tm->minute : tm->minute;
second= (tm->neg) ? -1 * tm->second : tm->second; second= (tm->neg) ? -1 * tm->second : tm->second;
@ -428,7 +475,7 @@ static PyObject *Mrdb_GetTimeDelta(MYSQL_TIME *tm)
days= hour / 24; days= hour / 24;
hour= hour % 24; hour= hour % 24;
second= hour * 3600 + minute * 60 + second; second= hour * 3600 + minute * 60 + second;
return PyDelta_FromDSU(days, second, second_part); return PyDelta_FromDSU(days, second, second_part);
} }
@ -452,7 +499,7 @@ static PyObject *ma_convert_value(MrdbCursor *self,
} }
return new_value; return new_value;
} }
void void
field_fetch_fromtext(MrdbCursor *self, char *data, unsigned int column) field_fetch_fromtext(MrdbCursor *self, char *data, unsigned int column)
{ {
@ -492,8 +539,8 @@ field_fetch_fromtext(MrdbCursor *self, char *data, unsigned int column)
self->values[column]= PyLong_FromString(p, NULL, 0); self->values[column]= PyLong_FromString(p, NULL, 0);
break; break;
} }
case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_FLOAT:
case MYSQL_TYPE_DOUBLE: case MYSQL_TYPE_DOUBLE:
{ {
double d= atof(data); double d= atof(data);
self->values[column]= PyFloat_FromDouble(d); self->values[column]= PyFloat_FromDouble(d);
@ -525,7 +572,7 @@ field_fetch_fromtext(MrdbCursor *self, char *data, unsigned int column)
Py_INCREF(Py_None); Py_INCREF(Py_None);
self->values[column]= Py_None; self->values[column]= Py_None;
} }
} else } else
{ {
if (check_date(tm.year, tm.month, tm.day) && if (check_date(tm.year, tm.month, tm.day) &&
check_time(&tm)) check_time(&tm))
@ -551,13 +598,13 @@ field_fetch_fromtext(MrdbCursor *self, char *data, unsigned int column)
} }
if (self->fields[column].charsetnr== CHARSET_BINARY) if (self->fields[column].charsetnr== CHARSET_BINARY)
{ {
self->values[column]= self->values[column]=
PyBytes_FromStringAndSize((const char *)data, PyBytes_FromStringAndSize((const char *)data,
(Py_ssize_t)length[column]); (Py_ssize_t)length[column]);
} }
else { else {
self->values[column]= self->values[column]=
PyUnicode_FromStringAndSize((const char *)data, PyUnicode_FromStringAndSize((const char *)data,
(Py_ssize_t)length[column]); (Py_ssize_t)length[column]);
} }
break; break;
@ -612,11 +659,11 @@ field_fetch_fromtext(MrdbCursor *self, char *data, unsigned int column)
if ((val= ma_convert_value(self, type, self->values[column]))) if ((val= ma_convert_value(self, type, self->values[column])))
self->values[column]= val; self->values[column]= val;
} }
} }
/* field_fetch_callback /* field_fetch_callback
This function was previously registered with mysql_stmt_attr_set and This function was previously registered with mysql_stmt_attr_set and
STMT_ATTR_FIELD_FETCH_CALLBACK parameter. Instead of filling a bind STMT_ATTR_FIELD_FETCH_CALLBACK parameter. Instead of filling a bind
buffer MariaDB Connector/C sends raw data in row for the specified column. buffer MariaDB Connector/C sends raw data in row for the specified column.
In case of a NULL value row ptr will be NULL. In case of a NULL value row ptr will be NULL.
@ -659,7 +706,7 @@ field_fetch_callback(void *data, unsigned int column, unsigned char **row)
*row+= 2; *row+= 2;
break; break;
case MYSQL_TYPE_INT24: case MYSQL_TYPE_INT24:
self->values[column]= self->values[column]=
(self->fields[column].flags & UNSIGNED_FLAG) ? (self->fields[column].flags & UNSIGNED_FLAG) ?
PyLong_FromUnsignedLong((unsigned long)uint3korr(*row)) : PyLong_FromUnsignedLong((unsigned long)uint3korr(*row)) :
PyLong_FromLong((long)sint3korr(*row)); PyLong_FromLong((long)sint3korr(*row));
@ -722,7 +769,7 @@ field_fetch_callback(void *data, unsigned int column, unsigned char **row)
} }
if (len == 11) if (len == 11)
second_part= uint4korr(*row + 7); second_part= uint4korr(*row + 7);
self->values[column]= PyDateTime_FromDateAndTime(year, month, self->values[column]= PyDateTime_FromDateAndTime(year, month,
day, hour, minute, second, second_part); day, hour, minute, second, second_part);
*row+= len; *row+= len;
break; break;
@ -782,13 +829,13 @@ field_fetch_callback(void *data, unsigned int column, unsigned char **row)
self->fields[column].max_length= length; self->fields[column].max_length= length;
if (self->fields[column].charsetnr== CHARSET_BINARY) if (self->fields[column].charsetnr== CHARSET_BINARY)
{ {
self->values[column]= self->values[column]=
PyBytes_FromStringAndSize((const char *)*row, PyBytes_FromStringAndSize((const char *)*row,
(Py_ssize_t)length); (Py_ssize_t)length);
} }
else { else {
self->values[column]= self->values[column]=
PyUnicode_FromStringAndSize((const char *)*row, PyUnicode_FromStringAndSize((const char *)*row,
(Py_ssize_t)length); (Py_ssize_t)length);
} }
*row+= length; *row+= length;
@ -831,7 +878,7 @@ field_fetch_callback(void *data, unsigned int column, unsigned char **row)
if (length > self->fields[column].max_length) if (length > self->fields[column].max_length)
self->fields[column].max_length= length; self->fields[column].max_length= length;
} else { } else {
self->values[column]= self->values[column]=
PyUnicode_FromStringAndSize((const char *)*row, PyUnicode_FromStringAndSize((const char *)*row,
(Py_ssize_t)length); (Py_ssize_t)length);
utf8len= (unsigned long)PyUnicode_GET_LENGTH(self->values[column]); utf8len= (unsigned long)PyUnicode_GET_LENGTH(self->values[column]);
@ -886,13 +933,13 @@ end:
return rc; return rc;
} }
/* /*
mariadb_get_column_info mariadb_get_column_info
This function analyzes the Python object and calculates the corresponding This function analyzes the Python object and calculates the corresponding
MYSQL_TYPE, unsigned flag or NULL values and stores the information in MYSQL_TYPE, unsigned flag or NULL values and stores the information in
MrdbParamInfo pointer. MrdbParamInfo pointer.
*/ */
static uint8_t static uint8_t
mariadb_get_column_info(PyObject *obj, MrdbParamInfo *paraminfo) mariadb_get_column_info(PyObject *obj, MrdbParamInfo *paraminfo)
{ {
if (obj == NULL) if (obj == NULL)
@ -943,8 +990,11 @@ mariadb_get_column_info(PyObject *obj, MrdbParamInfo *paraminfo)
paraminfo->type= MYSQL_TYPE_LONG_BLOB; paraminfo->type= MYSQL_TYPE_LONG_BLOB;
return 0; return 0;
} }
else { else if (Py_TYPE(obj)->tp_str) {
/* If Object has string representation, we will use string representation */ /* If Object has string representation, we will use string representation */
paraminfo->type= MYSQL_TYPE_VAR_STRING;
return 0;
} else {
/* no corresponding object, return error */ /* no corresponding object, return error */
return 2; return 2;
} }
@ -965,7 +1015,7 @@ PyObject *ListOrTuple_GetItem(PyObject *obj, Py_ssize_t index)
return NULL; return NULL;
} }
/* /*
mariadb_get_parameter() mariadb_get_parameter()
@brief Returns a bulk parameter which was passed to @brief Returns a bulk parameter which was passed to
@ -979,7 +1029,7 @@ PyObject *ListOrTuple_GetItem(PyObject *obj, Py_ssize_t index)
@return 0 on success, 1 on error @return 0 on success, 1 on error
*/ */
static uint8_t static uint8_t
mariadb_get_parameter(MrdbCursor *self, mariadb_get_parameter(MrdbCursor *self,
uint8_t is_bulk, uint8_t is_bulk,
uint32_t row_nr, uint32_t row_nr,
@ -1060,7 +1110,7 @@ mariadb_get_parameter(MrdbCursor *self,
{ {
param->indicator= STMT_INDICATOR_NULL; param->indicator= STMT_INDICATOR_NULL;
} }
} }
else { else {
param->value= column; param->value= column;
param->indicator= STMT_INDICATOR_NONE; param->indicator= STMT_INDICATOR_NONE;
@ -1070,7 +1120,7 @@ end:
return rc; return rc;
} }
/* /*
mariadb_get_parameter_info mariadb_get_parameter_info
mariadb_get_parameter_info fills the MYSQL_BIND structure mariadb_get_parameter_info fills the MYSQL_BIND structure
with correct field_types for the Python objects. with correct field_types for the Python objects.
@ -1079,7 +1129,7 @@ end:
the field type (e.g. by checking maxbit size for a PyLong). the field type (e.g. by checking maxbit size for a PyLong).
If the types in this column differ we will return an error. If the types in this column differ we will return an error.
*/ */
static uint8_t static uint8_t
mariadb_get_parameter_info(MrdbCursor *self, mariadb_get_parameter_info(MrdbCursor *self,
MYSQL_BIND *param, MYSQL_BIND *param,
uint32_t column_nr) uint32_t column_nr)
@ -1090,10 +1140,10 @@ mariadb_get_parameter_info(MrdbCursor *self,
param->is_unsigned= 0; param->is_unsigned= 0;
paramvalue.indicator= 0; paramvalue.indicator= 0;
uint8_t rc;
if (!self->array_size) if (!self->array_size)
{ {
uint8_t rc;
memset(&pinfo, 0, sizeof(MrdbParamInfo)); memset(&pinfo, 0, sizeof(MrdbParamInfo));
if (mariadb_get_parameter(self, 0, 0, column_nr, &paramvalue)) if (mariadb_get_parameter(self, 0, 0, column_nr, &paramvalue))
return 1; return 1;
@ -1103,15 +1153,14 @@ mariadb_get_parameter_info(MrdbCursor *self,
{ {
mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 0, mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 0,
"Can't retrieve column information for parameter %d", "Can't retrieve column information for parameter %d",
column_nr); column_nr + 1);
} }
if (rc == 2) if (rc == 2)
{ {
mariadb_throw_exception(NULL, Mariadb_NotSupportedError, 0, mariadb_throw_exception(NULL, Mariadb_NotSupportedError, 0,
"Data type '%s' in column %d not supported in MariaDB Connector/Python", "Data type '%s' in column %d not supported in MariaDB Connector/Python",
Py_TYPE(paramvalue.value)->tp_name, column_nr); Py_TYPE(paramvalue.value)->tp_name, column_nr + 1);
} }
return 1; return 1;
} }
param->buffer_type= pinfo.type; param->buffer_type= pinfo.type;
@ -1122,11 +1171,20 @@ mariadb_get_parameter_info(MrdbCursor *self,
if (mariadb_get_parameter(self, 1, i, column_nr, &paramvalue)) if (mariadb_get_parameter(self, 1, i, column_nr, &paramvalue))
return 1; return 1;
memset(&pinfo, 0, sizeof(MrdbParamInfo)); memset(&pinfo, 0, sizeof(MrdbParamInfo));
if (mariadb_get_column_info(paramvalue.value, &pinfo) && !paramvalue.indicator) if ((rc= mariadb_get_column_info(paramvalue.value, &pinfo) && !paramvalue.indicator))
{ {
mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 1, if (rc == 1)
"Invalid parameter type at row %d, column %d", {
i+1, column_nr + 1); mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 0,
"Can't retrieve column information for parameter %d at row %d.",
column_nr + 1, i + 1);
}
if (rc == 2)
{
mariadb_throw_exception(NULL, Mariadb_NotSupportedError, 0,
"Data type '%s' in column %d at row %d not supported in MariaDB Connector/Python",
Py_TYPE(paramvalue.value)->tp_name, column_nr + 1, i+1);
}
return 1; return 1;
} }
@ -1213,7 +1271,7 @@ static Py_ssize_t ListOrTuple_Size(PyObject *obj)
This function validates the specified bulk parameters and This function validates the specified bulk parameters and
translates the field types to MYSQL_TYPE_*. translates the field types to MYSQL_TYPE_*.
*/ */
uint8_t uint8_t
mariadb_check_bulk_parameters(MrdbCursor *self, mariadb_check_bulk_parameters(MrdbCursor *self,
PyObject *data) PyObject *data)
{ {
@ -1222,14 +1280,14 @@ mariadb_check_bulk_parameters(MrdbCursor *self,
if (!CHECK_TYPE((data), &PyList_Type) && if (!CHECK_TYPE((data), &PyList_Type) &&
!CHECK_TYPE(data, &PyTuple_Type)) !CHECK_TYPE(data, &PyTuple_Type))
{ {
mariadb_throw_exception(self->stmt, Mariadb_InterfaceError, 1, mariadb_throw_exception(self->stmt, Mariadb_InterfaceError, 1,
"Data must be passed as sequence (Tuple or List)"); "Data must be passed as sequence (Tuple or List)");
return 1; return 1;
} }
if (!(self->array_size= (uint32_t)ListOrTuple_Size(data))) if (!(self->array_size= (uint32_t)ListOrTuple_Size(data)))
{ {
mariadb_throw_exception(self->stmt, Mariadb_InterfaceError, 1, mariadb_throw_exception(self->stmt, Mariadb_InterfaceError, 1,
"Empty parameter list. At least one row must be specified"); "Empty parameter list. At least one row must be specified");
return 1; return 1;
} }
@ -1256,10 +1314,10 @@ mariadb_check_bulk_parameters(MrdbCursor *self,
} }
if (!self->parseinfo.paramcount || if (!self->parseinfo.paramcount ||
(self->parseinfo.paramstyle != PYFORMAT && (self->parseinfo.paramstyle != PYFORMAT &&
self->parseinfo.paramcount != ListOrTuple_Size(obj))) self->parseinfo.paramcount != ListOrTuple_Size(obj)))
{ {
mariadb_throw_exception(self->stmt, Mariadb_ProgrammingError, 1, mariadb_throw_exception(self->stmt, Mariadb_ProgrammingError, 1,
"Invalid number of parameters in row %d", i+1); "Invalid number of parameters in row %d", i+1);
return 1; return 1;
} }
@ -1275,7 +1333,7 @@ mariadb_check_bulk_parameters(MrdbCursor *self,
goto error; goto error;
} }
if (!(self->value= PyMem_RawCalloc(self->parseinfo.paramcount, if (!(self->value= PyMem_RawCalloc(self->parseinfo.paramcount,
sizeof(MrdbParamValue)))) sizeof(MrdbParamValue))))
{ {
mariadb_throw_exception(NULL, Mariadb_InterfaceError, 0, mariadb_throw_exception(NULL, Mariadb_InterfaceError, 0,
@ -1341,7 +1399,7 @@ error:
return 1; return 1;
} }
/* /*
mariadb_param_to_bind() mariadb_param_to_bind()
@brief Set the current value for the specified bind buffer @brief Set the current value for the specified bind buffer
@ -1352,7 +1410,7 @@ error:
@return 0 on success, otherwise error @return 0 on success, otherwise error
*/ */
static uint8_t static uint8_t
mariadb_param_to_bind(MrdbCursor *self, mariadb_param_to_bind(MrdbCursor *self,
MYSQL_BIND *bind, MYSQL_BIND *bind,
MrdbParamValue *value) MrdbParamValue *value)
@ -1428,27 +1486,41 @@ mariadb_param_to_bind(MrdbCursor *self,
*(double *)value->num= (double)PyFloat_AsDouble(value->value); *(double *)value->num= (double)PyFloat_AsDouble(value->value);
break; break;
case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_LONG_BLOB:
if (value->free_me)
{
MARIADB_FREE_MEM(value->buffer);
value->free_me= 0;
}
if (!strcmp(Py_TYPE(value->value)->tp_name, "array.array") || if (!strcmp(Py_TYPE(value->value)->tp_name, "array.array") ||
!strcmp(Py_TYPE(value->value)->tp_name, "array")) !strcmp(Py_TYPE(value->value)->tp_name, "array"))
{ {
Py_ssize_t size= PySequence_Length(value->value);
PyObject *byte_array= NULL; PyObject *byte_array= NULL;
Py_buffer v;
bind->buffer= NULL; bind->buffer= NULL;
if (PyObject_GetBuffer(value->value, &v, PyBUF_CONTIG_RO) < 0)
if (!size)
goto end; goto end;
if (!PyObject_HasAttrString(value->value, "tobytes")) if (!v.len)
goto end; goto end;
if (!(byte_array= PyObject_CallMethod(value->value, "tobytes", NULL))) bind->buffer_length= (unsigned long)v.len;
goto end; #if PY_BIG_ENDIAN == 0
bind->buffer= (void *)v.buf;
#else
bind->buffer= value->buffer= PyMem_RawCalloc(1, v.len);
if (!bind->buffer)
{
mariadb_throw_exception(NULL, Mariadb_InterfaceError, 0,
"Not enough memory (tried to allocated %lld bytes)", v.len);
return 1;
}
value->free_me= 1;
memcpy(bind->buffer, v.buf, v.len);
bind->buffer= (void *)PyBytes_AS_STRING(byte_array); bind->buffer= ma_byteswap((char *)bind->buffer, v.itemsize, v.len);
bind->buffer_length= (unsigned long)PyBytes_GET_SIZE(byte_array); #endif
PyBuffer_Release(&v);
Py_DECREF(byte_array);
} else { } else {
bind->buffer_length= (unsigned long)PyBytes_GET_SIZE(value->value); bind->buffer_length= (unsigned long)PyBytes_GET_SIZE(value->value);
bind->buffer= (void *) PyBytes_AS_STRING(value->value); bind->buffer= (void *) PyBytes_AS_STRING(value->value);
@ -1464,37 +1536,43 @@ mariadb_param_to_bind(MrdbCursor *self,
mariadb_pydate_to_tm(bind->buffer_type, value->value, &value->tm); mariadb_pydate_to_tm(bind->buffer_type, value->value, &value->tm);
break; break;
case MYSQL_TYPE_NEWDECIMAL: 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: case MYSQL_TYPE_VAR_STRING:
{ {
Py_ssize_t len; Py_ssize_t len;
bind->buffer= (void *)PyUnicode_AsUTF8AndSize(value->value, &len); if (value->free_me)
bind->buffer_length= (unsigned long)len; {
MARIADB_FREE_MEM(value->buffer);
value->free_me= 0;
}
if (CHECK_TYPE(value->value, &PyUnicode_Type)) {
bind->buffer= (void *)PyUnicode_AsUTF8AndSize(value->value, &len);
bind->buffer_length= (unsigned long)len;
} else {
PyObject *obj= PyObject_Str(value->value);
char *p;
if (!obj) {
mariadb_throw_exception(self->stmt, Mariadb_ProgrammingError, 0,
"Python type %s has no string representation",
Py_TYPE(value->value)->tp_name);
return 1;
}
p= (void *)PyUnicode_AsUTF8AndSize(obj, &len);
if (!(bind->buffer= value->buffer= PyMem_RawCalloc(1, len)))
{
mariadb_throw_exception(NULL, Mariadb_InterfaceError, 0,
"Not enough memory (tried to allocated %lld bytes)", len);
return 1;
}
value->free_me= 1;
memcpy(bind->buffer, p, len);
bind->buffer_length= (unsigned long)len;
Py_DECREF(obj);
}
break; break;
} }
case MYSQL_TYPE_NULL: case MYSQL_TYPE_NULL:
@ -1506,7 +1584,7 @@ end:
return rc; return rc;
} }
/* /*
mariadb_param_update() mariadb_param_update()
@brief Callback function which updates the bind structure's buffer and @brief Callback function which updates the bind structure's buffer and
length with data from the specified row number. This callback function length with data from the specified row number. This callback function
@ -1529,7 +1607,7 @@ mariadb_param_update(void *data, MYSQL_BIND *bind, uint32_t row_nr)
for (i=0; i < self->parseinfo.paramcount; i++) for (i=0; i < self->parseinfo.paramcount; i++)
{ {
if (mariadb_get_parameter(self, (self->array_size > 0), if (mariadb_get_parameter(self, (self->array_size > 0),
row_nr, i, &self->value[i])) row_nr, i, &self->value[i]))
{ {
goto end; goto end;

View File

@ -94,7 +94,7 @@ class TestCursor(unittest.TestCase):
cursor.execute("INSERT INTO t_vector VALUES (?,?)", (1, data)) cursor.execute("INSERT INTO t_vector VALUES (?,?)", (1, data))
cursor.execute("SELECT id, v, Vec_ToText(v) FROM t_vector") cursor.execute("SELECT id, v, Vec_ToText(v) FROM t_vector")
row= cursor.fetchone() row= cursor.fetchone()
self.connection.commit()
check_data= [row[1], array.array('f', eval(row[2]))] check_data= [row[1], array.array('f', eval(row[2]))]
cursor.execute("DROP TABLE t_vector") cursor.execute("DROP TABLE t_vector")
@ -613,6 +613,27 @@ class TestCursor(unittest.TestCase):
self.assertEqual(row[0], 2) self.assertEqual(row[0], 2)
del cursor del cursor
def test_conpy298(self):
import uuid, ipaddress
cursor= self.connection.cursor()
cursor.execute("DROP TABLE IF EXISTS t1")
cursor.execute("CREATE TABLE t1 (a inet6, b inet4, c uuid)")
values= (ipaddress.ip_address('::'), ipaddress.ip_address('192.168.0.1'),
uuid.uuid4())
cursor.execute("INSERT INTO t1 VALUES (?, ?, ?)", values)
cursor.execute("SELECT a,b,c FROM t1")
row= cursor.fetchone()
self.assertEqual(row[0], values[0].__str__())
self.assertEqual(row[1], values[1].__str__())
self.assertEqual(row[2], values[2].__str__())
cursor.execute("DROP TABLE t1")
cursor.close()
def test_conpy34(self): def test_conpy34(self):
cursor = self.connection.cursor() cursor = self.connection.cursor()
cursor.execute("CREATE TEMPORARY TABLE t1 (a varchar(20)," cursor.execute("CREATE TEMPORARY TABLE t1 (a varchar(20),"