diff --git a/include/ma_priv.h b/include/ma_priv.h index 1ab83119..13c979d5 100644 --- a/include/ma_priv.h +++ b/include/ma_priv.h @@ -39,6 +39,13 @@ static inline uint ma_extended_type_info_rows(const MYSQL *mysql) return ma_has_extended_type_info(mysql) ? 1 : 0; } +static inline my_bool ma_supports_cache_metadata(const MYSQL *mysql) +{ + return (mysql->extension->mariadb_server_capabilities & + (MARIADB_CLIENT_CACHE_METADATA >> 32)) != 0 ; +} + + static inline uint ma_result_set_rows(const MYSQL *mysql) { return ma_has_extended_type_info(mysql) ? 9 : 8; @@ -47,4 +54,7 @@ static inline uint ma_result_set_rows(const MYSQL *mysql) MA_FIELD_EXTENSION *ma_field_extension_deep_dup(MA_MEM_ROOT *memroot, const MA_FIELD_EXTENSION *from); +MYSQL_FIELD *ma_duplicate_resultset_metadata(MYSQL_FIELD *fields, size_t count, + MA_MEM_ROOT *memroot); + #endif diff --git a/include/mariadb_com.h b/include/mariadb_com.h index efbe042f..b6aea218 100644 --- a/include/mariadb_com.h +++ b/include/mariadb_com.h @@ -172,13 +172,16 @@ enum enum_server_command #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) +/* Do not resend metadata for prepared statements, since 10.6*/ +#define MARIADB_CLIENT_CACHE_METADATA (1ULL << 36) #define IS_MARIADB_EXTENDED_SERVER(mysql)\ (!(mysql->server_capabilities & CLIENT_MYSQL)) #define MARIADB_CLIENT_SUPPORTED_FLAGS (MARIADB_CLIENT_PROGRESS |\ MARIADB_CLIENT_STMT_BULK_OPERATIONS|\ - MARIADB_CLIENT_EXTENDED_METADATA) + MARIADB_CLIENT_EXTENDED_METADATA|\ + MARIADB_CLIENT_CACHE_METADATA) #define CLIENT_SUPPORTED_FLAGS (CLIENT_MYSQL |\ CLIENT_FOUND_ROWS |\ diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 9c0704d2..6e020ed6 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -2300,12 +2300,84 @@ corrupted: return -1; } + +static int ma_deep_copy_field(const MYSQL_FIELD *src, MYSQL_FIELD *dst, + MA_MEM_ROOT *r) +{ +#define MA_STRDUP(f) \ + do \ + { \ + if (src->f) \ + { \ + if ((dst->f= ma_strdup_root(r, src->f)) == NULL) \ + return -1; \ + } \ + else \ + { \ + dst->f= NULL; \ + } \ + } \ + while (0) + + + MA_STRDUP(catalog); + MA_STRDUP(db); + MA_STRDUP(def); + MA_STRDUP(name); + MA_STRDUP(org_name); + MA_STRDUP(org_table); + MA_STRDUP(table); +#undef MA_STRDUP + + dst->catalog_length= src->catalog_length; + dst->charsetnr= src->charsetnr; + dst->db_length= src->db_length; + dst->decimals= src->decimals; + dst->def_length= src->def_length; + dst->extension= + src->extension + ? ma_field_extension_deep_dup(r, + src->extension) + : NULL; + dst->flags= src->flags; + dst->length= src->length; + dst->max_length = src->max_length; + dst->name_length= src->name_length; + dst->org_name_length= src->org_name_length; + dst->org_table_length= src->org_table_length; + dst->table_length= src->table_length; + dst->type= src->type; + return 0; +} + + +MYSQL_FIELD *ma_duplicate_resultset_metadata(MYSQL_FIELD *fields, size_t count, + MA_MEM_ROOT *memroot) +{ + size_t i; + MYSQL_FIELD *result= + (MYSQL_FIELD *) ma_alloc_root(memroot, sizeof(MYSQL_FIELD) * count); + if (!result) + return NULL; + + for (i= 0; i < count; i++) + { + if (ma_deep_copy_field(&fields[i], &result[i], memroot)) + return NULL; + } + return result; +} + + int mthd_my_read_query_result(MYSQL *mysql) { uchar *pos; ulong field_count; MYSQL_DATA *fields; ulong length; + const uchar *end; + uchar has_metadata; + my_bool can_local_infile= (mysql->options.extension) && (mysql->extension->auto_local_infile != WAIT_FOR_QUERY); if (mysql->options.extension && mysql->extension->auto_local_infile == ACCEPT_FILE_REQUEST) @@ -2318,6 +2390,7 @@ int mthd_my_read_query_result(MYSQL *mysql) free_old_query(mysql); /* Free old result */ get_info: pos=(uchar*) mysql->net.read_pos; + end= pos + length; if ((field_count= net_field_length(&pos)) == 0) return ma_read_ok_packet(mysql, pos, length); if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */ @@ -2328,16 +2401,45 @@ get_info: return(-1); goto get_info; /* Get info packet */ } + + has_metadata= 1; + if (ma_supports_cache_metadata(mysql)) + { + assert(mysql->fields == NULL); + if (pos < end) + { + has_metadata= *pos; + pos++; + } + } + if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT)) 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, - ma_result_set_rows(mysql)))) - return(-1); - if (!(mysql->fields=unpack_fields(mysql, fields, &mysql->field_alloc, - (uint) field_count, 1))) - return(-1); + if (has_metadata) + { + if (!(fields= mysql->methods->db_read_rows(mysql, (MYSQL_FIELD *) 0, + ma_result_set_rows(mysql)))) + return (-1); + if (!(mysql->fields= unpack_fields(mysql, fields, &mysql->field_alloc, + (uint) field_count, 1))) + return (-1); + } + else + { + /* Read EOF, to get the status and warning count. */ + if ((length= ma_net_safe_read(mysql)) == packet_error) + { + return -1; + } + pos= (uchar *) mysql->net.read_pos; + if (length != 5 || pos[0] != 0xfe) + { + return -1; + } + mysql->warning_count= uint2korr(pos + 1); + mysql->server_status= uint2korr(pos + 3); + } mysql->status=MYSQL_STATUS_GET_RESULT; mysql->field_count=field_count; return(0); diff --git a/libmariadb/mariadb_stmt.c b/libmariadb/mariadb_stmt.c index 1cb7cee7..7c4b5f96 100644 --- a/libmariadb/mariadb_stmt.c +++ b/libmariadb/mariadb_stmt.c @@ -56,6 +56,7 @@ #include #include #include "ma_priv.h" +#include #define UPDATE_STMT_ERROR(stmt)\ @@ -1648,7 +1649,8 @@ int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned lon stmt->param_count= 0; stmt->field_count= 0; - stmt->params= 0; + stmt->fields= NULL; + stmt->params= NULL; int4store(stmt_id, stmt->stmt_id); if (mysql->methods->db_command(mysql, COM_STMT_CLOSE, stmt_id, @@ -1803,55 +1805,32 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) static int madb_alloc_stmt_fields(MYSQL_STMT *stmt) { - uint i; MA_MEM_ROOT *fields_ma_alloc_root= &((MADB_STMT_EXTENSION *)stmt->extension)->fields_ma_alloc_root; + MYSQL *mysql= stmt->mysql; + if (!mysql->field_count) + return 0; - if (stmt->mysql->field_count) + stmt->field_count= mysql->field_count; + if (mysql->fields) { + /* Column info was sent by server */ ma_free_root(fields_ma_alloc_root, MYF(0)); - if (!(stmt->fields= (MYSQL_FIELD *)ma_alloc_root(fields_ma_alloc_root, - sizeof(MYSQL_FIELD) * stmt->mysql->field_count))) + if (!(stmt->fields= ma_duplicate_resultset_metadata( + mysql->fields, mysql->field_count, + fields_ma_alloc_root))) { SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); return(1); } - stmt->field_count= stmt->mysql->field_count; - - for (i=0; i < stmt->field_count; i++) - { - if (stmt->mysql->fields[i].db) - stmt->fields[i].db= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].db); - if (stmt->mysql->fields[i].table) - stmt->fields[i].table= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].table); - if (stmt->mysql->fields[i].org_table) - stmt->fields[i].org_table= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].org_table); - if (stmt->mysql->fields[i].name) - stmt->fields[i].name= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].name); - if (stmt->mysql->fields[i].org_name) - stmt->fields[i].org_name= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].org_name); - if (stmt->mysql->fields[i].catalog) - stmt->fields[i].catalog= ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].catalog); - stmt->fields[i].def= stmt->mysql->fields[i].def ? ma_strdup_root(fields_ma_alloc_root, stmt->mysql->fields[i].def) : NULL; - stmt->fields[i].type= stmt->mysql->fields[i].type; - stmt->fields[i].length= stmt->mysql->fields[i].length; - stmt->fields[i].flags= stmt->mysql->fields[i].flags; - 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)))) - { + if (!(stmt->bind= (MYSQL_BIND *) ma_alloc_root( + fields_ma_alloc_root, stmt->field_count * sizeof(MYSQL_BIND)))) + { SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); - return(1); - } - memset(stmt->bind, 0, stmt->field_count * sizeof(MYSQL_BIND)); - stmt->bind_result_done= 0; + return (1); + } } + memset(stmt->bind, 0, stmt->field_count * sizeof(MYSQL_BIND)); + stmt->bind_result_done= 0; return(0); } @@ -1863,11 +1842,36 @@ int stmt_read_execute_response(MYSQL_STMT *stmt) if (!mysql) return(1); - ret= test((mysql->methods->db_read_stmt_result && - mysql->methods->db_read_stmt_result(mysql))); /* if a reconnect occurred, our connection handle is invalid */ if (!stmt->mysql) - return(1); + return (1); + + ret= test((mysql->methods->db_read_stmt_result && + mysql->methods->db_read_stmt_result(mysql))); + + if (!ret && mysql->field_count && !mysql->fields) + { + /* + Column info was not sent by server, copy + from stmt->fields + */ + assert(stmt->fields); + /* + Too bad, C/C resets stmt->field_count to 0 + before reading SP output variables result sets. + */ + if(!stmt->field_count) + stmt->field_count = mysql->field_count; + else + assert(mysql->field_count == stmt->field_count); + mysql->fields= ma_duplicate_resultset_metadata( + stmt->fields, stmt->field_count, &mysql->field_alloc); + if (!mysql->fields) + { + SET_CLIENT_STMT_ERROR(stmt, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + return (1); + } + } /* update affected rows, also if an error occurred */ stmt->upsert_status.affected_rows= stmt->mysql->affected_rows; diff --git a/plugins/auth/my_auth.c b/plugins/auth/my_auth.c index 0435b4f3..c9313c18 100644 --- a/plugins/auth/my_auth.c +++ b/plugins/auth/my_auth.c @@ -267,7 +267,10 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, memset(buff + 9, 0, 32-9); if (!(mysql->server_capabilities & CLIENT_MYSQL)) { - mysql->extension->mariadb_client_flag = MARIADB_CLIENT_SUPPORTED_FLAGS >> 32; + uint server_extended_cap= mysql->extension->mariadb_server_capabilities; + uint client_extended_cap= (uint)(MARIADB_CLIENT_SUPPORTED_FLAGS >> 32); + mysql->extension->mariadb_client_flag= + server_extended_cap & client_extended_cap; int4store(buff + 28, mysql->extension->mariadb_client_flag); } end= buff+32;