diff --git a/include/ma_common.h b/include/ma_common.h index c5981366..123a5104 100644 --- a/include/ma_common.h +++ b/include/ma_common.h @@ -113,3 +113,9 @@ struct st_mariadb_extension { (a)->options.extension->key : 0 #endif + + +typedef struct st_mariadb_field_extension +{ + MARIADB_CONST_STRING metadata[MARIADB_FIELD_ATTR_LAST+1]; /* 10.5 */ +} MA_FIELD_EXTENSION; diff --git a/include/ma_priv.h b/include/ma_priv.h index 7d300947..cdfebbaf 100644 --- a/include/ma_priv.h +++ b/include/ma_priv.h @@ -24,8 +24,27 @@ void free_rows(MYSQL_DATA *cur); int ma_multi_command(MYSQL *mysql, enum enum_multi_status status); -MYSQL_FIELD * unpack_fields(MYSQL_DATA *data, +MYSQL_FIELD * unpack_fields(const MYSQL *mysql, MYSQL_DATA *data, MA_MEM_ROOT *alloc,uint fields, my_bool default_value); +static inline my_bool ma_has_extended_type_info(const MYSQL *mysql) +{ + return ((mysql->extension->mariadb_server_capabilities << 32) & + MARIADB_CLIENT_EXTENDED_METADATA) != 0; +} + +static inline uint ma_extended_type_info_rows(const MYSQL *mysql) +{ + return ma_has_extended_type_info(mysql) ? 1 : 0; +} + +static inline uint ma_result_set_rows(const MYSQL *mysql) +{ + return ma_has_extended_type_info(mysql) ? 9 : 8; +} + +MA_FIELD_EXTENSION *ma_field_extension_deep_dup(MA_MEM_ROOT *memroot, + const MA_FIELD_EXTENSION *from); + #endif diff --git a/include/mariadb_com.h b/include/mariadb_com.h index 9a5da28d..c63e5b06 100644 --- a/include/mariadb_com.h +++ b/include/mariadb_com.h @@ -170,13 +170,16 @@ enum enum_server_command #define MARIADB_CLIENT_PROGRESS (1ULL << 32) #define MARIADB_CLIENT_COM_MULTI (1ULL << 33) #define MARIADB_CLIENT_STMT_BULK_OPERATIONS (1ULL << 34) +/* support of extended data type/format information, since 10.5.0 */ +#define MARIADB_CLIENT_EXTENDED_METADATA (1ULL << 35) #define IS_MARIADB_EXTENDED_SERVER(mysql)\ !(mysql->server_capabilities & CLIENT_MYSQL) #define MARIADB_CLIENT_SUPPORTED_FLAGS (MARIADB_CLIENT_PROGRESS |\ MARIADB_CLIENT_COM_MULTI |\ - MARIADB_CLIENT_STMT_BULK_OPERATIONS) + MARIADB_CLIENT_STMT_BULK_OPERATIONS|\ + MARIADB_CLIENT_EXTENDED_METADATA) #define CLIENT_SUPPORTED_FLAGS (CLIENT_MYSQL |\ CLIENT_FOUND_ROWS |\ diff --git a/include/mysql.h b/include/mysql.h index dcaf316d..a160de0a 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -61,6 +61,14 @@ typedef int my_socket; #include "ma_list.h" #include "mariadb_ctype.h" + +typedef struct st_ma_const_string +{ + const char *str; + size_t length; +} MARIADB_CONST_STRING; + + #ifndef ST_MA_USED_MEM_DEFINED #define ST_MA_USED_MEM_DEFINED typedef struct st_ma_used_mem { /* struct for once_alloc */ @@ -383,6 +391,20 @@ typedef struct void *extension; } MYSQL_PARAMETERS; + +enum mariadb_field_attr_t +{ + MARIADB_FIELD_ATTR_DATA_TYPE_NAME= 0, + MARIADB_FIELD_ATTR_FORMAT_NAME= 1 +}; + +#define MARIADB_FIELD_ATTR_LAST MARIADB_FIELD_ATTR_FORMAT_NAME + + +int STDCALL mariadb_field_attr(MARIADB_CONST_STRING *attr, + const MYSQL_FIELD *field, + enum mariadb_field_attr_t type); + #ifndef _mysql_time_h_ enum enum_mysql_timestamp_type { diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index 27e036cb..4ec34215 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -29,7 +29,9 @@ SET(MARIADB_LIB_SYMBOLS mariadb_rpl_fetch mariadb_rpl_optionsv mariadb_rpl_get_optionsv - mariadb_free_rpl_event) + mariadb_free_rpl_event + mariadb_field_attr +) IF(WITH_SSL) SET(MARIADB_LIB_SYMBOLS ${MARIADB_LIB_SYMBOLS} mariadb_deinitialize_ssl) ENDIF() diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 896f87de..6feffbeb 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -752,6 +752,79 @@ my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const return 1; } + +static MARIADB_CONST_STRING null_const_string= {0,0}; + +/*************************************************************************** +** Allocate a string copy on memroot +***************************************************************************/ +static MARIADB_CONST_STRING ma_const_string_copy_root(MA_MEM_ROOT *memroot, + const char *str, + size_t length) +{ + MARIADB_CONST_STRING res; + if (!str || !(res.str= ma_memdup_root(memroot, str, length))) + return null_const_string; + res.length= length; + return res; +} + + +/*************************************************************************** +** Allocate and initialize MA_FIELD_EXTENSION +***************************************************************************/ +MA_FIELD_EXTENSION *new_ma_field_extension(MA_MEM_ROOT *memroot) +{ + MA_FIELD_EXTENSION *ext= ma_alloc_root(memroot, sizeof(MA_FIELD_EXTENSION)); + if (ext) + memset((void *) ext, 0, sizeof(*ext)); + return ext; +} + + +/*************************************************************************** +** Populate field extension from a type info packet +***************************************************************************/ + +static void ma_field_extension_init_type_info(MA_MEM_ROOT *memroot, + MA_FIELD_EXTENSION *ext, + const char *ptr, size_t length) +{ + const char *end= ptr + length; + for ( ; ptr < end; ) + { + uint itype= (uchar) *ptr++; + uint len= (uchar) *ptr++; + if (ptr + len > end || len > 127) + break; /*Badly encoded data*/ + if (itype <= 127 && itype <= MARIADB_FIELD_ATTR_LAST) + ext->metadata[itype]= ma_const_string_copy_root(memroot, ptr, len); + ptr+= len; + } +} + + +/*************************************************************************** +** Allocate a field extension deep copy +***************************************************************************/ + +MA_FIELD_EXTENSION *ma_field_extension_deep_dup(MA_MEM_ROOT *memroot, + const MA_FIELD_EXTENSION *from) +{ + MA_FIELD_EXTENSION *ext= new_ma_field_extension(memroot); + uint i; + if (!ext) + return NULL; + for (i= 0; i < MARIADB_FIELD_ATTR_LAST; i++) + { + if (from->metadata[i].str) + ext->metadata[i]= ma_const_string_copy_root(memroot, + from->metadata[i].str, + from->metadata[i].length); + } + return ext; +} + /*************************************************************************** ** Change field rows to field structs ***************************************************************************/ @@ -772,7 +845,8 @@ static size_t rset_field_offsets[]= { }; MYSQL_FIELD * -unpack_fields(MYSQL_DATA *data,MA_MEM_ROOT *alloc,uint fields, +unpack_fields(const MYSQL *mysql, + MYSQL_DATA *data, MA_MEM_ROOT *alloc, uint fields, my_bool default_value) { MYSQL_ROWS *row; @@ -805,7 +879,20 @@ unpack_fields(MYSQL_DATA *data,MA_MEM_ROOT *alloc,uint fields, } } - p= (char *)row->data[6]; + field->extension= NULL; + if (ma_has_extended_type_info(mysql)) + { + if (row->data[i+1] - row->data[i] > 1) + { + size_t len= row->data[i+1] - row->data[i] - 1; + MA_FIELD_EXTENSION *ext= new_ma_field_extension(alloc); + if ((field->extension= ext)) + ma_field_extension_init_type_info(alloc, ext, row->data[i], len); + } + i++; + } + + p= (char *)row->data[i]; /* filler */ field->charsetnr= uint2korr(p); p+= 2; @@ -824,10 +911,11 @@ unpack_fields(MYSQL_DATA *data,MA_MEM_ROOT *alloc,uint fields, if (INTERNAL_NUM_FIELD(field)) field->flags|= NUM_FLAG; + i++; /* This is used by deprecated function mysql_list_fields only, however the reported length is not correct, so we always zero it */ - if (default_value && row->data[7]) - field->def=ma_strdup_root(alloc,(char*) row->data[7]); + if (default_value && row->data[i]) + field->def=ma_strdup_root(alloc,(char*) row->data[i]); else field->def=0; field->def_length= 0; @@ -2213,9 +2301,10 @@ get_info: mysql->server_status|= SERVER_STATUS_IN_TRANS; mysql->extra_info= net_field_length_ll(&pos); /* Maybe number of rec */ - if (!(fields=mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,8))) + if (!(fields=mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0, + ma_result_set_rows(mysql)))) return(-1); - if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc, + if (!(mysql->fields=unpack_fields(mysql, fields, &mysql->field_alloc, (uint) field_count, 1))) return(-1); mysql->status=MYSQL_STATUS_GET_RESULT; @@ -2365,6 +2454,26 @@ mysql_fetch_field(MYSQL_RES *result) return &result->fields[result->current_field++]; } + +/************************************************************************** +** Return mysql field metadata +**************************************************************************/ +int STDCALL +mariadb_field_attr(MARIADB_CONST_STRING *attr, + const MYSQL_FIELD *field, + enum mariadb_field_attr_t type) +{ + MA_FIELD_EXTENSION *ext= (MA_FIELD_EXTENSION*) field->extension; + if (!ext || type > MARIADB_FIELD_ATTR_LAST) + { + *attr= null_const_string; + return 1; + } + *attr= ext->metadata[type]; + return 0; +} + + /************************************************************************** ** Return next row of the query results **************************************************************************/ @@ -2544,7 +2653,8 @@ mysql_list_fields(MYSQL *mysql, const char *table, const char *wild) length= snprintf(buff, 128, "%s%c%s", table, '\0', wild ? wild : ""); if (ma_simple_command(mysql, COM_FIELD_LIST,buff,length,1,0) || - !(query = mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,8))) + !(query = mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0, + ma_result_set_rows(mysql)))) return(NULL); free_old_query(mysql); @@ -2557,7 +2667,7 @@ mysql_list_fields(MYSQL *mysql, const char *table, const char *wild) mysql->fields=0; result->eof=1; result->field_count = (uint) query->rows; - result->fields= unpack_fields(query,&result->field_alloc, + result->fields= unpack_fields(mysql, query, &result->field_alloc, result->field_count, 1); if (result->fields) return(result); @@ -2589,8 +2699,8 @@ mysql_list_processes(MYSQL *mysql) field_count=(uint) net_field_length(&pos); if (!(fields = mysql->methods->db_read_rows(mysql,(MYSQL_FIELD*) 0,7))) return(NULL); - if (!(mysql->fields=unpack_fields(fields, &mysql->field_alloc, - field_count, 0))) + if (!(mysql->fields= unpack_fields(mysql, fields, &mysql->field_alloc, + field_count, 0))) return(NULL); mysql->status=MYSQL_STATUS_GET_RESULT; mysql->field_count=field_count; diff --git a/libmariadb/mariadb_stmt.c b/libmariadb/mariadb_stmt.c index 86e39cae..433e4954 100644 --- a/libmariadb/mariadb_stmt.c +++ b/libmariadb/mariadb_stmt.c @@ -1581,7 +1581,8 @@ my_bool mthd_stmt_get_param_metadata(MYSQL_STMT *stmt) { MYSQL_DATA *result; - if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, 7))) + if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, + 7 + ma_extended_type_info_rows(stmt->mysql)))) return(1); free_rows(result); @@ -1593,9 +1594,10 @@ my_bool mthd_stmt_get_result_metadata(MYSQL_STMT *stmt) MYSQL_DATA *result; MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; - if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, 7))) + if (!(result= stmt->mysql->methods->db_read_rows(stmt->mysql, (MYSQL_FIELD *)0, + 7 + ma_extended_type_info_rows(stmt->mysql)))) return(1); - if (!(stmt->fields= unpack_fields(result,fields_ma_alloc_root, + if (!(stmt->fields= unpack_fields(stmt->mysql, result, fields_ma_alloc_root, stmt->field_count, 0))) return(1); return(0); @@ -1836,6 +1838,11 @@ static int madb_alloc_stmt_fields(MYSQL_STMT *stmt) stmt->fields[i].decimals= stmt->mysql->fields[i].decimals; stmt->fields[i].charsetnr= stmt->mysql->fields[i].charsetnr; stmt->fields[i].max_length= stmt->mysql->fields[i].max_length; + stmt->fields[i].extension= + stmt->mysql->fields[i].extension ? + ma_field_extension_deep_dup(fields_ma_alloc_root, + stmt->mysql->fields[i].extension) : + NULL; } if (!(stmt->bind= (MYSQL_BIND *)ma_alloc_root(fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) { @@ -1912,7 +1919,6 @@ int stmt_read_execute_response(MYSQL_STMT *stmt) /* since all pointers will be incorrect if another statement will be executed, so we need to allocate memory and copy the information */ - stmt->fields[i].extension= 0; /* not in use yet */ if (mysql->fields[i].db) stmt->fields[i].db= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].db); if (mysql->fields[i].table) @@ -1927,6 +1933,11 @@ int stmt_read_execute_response(MYSQL_STMT *stmt) stmt->fields[i].catalog= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].catalog); if (mysql->fields[i].def) stmt->fields[i].def= ma_strdup_root(fields_ma_alloc_root, mysql->fields[i].def); + stmt->fields[i].extension= + mysql->fields[i].extension ? + ma_field_extension_deep_dup(fields_ma_alloc_root, + mysql->fields[i].extension) : + NULL; } }