Skip to content

Commit cfe1d10

Browse files
committed
CONPY-271: Added cursor.metadata property
Similiar to description property, this property returns a dictionary with complete metadata. The dictionary contains the following keys: - catalog: catalog (always 'def') - schema: current schema - field: alias column name or if no alias was specified column name - org_field: original column name - table: alias table name or if no alias was specified table name - org_table: original table name - type: column type - charset: character set (utf8mb4 or binary) - length: The length of the column - max length: The maximum length of the column - decimals: The numer of decimals - flags: Flags (flags are defined in constants.FIELD_FLAG) - ext_type: Extended data type (types are defined in constants.EXT_FIELD_TYPE) This fixes also CONPY-270: Instead of checking BINARY_FLAG we now check character set for binary object types.
1 parent 05f4ced commit cfe1d10

File tree

7 files changed

+256
-34
lines changed

7 files changed

+256
-34
lines changed

include/docs/cursor.h

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,30 @@ PyDoc_STRVAR(
3131
"- field_flags\n"
3232
"- table_name\n"
3333
"- original_column_name\n"
34-
"- original_table_name\n\n"
34+
"- original_table_name\n"
3535
"This attribute will be None for operations that do not return rows or if the cursor has\n"
36-
"not had an operation invoked via the .execute*() method yet."
36+
"not had an operation invoked via the .execute*() method yet.\n\n"
37+
"extended field type information was added in MariaDB Connector/Python 1.1.8. It will be available\n"
38+
"only, if the cursor or connection was created with optional parameter ext_field_type=True.\n"
39+
);
40+
41+
PyDoc_STRVAR(
42+
cursor_metadata__doc__,
43+
"Similiar to description property, this property returns a dictionary with complete metadata.\n\n"
44+
"The dictionary contains the following keys:\n"
45+
"- catalog: catalog (always 'def')\n"
46+
"- schema: current schema\n"
47+
"- field: alias column name or if no alias was specified column name\n"
48+
"- org_field: original column name\n"
49+
"- table: alias table name or if no alias was specified table name\n"
50+
"- org_table: original table name\n"
51+
"- type: column type\n"
52+
"- charset: character set (utf8mb4 or binary)\n"
53+
"- length: The length of the column\n"
54+
"- max length: The maximum length of the column\n"
55+
"- decimals: The numer of decimals\n"
56+
"- flags: Flags (flags are defined in constants.FIELD_FLAG)\n"
57+
"- ext_type: Extended data type (types are defined in constants.EXT_FIELD_TYPE)\n"
3758
);
3859

