mirror of
https://github.com/mariadb-corporation/mariadb-connector-python.git
synced 2025-08-07 11:39:43 +00:00
Fix for CONPY-9 and CONPY-17:
description attribute is now implemented as getter function(). Changes: - for strings length will now report the number of codepoints (instead) of bytes - variable length datatypes will report -1 (packed len < 1). - For floating point values (decimal, float, double) the values for precision and scale will be zero, in case the server sends decimals=31 (which means no precision/scale) was specified for the column.
This commit is contained in:
@ -134,7 +134,6 @@ typedef struct {
|
||||
MYSQL_FIELD *fields;
|
||||
char *statement;
|
||||
unsigned long statement_len;
|
||||
PyObject *description;
|
||||
PyObject **values;
|
||||
PyStructSequence_Desc sequence_desc;
|
||||
PyStructSequence_Field *sequence_fields;
|
||||
|
@ -249,6 +249,8 @@ void field_fetch_callback(void *data, unsigned int column, unsigned char **row)
|
||||
case MYSQL_TYPE_LONG_BLOB:
|
||||
{
|
||||
unsigned long length= mysql_net_field_length(row);
|
||||
if (length > self->fields[column].max_length)
|
||||
self->fields[column].max_length= length;
|
||||
if (self->fields[column].flags & BINARY_FLAG)
|
||||
{
|
||||
if (!(self->values[column]= mariadb_get_pickled(*row, (size_t)length)))
|
||||
@ -271,8 +273,13 @@ void field_fetch_callback(void *data, unsigned int column, unsigned char **row)
|
||||
case MYSQL_TYPE_ENUM:
|
||||
{
|
||||
unsigned long length;
|
||||
Py_ssize_t utf8len;
|
||||
length= mysql_net_field_length(row);
|
||||
|
||||
self->values[column]= PyUnicode_FromStringAndSize((const char *)*row, (Py_ssize_t)length);
|
||||
utf8len= PyUnicode_GET_LENGTH(self->values[column]);
|
||||
if (utf8len > self->fields[column].max_length)
|
||||
self->fields[column].max_length= utf8len;
|
||||
*row+= length;
|
||||
}
|
||||
default:
|
||||
|
@ -62,6 +62,10 @@ static int MrdbCursor_setbuffered(MrdbCursor *self, PyObject *arg);
|
||||
|
||||
static PyGetSetDef MrdbCursor_sets[]=
|
||||
{
|
||||
{"description", (getter)MrdbCursor_description, NULL,
|
||||
"This read-only attribute is a sequence of 8-item sequences. "
|
||||
"Each of these sequences contains information describing one result column",
|
||||
NULL},
|
||||
{"rowcount", (getter)Mariadb_row_count, NULL, "doc", NULL},
|
||||
{"warnings", (getter)MrdbCursor_warnings, NULL,
|
||||
"Number of warnings which were produced from last execute() call", NULL},
|
||||
@ -128,11 +132,6 @@ static struct PyMemberDef MrdbCursor_Members[] =
|
||||
offsetof(MrdbCursor, statement),
|
||||
READONLY,
|
||||
"The last executed statement"},
|
||||
{"description",
|
||||
T_OBJECT,
|
||||
offsetof(MrdbCursor, description),
|
||||
READONLY,
|
||||
"This read-only attribute is a sequence of 8-item sequences. Each of these sequences contains information describing one result column"},
|
||||
{"lastrowid",
|
||||
T_LONG,
|
||||
offsetof(MrdbCursor, lastrowid),
|
||||
@ -339,7 +338,7 @@ void MrdbCursor_clear(MrdbCursor *self)
|
||||
}
|
||||
|
||||
MARIADB_FREE_MEM(self->sequence_fields);
|
||||
MARIADB_FREE_MEM(self->fields);
|
||||
self->fields= NULL;
|
||||
MARIADB_FREE_MEM(self->values);
|
||||
MARIADB_FREE_MEM(self->bind);
|
||||
MARIADB_FREE_MEM(self->statement);
|
||||
@ -506,9 +505,6 @@ PyObject *MrdbCursor_execute(MrdbCursor *self,
|
||||
self->row_number= 0;
|
||||
if (mysql_stmt_field_count(self->stmt))
|
||||
{
|
||||
MYSQL_RES *res= mysql_stmt_result_metadata(self->stmt);
|
||||
MYSQL_FIELD *fields= mysql_fetch_fields(res);
|
||||
|
||||
if (self->is_buffered)
|
||||
{
|
||||
if (mysql_stmt_store_result(self->stmt))
|
||||
@ -519,11 +515,7 @@ PyObject *MrdbCursor_execute(MrdbCursor *self,
|
||||
self->affected_rows= mysql_stmt_num_rows(self->stmt);
|
||||
}
|
||||
|
||||
/* store metadata field information */
|
||||
if (!(self->fields= (MYSQL_FIELD *)PyMem_RawCalloc(mysql_stmt_field_count(self->stmt), sizeof(MYSQL_FIELD))))
|
||||
goto error;
|
||||
memcpy(self->fields, fields, sizeof(MYSQL_FIELD) * mysql_stmt_field_count(self->stmt));
|
||||
mysql_free_result(res);
|
||||
self->fields= mariadb_stmt_fetch_fields(self->stmt);
|
||||
if (self->is_named_tuple) {
|
||||
if (!(self->sequence_fields= (PyStructSequence_Field *)
|
||||
PyMem_RawCalloc(mysql_stmt_field_count(self->stmt) + 1,
|
||||
@ -544,11 +536,6 @@ PyObject *MrdbCursor_execute(MrdbCursor *self,
|
||||
if (!(self->values= (PyObject**)PyMem_RawCalloc(mysql_stmt_field_count(self->stmt), sizeof(PyObject *))))
|
||||
goto error;
|
||||
mysql_stmt_attr_set(self->stmt, STMT_ATTR_CB_RESULT, field_fetch_callback);
|
||||
self->description= MrdbCursor_description(self);
|
||||
}
|
||||
else {
|
||||
Py_INCREF(Py_None);
|
||||
self->description= Py_None;
|
||||
}
|
||||
end:
|
||||
MARIADB_FREE_MEM(self->value);
|
||||
@ -595,26 +582,29 @@ PyObject *MrdbCursor_description(MrdbCursor *self)
|
||||
for (i=0; i < mysql_stmt_field_count(self->stmt); i++)
|
||||
{
|
||||
uint32_t precision= 0;
|
||||
uint32_t display_size= self->fields[i].max_length;
|
||||
MARIADB_CHARSET_INFO *cinfo;
|
||||
cinfo= mariadb_get_charset_by_nr(self->fields[i].charsetnr);
|
||||
|
||||
if (cinfo && cinfo->char_maxlen > 1)
|
||||
display_size /= cinfo->char_maxlen;
|
||||
uint32_t decimals= 0;
|
||||
unsigned long display_length= self->fields[i].max_length;
|
||||
long packed_len= mysql_ps_fetch_functions[self->fields[i].type].pack_len;
|
||||
|
||||
if (self->fields[i].decimals)
|
||||
precision= self->fields[i].max_length - 1;
|
||||
{
|
||||
if (self->fields[i].decimals < 31)
|
||||
{
|
||||
decimals= self->fields[i].decimals;
|
||||
precision= self->fields[i].length;
|
||||
display_length= precision + 1;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject *desc;
|
||||
if (!(desc= Py_BuildValue("(sIIIIIII)",
|
||||
if (!(desc= Py_BuildValue("(sIIiIIOI)",
|
||||
self->fields[i].name,
|
||||
self->fields[i].type,
|
||||
self->fields[i].max_length,
|
||||
self->fields[i].length,
|
||||
display_length,
|
||||
packed_len >= 0 ? packed_len : -1,
|
||||
precision,
|
||||
IS_NUM(self->fields[i].type) ?
|
||||
self->fields[i].decimals: 0,
|
||||
!IS_NOT_NULL(self->fields[i].flags),
|
||||
decimals,
|
||||
PyBool_FromLong(!IS_NOT_NULL(self->fields[i].flags)),
|
||||
self->fields[i].flags)))
|
||||
{
|
||||
Py_XDECREF(obj);
|
||||
@ -1001,6 +991,7 @@ PyObject *MrdbCursor_nextset(MrdbCursor *self)
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
self->fields= mariadb_stmt_fetch_fields(self->stmt);
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
/* }}} */
|
||||
|
@ -368,10 +368,18 @@ class CursorTest(unittest.TestCase):
|
||||
self.assertEqual(row[0],3)
|
||||
cursor.scroll(-2, mode='relative')
|
||||
row= cursor.fetchone()
|
||||
print(row)
|
||||
del cursor
|
||||
|
||||
def test_compy_9(self):
|
||||
cursor=self.connection.cursor(buffered=True)
|
||||
cursor.execute("SELECT 'utf8mb4', 123.5678")
|
||||
print(cursor.description)
|
||||
cursor=self.connection.cursor()
|
||||
cursor.execute("CREATE OR REPLACE TABLE t1 (a varchar(20), b double(6,3), c double)");
|
||||
cursor.execute("INSERT INTO t1 VALUES ('€uro', 123.345, 12345.678)")
|
||||
cursor.execute("SELECT a,b,c FROM t1")
|
||||
cursor.fetchone()
|
||||
d= cursor.description;
|
||||
self.assertEqual(d[0][2], 4); # 4 code points only
|
||||
self.assertEqual(d[0][3], -1); # variable length
|
||||
self.assertEqual(d[1][2], 7); # length=precision + 1
|
||||
self.assertEqual(d[1][4], 6); # precision
|
||||
self.assertEqual(d[1][5], 3); # decimals
|
||||
del cursor
|
||||
|
Reference in New Issue
Block a user