mirror of
https://github.com/mariadb-corporation/mariadb-connector-python.git
synced 2025-08-04 08:04:45 +00:00
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:
@ -24,6 +24,53 @@
|
||||
#define IS_DECIMAL_TYPE(type) \
|
||||
((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)
|
||||
{
|
||||
PyObject *pyLong= PyObject_GetAttrString(column, "indicator");
|
||||
@ -943,8 +990,11 @@ mariadb_get_column_info(PyObject *obj, MrdbParamInfo *paraminfo)
|
||||
paraminfo->type= MYSQL_TYPE_LONG_BLOB;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
else if (Py_TYPE(obj)->tp_str) {
|
||||
/* If Object has string representation, we will use string representation */
|
||||
paraminfo->type= MYSQL_TYPE_VAR_STRING;
|
||||
return 0;
|
||||
} else {
|
||||
/* no corresponding object, return error */
|
||||
return 2;
|
||||
}
|
||||
@ -1090,10 +1140,10 @@ mariadb_get_parameter_info(MrdbCursor *self,
|
||||
|
||||
param->is_unsigned= 0;
|
||||
paramvalue.indicator= 0;
|
||||
uint8_t rc;
|
||||
|
||||
if (!self->array_size)
|
||||
{
|
||||
uint8_t rc;
|
||||
memset(&pinfo, 0, sizeof(MrdbParamInfo));
|
||||
if (mariadb_get_parameter(self, 0, 0, column_nr, ¶mvalue))
|
||||
return 1;
|
||||
@ -1103,15 +1153,14 @@ mariadb_get_parameter_info(MrdbCursor *self,
|
||||
{
|
||||
mariadb_throw_exception(NULL, Mariadb_ProgrammingError, 0,
|
||||
"Can't retrieve column information for parameter %d",
|
||||
column_nr);
|
||||
column_nr + 1);
|
||||
}
|
||||
if (rc == 2)
|
||||
{
|
||||
mariadb_throw_exception(NULL, Mariadb_NotSupportedError, 0,
|
||||
"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;
|
||||
}
|
||||
param->buffer_type= pinfo.type;
|
||||
@ -1122,11 +1171,20 @@ mariadb_get_parameter_info(MrdbCursor *self,
|
||||
if (mariadb_get_parameter(self, 1, i, column_nr, ¶mvalue))
|
||||
return 1;
|
||||
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,
|
||||
"Invalid parameter type at row %d, column %d",
|
||||
i+1, column_nr + 1);
|
||||
if (rc == 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;
|
||||
}
|
||||
|
||||
@ -1428,27 +1486,41 @@ mariadb_param_to_bind(MrdbCursor *self,
|
||||
*(double *)value->num= (double)PyFloat_AsDouble(value->value);
|
||||
break;
|
||||
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") ||
|
||||
!strcmp(Py_TYPE(value->value)->tp_name, "array"))
|
||||
{
|
||||
Py_ssize_t size= PySequence_Length(value->value);
|
||||
PyObject *byte_array= NULL;
|
||||
Py_buffer v;
|
||||
|
||||
bind->buffer= NULL;
|
||||
|
||||
if (!size)
|
||||
if (PyObject_GetBuffer(value->value, &v, PyBUF_CONTIG_RO) < 0)
|
||||
goto end;
|
||||
|
||||
if (!PyObject_HasAttrString(value->value, "tobytes"))
|
||||
if (!v.len)
|
||||
goto end;
|
||||
|
||||
if (!(byte_array= PyObject_CallMethod(value->value, "tobytes", NULL)))
|
||||
goto end;
|
||||
bind->buffer_length= (unsigned long)v.len;
|
||||
#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_length= (unsigned long)PyBytes_GET_SIZE(byte_array);
|
||||
|
||||
Py_DECREF(byte_array);
|
||||
bind->buffer= ma_byteswap((char *)bind->buffer, v.itemsize, v.len);
|
||||
#endif
|
||||
PyBuffer_Release(&v);
|
||||
} else {
|
||||
bind->buffer_length= (unsigned long)PyBytes_GET_SIZE(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);
|
||||
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;
|
||||
if (value->free_me)
|
||||
{
|
||||
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;
|
||||
}
|
||||
case MYSQL_TYPE_NULL:
|
||||
|
@ -94,7 +94,7 @@ class TestCursor(unittest.TestCase):
|
||||
cursor.execute("INSERT INTO t_vector VALUES (?,?)", (1, data))
|
||||
cursor.execute("SELECT id, v, Vec_ToText(v) FROM t_vector")
|
||||
row= cursor.fetchone()
|
||||
|
||||
self.connection.commit()
|
||||
check_data= [row[1], array.array('f', eval(row[2]))]
|
||||
|
||||
cursor.execute("DROP TABLE t_vector")
|
||||
@ -613,6 +613,27 @@ class TestCursor(unittest.TestCase):
|
||||
self.assertEqual(row[0], 2)
|
||||
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):
|
||||
cursor = self.connection.cursor()
|
||||
cursor.execute("CREATE TEMPORARY TABLE t1 (a varchar(20),"
|
||||
|
Reference in New Issue
Block a user