3960
PyDoc_STRVAR(

include/mariadb_python.h

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,17 @@ enum enum_binary_command {
127127
enum enum_extended_field_type
128128
{
129129
EXT_TYPE_NONE=0,
130-
EXT_TYPE_JSON=1
130+
EXT_TYPE_JSON,
131+
EXT_TYPE_UUID,
132+
EXT_TYPE_INET4,
133+
EXT_TYPE_INET6,
134+
EXT_TYPE_POINT,
135+
EXT_TYPE_MULTIPOINT,
136+
EXT_TYPE_LINESTRING,
137+
EXT_TYPE_MULTILINESTRING,
138+
EXT_TYPE_POLYGON,
139+
EXT_TYPE_MULTIPOLYGON,
140+
EXT_TYPE_GEOMETRYCOLLECTION
131141
};
132142

133143
enum enum_result_format
@@ -162,6 +172,11 @@ enum enum_paramstyle
162172
PYFORMAT= 3
163173
};
164174

175+
typedef struct st_ext_field_type {
176+
enum enum_extended_field_type ext_type;
177+
MARIADB_CONST_STRING str;
178+
} Mrdb_ExtFieldType;
179+
165180
typedef struct st_parser {
166181
MrdbString statement;
167182
MrdbString *keys;
@@ -332,7 +347,7 @@ mariadb_throw_exception(void *handle,
332347
const char *message,
333348
...);
334349

335-
enum enum_extended_field_type mariadb_extended_field_type(const MYSQL_FIELD *field);
350+
Mrdb_ExtFieldType *mariadb_extended_field_type(const MYSQL_FIELD *field);
336351

337352
PyObject *
338353
MrdbConnection_ping(MrdbConnection *self);

mariadb/constants/EXT_FIELD_TYPE.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""
2+
MariaDB EXT_FIELD_TYPE Constants
3+
4+
These constants represent the extended field types supported by MariaDB.
5+
6+
Extended field types are defined in module *mariadb.constants.EXT_FIELD_TYPE*
7+
"""
8+
9+
NONE =0
10+
JSON = 1
11+
UUID = 2
12+
INET4 = 3
13+
INET6 = 4
14+
POINT = 5
15+
MULTIPOINT = 6
16+
LINESTRING = 7
17+
MULTILINESTRING = 8
18+
POLYGON = 9
19+
MULTIPOLYGON = 10
20+
GEOMETRYCOLLECTION = 11

mariadb/mariadb_codecs.c

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,46 @@ int codecs_datetime_init(void)
4141
return 0;
4242
}
4343

44-
enum enum_extended_field_type mariadb_extended_field_type(const MYSQL_FIELD *field)
44+
Mrdb_ExtFieldType extended_field_types[] = {
45+
{EXT_TYPE_JSON, {"json", 4}},
46+
{EXT_TYPE_UUID, {"uuid", 4}},
47+
{EXT_TYPE_INET4, {"inet4", 5}},
48+
{EXT_TYPE_INET6, {"inet6", 5}},
49+
{EXT_TYPE_POINT, {"point", 5}},
50+
{EXT_TYPE_MULTIPOINT, {"multipoint", 10}},
51+
{EXT_TYPE_LINESTRING, {"linestring", 10}},
52+
{EXT_TYPE_MULTILINESTRING, {"multilinestring", 15}},
53+
{EXT_TYPE_POLYGON, {"polygon", 7}},
54+
{EXT_TYPE_MULTIPOLYGON, {"multipolygon", 12}},
55+
{EXT_TYPE_GEOMETRYCOLLECTION, {"geometrycollection", 18}},
56+
{0, {NULL, 0}}
57+
};
58+
59+
Mrdb_ExtFieldType *mariadb_extended_field_type(const MYSQL_FIELD *field)
4560
{
4661
#if MARIADB_PACKAGE_VERSION_ID > 30107
47-
MARIADB_CONST_STRING str;
62+
MARIADB_CONST_STRING str= {0,0};
4863

49-
if (!mariadb_field_attr(&str, field, MARIADB_FIELD_ATTR_FORMAT_NAME))
50-
{
51-
if (str.length == 4 && !strncmp(str.str, "json", 4))
52-
return EXT_TYPE_JSON;
64+
/* Extended field type has either format name or type name */
65+
if (mariadb_field_attr(&str, field, MARIADB_FIELD_ATTR_FORMAT_NAME))
66+
return NULL;
67+
if (!str.length && mariadb_field_attr(&str, field, MARIADB_FIELD_ATTR_DATA_TYPE_NAME))
68+
return NULL;
69+
if (str.length) {
70+
uint8_t i= 0;
71+
72+
while (extended_field_types[i].ext_type)
73+
{
74+
if (extended_field_types[i].str.length == str.length &&
75+
!strncmp(str.str, extended_field_types[i].str.str, str.length))
76+
{
77+
return &extended_field_types[i];
78+
}
79+
i++;
80+
}
5381
}
5482
#endif
55-
return EXT_TYPE_NONE;
83+
return NULL;
5684
}
5785

5886
/*
@@ -430,9 +458,11 @@ field_fetch_fromtext(MrdbCursor *self, char *data, unsigned int column)
430458
{
431459
MYSQL_TIME tm;
432460
unsigned long *length;
433-
enum enum_extended_field_type ext_type= mariadb_extended_field_type(&self->fields[column]);
461+
Mrdb_ExtFieldType *ext_field_type;
434462
uint16_t type= self->fields[column].type;
435463

464+
ext_field_type= mariadb_extended_field_type(&self->fields[column]);
465+
436466
if (!data)
437467
type= MYSQL_TYPE_NULL;
438468

@@ -519,8 +549,7 @@ field_fetch_fromtext(MrdbCursor *self, char *data, unsigned int column)
519549
{
520550
self->fields[column].max_length= length[column];
521551
}
522-
if (self->fields[column].charsetnr== CHARSET_BINARY &&
523-
ext_type != EXT_TYPE_JSON)
552+
if (self->fields[column].charsetnr== CHARSET_BINARY)
524553
{
525554
self->values[column]=
526555
PyBytes_FromStringAndSize((const char *)data,
@@ -575,7 +604,7 @@ field_fetch_fromtext(MrdbCursor *self, char *data, unsigned int column)
575604
PyObject *val;
576605
enum enum_field_types type;
577606

578-
if (ext_type == EXT_TYPE_JSON)
607+
if (ext_field_type && ext_field_type->ext_type == EXT_TYPE_JSON)
579608
type= MYSQL_TYPE_JSON;
580609
else
581610
type= self->fields[column].type;
@@ -599,7 +628,9 @@ void
599628
field_fetch_callback(void *data, unsigned int column, unsigned char **row)
600629
{
601630
MrdbCursor *self= (MrdbCursor *)data;
602-
enum enum_extended_field_type ext_type= mariadb_extended_field_type(&self->fields[column]);
631+
Mrdb_ExtFieldType *ext_field_type;
632+
633+
ext_field_type= mariadb_extended_field_type(&self->fields[column]);
603634

604635
if (!row)
605636
{
@@ -749,8 +780,7 @@ field_fetch_callback(void *data, unsigned int column, unsigned char **row)
749780
unsigned long length= mysql_net_field_length(row);
750781
if (length > self->fields[column].max_length)
751782
self->fields[column].max_length= length;
752-
if (self->fields[column].charsetnr == CHARSET_BINARY &&
753-
ext_type != EXT_TYPE_JSON)
783+
if (self->fields[column].charsetnr== CHARSET_BINARY)
754784
{
755785
self->values[column]=
756786
PyBytes_FromStringAndSize((const char *)*row,
@@ -793,8 +823,7 @@ field_fetch_callback(void *data, unsigned int column, unsigned char **row)
793823
unsigned long utf8len;
794824
length= mysql_net_field_length(row);
795825

796-
if ((self->fields[column].flags & BINARY_FLAG ||
797-
self->fields[column].charsetnr == CHARSET_BINARY))
826+
if (self->fields[column].charsetnr== CHARSET_BINARY)
798827
{
799828
self->values[column]=
800829
PyBytes_FromStringAndSize((const char *)*row,
@@ -820,7 +849,7 @@ field_fetch_callback(void *data, unsigned int column, unsigned char **row)
820849
PyObject *val;
821850
enum enum_field_types type;
822851

823-
if (ext_type == EXT_TYPE_JSON)
852+
if (ext_field_type && ext_field_type->ext_type == EXT_TYPE_JSON)
824853
type= MYSQL_TYPE_JSON;
825854
else
826855
type= self->fields[column].type;

mariadb/mariadb_cursor.c

Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*****************************************************************************
1+
/*****************************************************************************
22
Copyright (C) 2018-2020 Georg Richter and MariaDB Corporation AB
33
44
This library is free software; you can redistribute it and/or
@@ -106,12 +106,15 @@ static PyObject *Mariadb_row_count(MrdbCursor *self);
106106
static PyObject *Mariadb_row_number(MrdbCursor *self);
107107
static PyObject *MrdbCursor_warnings(MrdbCursor *self);
108108
static PyObject *MrdbCursor_closed(MrdbCursor *self);
109+
static PyObject *MrdbCursor_metadata(MrdbCursor *self);
109110

110111

111112
static PyGetSetDef MrdbCursor_sets[]=
112113
{
113114
{"description", (getter)MrdbCursor_description, NULL,
114115
cursor_description__doc__, NULL},
116+
{"metadata", (getter)MrdbCursor_metadata, NULL,
117+
cursor_metadata__doc__, NULL},
115118
{"rowcount", (getter)Mariadb_row_count, NULL,
116119
NULL, NULL},
117120
{"warnings", (getter)MrdbCursor_warnings, NULL,
@@ -715,6 +718,70 @@ static int Mrdb_execute_direct(MrdbCursor *self,
715718
return rc;
716719
}
717720

721+
/* {{{ MrdbCursor_metadata */
722+
static PyObject *MrdbCursor_metadata(MrdbCursor *self)
723+
{
724+
uint32_t i;
725+
PyObject *dict;
726+
const char *keys[14]= {"catalog", "schema", "field", "org_field", "table",
727+
"org_table", "type", "charset", "length",
728+
"max_length", "decimals", "flags", "ext_type_or_format"};
729+
PyObject *tuple[14]= {0};
730+
Mrdb_ExtFieldType *ext_field_type= NULL;
731+
732+
if (!self->field_count)
733+
Py_RETURN_NONE;
734+
735+
if (PyErr_Occurred())
736+
return NULL;
737+
738+
for (i=0; i < 13; i++)
739+
if (!(tuple[i] = PyTuple_New(self->field_count)))
740+
goto error;
741+
742+
743+
for (i=0; i < self->field_count; i++)
744+
{
745+
PyTuple_SetItem(tuple[0], i, PyUnicode_FromString(self->fields[i].catalog));
746+
PyTuple_SetItem(tuple[1], i, PyUnicode_FromString(self->fields[i].db));
747+
PyTuple_SetItem(tuple[2], i, PyUnicode_FromString(self->fields[i].name));
748+
PyTuple_SetItem(tuple[3], i, PyUnicode_FromString(self->fields[i].org_name));
749+
PyTuple_SetItem(tuple[4], i, PyUnicode_FromString(self->fields[i].table));
750+
PyTuple_SetItem(tuple[5], i, PyUnicode_FromString(self->fields[i].org_table));
751+
PyTuple_SetItem(tuple[6], i, PyLong_FromLong((long)self->fields[i].type));
752+
PyTuple_SetItem(tuple[7], i, PyLong_FromLong((long)self->fields[i].charsetnr));
753+
PyTuple_SetItem(tuple[8], i, PyLong_FromLongLong((long long)self->fields[i].max_length));
754+
PyTuple_SetItem(tuple[9], i, PyLong_FromLongLong((long long)self->fields[i].length));
755+
PyTuple_SetItem(tuple[10], i, PyLong_FromLong((long)self->fields[i].decimals));
756+
PyTuple_SetItem(tuple[11], i, PyLong_FromLong((long)self->fields[i].flags));
757+
758+
if (ext_field_type= mariadb_extended_field_type(&self->fields[i]))
759+
PyTuple_SetItem(tuple[12], i, PyLong_FromLong((long)ext_field_type->ext_type));
760+
else
761+
PyTuple_SetItem(tuple[12], i, PyLong_FromLong((long)EXT_TYPE_NONE));
762+
}
763+
764+
if (!(dict =PyDict_New()))
765+
goto error;
766+
767+
for (i=0; i < 13; i++)
768+
{
769+
if (PyDict_SetItem(dict, PyUnicode_FromString(keys[i]), tuple[i]))
770+
goto error;
771+
Py_DECREF(tuple[i]);
772+
tuple[i]= NULL;
773+
}
774+
return dict;
775+
error:
776+
for (i=0; i < 13; i++)
777+
if (tuple[i])
778+
Py_DECREF(tuple[i]);
779+
if (dict)
780+
Py_DECREF(dict);
781+
return NULL;
782+
}
783+
/* }}}*/
784+
718785
/* {{{ MrdbCursor_description
719786
PEP-249 description method()
720787
@@ -730,7 +797,6 @@ PyObject *MrdbCursor_description(MrdbCursor *self)
730797
if (PyErr_Occurred())
731798
return NULL;
732799

733-
734800
if (self->fields && field_count)
735801
{
736802
uint32_t i;
@@ -742,22 +808,22 @@ PyObject *MrdbCursor_description(MrdbCursor *self)
742808
{
743809
uint32_t precision= 0;
744810
uint32_t decimals= 0;
811+
uint8_t err= 0;
745812
MY_CHARSET_INFO cs;
746813
unsigned long display_length;
747814
long packed_len= 0;
748815
PyObject *desc;
749-
enum enum_extended_field_type ext_type= mariadb_extended_field_type(&self->fields[i]);
816+
Mrdb_ExtFieldType *ext_field_type= mariadb_extended_field_type(&self->fields[i]);
750817

751818
display_length= self->fields[i].max_length > self->fields[i].length ?
752819
self->fields[i].max_length : self->fields[i].length;
753820
mysql_get_character_set_info(self->connection->mysql, &cs);
754821
if (cs.mbmaxlen > 1)
755822
{
756-
packed_len= display_length;
757-
display_length/= cs.mbmaxlen;
758-
}
759-
else {
760-
packed_len= mysql_ps_fetch_functions[self->fields[i].type].pack_len;
823+
packed_len= display_length;
824+
display_length/= cs.mbmaxlen;
825+
} else {
826+
packed_len= mysql_ps_fetch_functions[self->fields[i].type].pack_len;
761827
}
762828

763829
if (self->fields[i].decimals)
@@ -770,9 +836,11 @@ PyObject *MrdbCursor_description(MrdbCursor *self)
770836
}
771837
}
772838

773-
if (ext_type == EXT_TYPE_JSON)
774-
self->fields[i].type= MYSQL_TYPE_JSON;
775-
839+
if (ext_field_type)
840+
{
841+
if (ext_field_type->ext_type == EXT_TYPE_JSON)
842+
self->fields[i].type= MYSQL_TYPE_JSON;
843+
}
776844
if (!(desc= Py_BuildValue("(sIIiIIOIsss)",
777845
self->fields[i].name,
778846
self->fields[i].type,
@@ -788,7 +856,7 @@ PyObject *MrdbCursor_description(MrdbCursor *self)
788856
{
789857
Py_XDECREF(obj);
790858
mariadb_throw_exception(NULL, Mariadb_OperationalError, 0,
791-
"Can't build descriptor record");
859+
"Can't build descriptor record");
792860
return NULL;
793861
}
794862
PyTuple_SetItem(obj, i, desc);

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@
141141
'mariadb.constants.ERR',
142142
'mariadb.constants.FIELD_FLAG',
143143
'mariadb.constants.FIELD_TYPE',
144+
'mariadb.constants.EXT_FIELD_TYPE',
144145
'mariadb.constants.INDICATOR',
145146
'mariadb.constants.INFO',
146147
'mariadb.constants.STATUS',

0 commit comments

Comments
 (0)