diff --git a/include/mariadb_python.h b/include/mariadb_python.h index 0c8dfa6..a8d96ec 100644 --- a/include/mariadb_python.h +++ b/include/mariadb_python.h @@ -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; diff --git a/src/mariadb_codecs.c b/src/mariadb_codecs.c index 4464781..691a7b2 100644 --- a/src/mariadb_codecs.c +++ b/src/mariadb_codecs.c @@ -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: diff --git a/src/mariadb_cursor.c b/src/mariadb_cursor.c index c3065d6..bf136fd 100644 --- a/src/mariadb_cursor.c +++ b/src/mariadb_cursor.c @@ -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; } /* }}} */ diff --git a/test/cursor.py b/test/cursor.py index 73e1ae4..db0b3b9 100644 --- a/test/cursor.py +++ b/test/cursor.py @@ -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