diff options
Diffstat (limited to 'libmysql/libmysql.c')
-rw-r--r-- | libmysql/libmysql.c | 448 |
1 files changed, 250 insertions, 198 deletions
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 1fa3181b1be..1264f2765ba 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -207,11 +207,7 @@ void STDCALL mysql_server_end() /* If library called my_init(), free memory allocated by it */ if (!org_my_init_done) { - my_end(MY_DONT_FREE_DBUG); -#ifndef THREAD - /* Remove TRACING, if enabled by mysql_debug() */ - DBUG_POP(); -#endif + my_end(0); } else { @@ -230,7 +226,7 @@ void STDCALL mysql_server_end() } static MYSQL_PARAMETERS mysql_internal_parameters= -{&max_allowed_packet, &net_buffer_length}; +{&max_allowed_packet, &net_buffer_length, 0}; MYSQL_PARAMETERS *STDCALL mysql_get_parameters(void) { @@ -356,7 +352,7 @@ my_bool STDCALL mysql_master_send_query(MYSQL *mysql, const char *q, DBUG_RETURN(1); master->reconnect= 1; mysql->last_used_con = master; - DBUG_RETURN(simple_command(master, COM_QUERY, q, length, 1)); + DBUG_RETURN(simple_command(master, COM_QUERY, (const uchar*) q, length, 1)); } @@ -390,7 +386,8 @@ my_bool STDCALL mysql_slave_send_query(MYSQL *mysql, const char *q, 0,0,0,0)) DBUG_RETURN(1); slave_to_use->reconnect= 1; - DBUG_RETURN(simple_command(slave_to_use, COM_QUERY, q, length, 1)); + DBUG_RETURN(simple_command(slave_to_use, COM_QUERY, (const uchar*) q, + length, 1)); } @@ -653,7 +650,7 @@ mysql_connect(MYSQL *mysql,const char *host, if (!(res=mysql_real_connect(mysql,host,user,passwd,NullS,0,NullS,0))) { if (mysql->free_me) - my_free((gptr) mysql,MYF(0)); + my_free((uchar*) mysql,MYF(0)); } mysql->reconnect= 1; DBUG_RETURN(res); @@ -684,11 +681,10 @@ int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd) password in old format. The reply contains scramble_323. */ scramble_323(buff, mysql->scramble, passwd); - if (my_net_write(net, buff, SCRAMBLE_LENGTH_323 + 1) || net_flush(net)) + if (my_net_write(net, (uchar*) buff, SCRAMBLE_LENGTH_323 + 1) || + net_flush(net)) { - net->last_errno= CR_SERVER_LOST; - strmov(net->sqlstate, unknown_sqlstate); - strmov(net->last_error,ER(net->last_errno)); + set_mysql_error(mysql, CR_SERVER_LOST, unknown_sqlstate); return 1; } /* Read what server thinks about out new auth message report */ @@ -698,15 +694,26 @@ int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd) return 0; } - my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, const char *passwd, const char *db) { char buff[USERNAME_LENGTH+SCRAMBLED_PASSWORD_CHAR_LENGTH+NAME_LEN+2]; char *end= buff; int rc; + CHARSET_INFO *saved_cs= mysql->charset; + DBUG_ENTER("mysql_change_user"); + /* Get the connection-default character set. */ + + if (mysql_init_character_set(mysql)) + { + mysql->charset= saved_cs; + DBUG_RETURN(TRUE); + } + + /* Use an empty string instead of NULL. */ + if (!user) user=""; if (!passwd) @@ -735,8 +742,16 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, /* Add database if needed */ end= strmake(end, db ? db : "", NAME_LEN) + 1; + /* Add character set number. */ + + if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION) + { + int2store(end, (ushort) mysql->charset->number); + end+= 2; + } + /* Write authentication package */ - simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (end-buff),1); + simple_command(mysql,COM_CHANGE_USER, (uchar*) buff, (ulong) (end-buff), 1); rc= (*mysql->methods->read_change_user_result)(mysql, buff, passwd); @@ -744,7 +759,7 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, The server will close all statements no matter was the attempt to change user successful or not. */ - mysql_detach_stmt_list(&mysql->stmts); + mysql_detach_stmt_list(&mysql->stmts, "mysql_change_user"); if (rc == 0) { /* Free old connect information */ @@ -757,6 +772,11 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, mysql->passwd=my_strdup(passwd,MYF(MY_WME)); mysql->db= db ? my_strdup(db,MYF(MY_WME)) : 0; } + else + { + mysql->charset= saved_cs; + } + DBUG_RETURN(rc); } @@ -773,7 +793,7 @@ void read_user_name(char *name) strmake(name, str ? str : "UNKNOWN_USER", USERNAME_LENGTH); } -#elif !defined(MSDOS) && ! defined(VMS) && !defined(__WIN__) && !defined(OS2) +#elif !defined(MSDOS) && ! defined(VMS) && !defined(__WIN__) void read_user_name(char *name) { @@ -837,8 +857,7 @@ my_bool handle_local_infile(MYSQL *mysql, const char *net_filename) /* copy filename into local memory and allocate read buffer */ if (!(buf=my_malloc(packet_length, MYF(0)))) { - strmov(net->sqlstate, unknown_sqlstate); - strmov(net->last_error, ER(net->last_errno=CR_OUT_OF_MEMORY)); + set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate); DBUG_RETURN(1); } @@ -846,12 +865,13 @@ my_bool handle_local_infile(MYSQL *mysql, const char *net_filename) if ((*options->local_infile_init)(&li_ptr, net_filename, options->local_infile_userdata)) { - VOID(my_net_write(net,"",0)); /* Server needs one packet */ + VOID(my_net_write(net,(const uchar*) "",0)); /* Server needs one packet */ net_flush(net); strmov(net->sqlstate, unknown_sqlstate); - net->last_errno= (*options->local_infile_error)(li_ptr, - net->last_error, - sizeof(net->last_error)-1); + net->last_errno= + (*options->local_infile_error)(li_ptr, + net->last_error, + sizeof(net->last_error)-1); goto err; } @@ -860,31 +880,28 @@ my_bool handle_local_infile(MYSQL *mysql, const char *net_filename) (*options->local_infile_read)(li_ptr, buf, packet_length)) > 0) { - if (my_net_write(net,buf,readcount)) + if (my_net_write(net, (uchar*) buf, readcount)) { DBUG_PRINT("error", ("Lost connection to MySQL server during LOAD DATA of local file")); - strmov(net->sqlstate, unknown_sqlstate); - net->last_errno=CR_SERVER_LOST; - strmov(net->last_error,ER(net->last_errno)); + set_mysql_error(mysql, CR_SERVER_LOST, unknown_sqlstate); goto err; } } /* Send empty packet to mark end of file */ - if (my_net_write(net,"",0) || net_flush(net)) + if (my_net_write(net, (const uchar*) "", 0) || net_flush(net)) { - strmov(net->sqlstate, unknown_sqlstate); - net->last_errno=CR_SERVER_LOST; - sprintf(net->last_error,ER(net->last_errno),errno); + set_mysql_error(mysql, CR_SERVER_LOST, unknown_sqlstate); goto err; } if (readcount < 0) { - net->last_errno= (*options->local_infile_error)(li_ptr, - net->last_error, - sizeof(net->last_error)-1); + net->last_errno= + (*options->local_infile_error)(li_ptr, + net->last_error, + sizeof(net->last_error)-1); goto err; } @@ -975,7 +992,7 @@ static int default_local_infile_read(void *ptr, char *buf, uint buf_len) int count; default_local_infile_data*data = (default_local_infile_data *) ptr; - if ((count= (int) my_read(data->fd, (byte *) buf, buf_len, MYF(0))) < 0) + if ((count= (int) my_read(data->fd, (uchar *) buf, buf_len, MYF(0))) < 0) { data->error_num= EE_READ; /* the errmsg for not entire file read */ my_snprintf(data->error_msg, sizeof(data->error_msg)-1, @@ -1258,7 +1275,8 @@ mysql_list_fields(MYSQL *mysql, const char *table, const char *wild) end=strmake(strmake(buff, table,128)+1,wild ? wild : "",128); free_old_query(mysql); - if (simple_command(mysql,COM_FIELD_LIST,buff,(ulong) (end-buff),1) || + if (simple_command(mysql, COM_FIELD_LIST, (uchar*) buff, + (ulong) (end-buff), 1) || !(fields= (*mysql->methods->list_fields)(mysql))) DBUG_RETURN(NULL); @@ -1329,7 +1347,7 @@ mysql_shutdown(MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level) uchar level[1]; DBUG_ENTER("mysql_shutdown"); level[0]= (uchar) shutdown_level; - DBUG_RETURN(simple_command(mysql, COM_SHUTDOWN, (char *)level, 1, 0)); + DBUG_RETURN(simple_command(mysql, COM_SHUTDOWN, level, 1, 0)); } @@ -1339,14 +1357,14 @@ mysql_refresh(MYSQL *mysql,uint options) uchar bits[1]; DBUG_ENTER("mysql_refresh"); bits[0]= (uchar) options; - DBUG_RETURN(simple_command(mysql,COM_REFRESH,(char*) bits,1,0)); + DBUG_RETURN(simple_command(mysql, COM_REFRESH, bits, 1, 0)); } int STDCALL mysql_kill(MYSQL *mysql,ulong pid) { - char buff[4]; + uchar buff[4]; DBUG_ENTER("mysql_kill"); int4store(buff,pid); DBUG_RETURN(simple_command(mysql,COM_PROCESS_KILL,buff,sizeof(buff),0)); @@ -1356,7 +1374,7 @@ mysql_kill(MYSQL *mysql,ulong pid) int STDCALL mysql_set_server_option(MYSQL *mysql, enum enum_mysql_set_option option) { - char buff[2]; + uchar buff[2]; DBUG_ENTER("mysql_set_server_option"); int2store(buff, (uint) option); DBUG_RETURN(simple_command(mysql, COM_SET_OPTION, buff, sizeof(buff), 0)); @@ -1376,9 +1394,7 @@ const char *cli_read_statistics(MYSQL *mysql) mysql->net.read_pos[mysql->packet_length]=0; /* End of stat string */ if (!mysql->net.read_pos[0]) { - strmov(mysql->net.sqlstate, unknown_sqlstate); - mysql->net.last_errno=CR_WRONG_HOST_INFO; - strmov(mysql->net.last_error, ER(mysql->net.last_errno)); + set_mysql_error(mysql, CR_WRONG_HOST_INFO, unknown_sqlstate); return mysql->net.last_error; } return (char*) mysql->net.read_pos; @@ -1601,7 +1617,7 @@ mysql_hex_string(char *to, const char *from, ulong length) ulong STDCALL mysql_escape_string(char *to,const char *from,ulong length) { - return escape_string_for_mysql(default_charset_info, to, 0, from, length); + return (uint) escape_string_for_mysql(default_charset_info, to, 0, from, length); } ulong STDCALL @@ -1609,8 +1625,8 @@ mysql_real_escape_string(MYSQL *mysql, char *to,const char *from, ulong length) { if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) - return escape_quotes_for_mysql(mysql->charset, to, 0, from, length); - return escape_string_for_mysql(mysql->charset, to, 0, from, length); + return (uint) escape_quotes_for_mysql(mysql->charset, to, 0, from, length); + return (uint) escape_string_for_mysql(mysql->charset, to, 0, from, length); } void STDCALL @@ -1687,6 +1703,7 @@ static my_bool setup_one_fetch_function(MYSQL_BIND *, MYSQL_FIELD *field); #define RESET_SERVER_SIDE 1 #define RESET_LONG_DATA 2 #define RESET_STORE_RESULT 4 +#define RESET_CLEAR_ERROR 8 static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags); @@ -1751,24 +1768,17 @@ static my_bool my_realloc_str(NET *net, ulong length) if (buf_length + length > net->max_packet) { res= net_realloc(net, buf_length + length); + if (res) + { + strmov(net->sqlstate, unknown_sqlstate); + strmov(net->last_error, ER(net->last_errno)); + } net->write_pos= net->buff+ buf_length; } DBUG_RETURN(res); } -/* Clear possible error statee of struct NET */ - -static void net_clear_error(NET *net) -{ - if (net->last_errno) - { - net->last_errno= 0; - net->last_error[0]= '\0'; - strmov(net->sqlstate, not_error_sqlstate); - } -} - static void stmt_clear_error(MYSQL_STMT *stmt) { if (stmt->last_errno) @@ -1779,18 +1789,21 @@ static void stmt_clear_error(MYSQL_STMT *stmt) } } -/* +/** Set statement error code, sqlstate, and error message from given errcode and sqlstate. */ -static void set_stmt_error(MYSQL_STMT * stmt, int errcode, - const char *sqlstate) +void set_stmt_error(MYSQL_STMT * stmt, int errcode, + const char *sqlstate, const char *err) { DBUG_ENTER("set_stmt_error"); DBUG_PRINT("enter", ("error: %d '%s'", errcode, ER(errcode))); DBUG_ASSERT(stmt != 0); + if (err == 0) + err= ER(errcode); + stmt->last_errno= errcode; strmov(stmt->last_error, ER(errcode)); strmov(stmt->sqlstate, sqlstate); @@ -1799,21 +1812,26 @@ static void set_stmt_error(MYSQL_STMT * stmt, int errcode, } -/* - Set statement error code, sqlstate, and error message. +/** + Set statement error code, sqlstate, and error message from NET. + + @param stmt a statement handle. Copy the error here. + @param net mysql->net. Source of the error. */ -void set_stmt_errmsg(MYSQL_STMT * stmt, const char *err, int errcode, - const char *sqlstate) +void set_stmt_errmsg(MYSQL_STMT *stmt, NET *net) { DBUG_ENTER("set_stmt_errmsg"); - DBUG_PRINT("enter", ("error: %d/%s '%s'", errcode, sqlstate, err)); + DBUG_PRINT("enter", ("error: %d/%s '%s'", + net->last_errno, + net->sqlstate, + net->last_error)); DBUG_ASSERT(stmt != 0); - stmt->last_errno= errcode; - if (err && err[0]) - strmov(stmt->last_error, err); - strmov(stmt->sqlstate, sqlstate); + stmt->last_errno= net->last_errno; + if (net->last_error && net->last_error[0]) + strmov(stmt->last_error, net->last_error); + strmov(stmt->sqlstate, net->sqlstate); DBUG_VOID_RETURN; } @@ -1988,7 +2006,7 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) if (!mysql) { /* mysql can be reset in mysql_close called from mysql_reconnect */ - set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate); + set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate, NULL); DBUG_RETURN(1); } @@ -2002,7 +2020,7 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE) { /* This is second prepare with another statement */ - char buff[MYSQL_STMT_HEADER]; /* 4 bytes - stmt id */ + uchar buff[MYSQL_STMT_HEADER]; /* 4 bytes - stmt id */ if (reset_stmt_handle(stmt, RESET_LONG_DATA | RESET_STORE_RESULT)) DBUG_RETURN(1); @@ -2026,23 +2044,20 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) stmt->state= MYSQL_STMT_INIT_DONE; if (stmt_command(mysql, COM_STMT_CLOSE, buff, 4, stmt)) { - set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, - mysql->net.sqlstate); + set_stmt_errmsg(stmt, &mysql->net); DBUG_RETURN(1); } } - if (stmt_command(mysql, COM_STMT_PREPARE, query, length, stmt)) + if (stmt_command(mysql, COM_STMT_PREPARE, (const uchar*) query, length, stmt)) { - set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, - mysql->net.sqlstate); + set_stmt_errmsg(stmt, &mysql->net); DBUG_RETURN(1); } if ((*mysql->methods->read_prepare_result)(mysql, stmt)) { - set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, - mysql->net.sqlstate); + set_stmt_errmsg(stmt, &mysql->net); DBUG_RETURN(1); } @@ -2057,7 +2072,7 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) (stmt->param_count + stmt->field_count)))) { - set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate); + set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL); DBUG_RETURN(1); } stmt->bind= stmt->params + stmt->param_count; @@ -2073,7 +2088,7 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) To be removed when all commands will fully support prepared mode. */ -static unsigned int alloc_stmt_fields(MYSQL_STMT *stmt) +static void alloc_stmt_fields(MYSQL_STMT *stmt) { MYSQL_FIELD *fields, *field, *end; MEM_ROOT *alloc= &stmt->mem_root; @@ -2091,7 +2106,10 @@ static unsigned int alloc_stmt_fields(MYSQL_STMT *stmt) !(stmt->bind= (MYSQL_BIND *) alloc_root(alloc, sizeof(MYSQL_BIND) * stmt->field_count))) - return 0; + { + set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL); + return; + } for (fields= mysql->fields, end= fields+stmt->field_count, field= stmt->fields; @@ -2110,13 +2128,15 @@ static unsigned int alloc_stmt_fields(MYSQL_STMT *stmt) field->def = fields->def ? strdup_root(alloc,fields->def): 0; field->max_length= 0; } - return stmt->field_count; } -/* +/** Update result set columns metadata if it was sent again in reply to COM_STMT_EXECUTE. + + @note If the new field count is different from the original one, + an error is set and no update is performed. */ static void update_stmt_fields(MYSQL_STMT *stmt) @@ -2126,7 +2146,22 @@ static void update_stmt_fields(MYSQL_STMT *stmt) MYSQL_FIELD *stmt_field= stmt->fields; MYSQL_BIND *my_bind= stmt->bind_result_done ? stmt->bind : 0; - DBUG_ASSERT(stmt->field_count == stmt->mysql->field_count); + if (stmt->field_count != stmt->mysql->field_count) + { + /* + The tables used in the statement were altered, + and the query now returns a different number of columns. + There is no way to continue without reallocating the bind + array: + - if the number of columns increased, mysql_stmt_fetch() + will write beyond allocated memory + - if the number of columns decreased, some user-bound + buffers will be left unassigned without user knowing + that. + */ + set_stmt_error(stmt, CR_NEW_STMT_METADATA, unknown_sqlstate, NULL); + return; + } for (; field < field_end; ++field, ++stmt_field) { @@ -2187,7 +2222,7 @@ mysql_stmt_result_metadata(MYSQL_STMT *stmt) if (!(result=(MYSQL_RES*) my_malloc(sizeof(*result), MYF(MY_WME | MY_ZEROFILL)))) { - set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate); + set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL); DBUG_RETURN(0); } @@ -2368,9 +2403,9 @@ static void store_param_str(NET *net, MYSQL_BIND *param) { /* param->length is always set in mysql_stmt_bind_param */ ulong length= *param->length; - char *to= (char *) net_store_length((char *) net->write_pos, length); + uchar *to= net_store_length(net->write_pos, length); memcpy(to, param->buffer, length); - net->write_pos= (uchar*) to+length; + net->write_pos= to+length; } @@ -2420,7 +2455,7 @@ static my_bool store_param(MYSQL_STMT *stmt, MYSQL_BIND *param) */ if ((my_realloc_str(net, *param->length))) { - set_stmt_error(stmt, net->last_errno, unknown_sqlstate); + set_stmt_errmsg(stmt, net); DBUG_RETURN(1); } (*param->store_param_func)(net, param); @@ -2438,12 +2473,11 @@ static my_bool execute(MYSQL_STMT *stmt, char *packet, ulong length) { MYSQL *mysql= stmt->mysql; NET *net= &mysql->net; - char buff[4 /* size of stmt id */ + - 5 /* execution flags */]; + uchar buff[4 /* size of stmt id */ + + 5 /* execution flags */]; my_bool res; - DBUG_ENTER("execute"); - DBUG_DUMP("packet", (uchar*)packet, length); + DBUG_DUMP("packet", (uchar *) packet, length); mysql->last_used_con= mysql; int4store(buff, stmt->stmt_id); /* Send stmt id to server */ @@ -2451,14 +2485,14 @@ static my_bool execute(MYSQL_STMT *stmt, char *packet, ulong length) int4store(buff+5, 1); /* iteration count */ res= test(cli_advanced_command(mysql, COM_STMT_EXECUTE, buff, sizeof(buff), - packet, length, 1, stmt) || + (uchar*) packet, length, 1, stmt) || (*mysql->methods->read_query_result)(mysql)); stmt->affected_rows= mysql->affected_rows; stmt->server_status= mysql->server_status; stmt->insert_id= mysql->insert_id; if (res) { - set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate); + set_stmt_errmsg(stmt, net); DBUG_RETURN(1); } DBUG_RETURN(0); @@ -2481,22 +2515,22 @@ int cli_stmt_execute(MYSQL_STMT *stmt) if (!stmt->bind_param_done) { - set_stmt_error(stmt, CR_PARAMS_NOT_BOUND, unknown_sqlstate); + set_stmt_error(stmt, CR_PARAMS_NOT_BOUND, unknown_sqlstate, NULL); DBUG_RETURN(1); } if (mysql->status != MYSQL_STATUS_READY || mysql->server_status & SERVER_MORE_RESULTS_EXISTS) { - set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); + set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate, NULL); DBUG_RETURN(1); } - net_clear(net); /* Sets net->write_pos */ + net_clear(net, 1); /* Sets net->write_pos */ /* Reserve place for null-marker bytes */ null_count= (stmt->param_count+7) /8; if (my_realloc_str(net, null_count + 1)) { - set_stmt_error(stmt, net->last_errno, unknown_sqlstate); + set_stmt_errmsg(stmt, net); DBUG_RETURN(1); } bzero((char*) net->write_pos, null_count); @@ -2509,7 +2543,7 @@ int cli_stmt_execute(MYSQL_STMT *stmt) { if (my_realloc_str(net, 2 * stmt->param_count)) { - set_stmt_error(stmt, net->last_errno, unknown_sqlstate); + set_stmt_errmsg(stmt, net); DBUG_RETURN(1); } /* @@ -2530,9 +2564,9 @@ int cli_stmt_execute(MYSQL_STMT *stmt) } length= (ulong) (net->write_pos - net->buff); /* TODO: Look into avoding the following memdup */ - if (!(param_data= my_memdup((const char*) net->buff, length, MYF(0)))) + if (!(param_data= my_memdup(net->buff, length, MYF(0)))) { - set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate); + set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL); DBUG_RETURN(1); } result= execute(stmt, param_data, length); @@ -2596,20 +2630,19 @@ static int stmt_read_row_unbuffered(MYSQL_STMT *stmt, unsigned char **row) */ if (!mysql) { - set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate); + set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate, NULL); return 1; } if (mysql->status != MYSQL_STATUS_GET_RESULT) { set_stmt_error(stmt, stmt->unbuffered_fetch_cancelled ? CR_FETCH_CANCELED : CR_COMMANDS_OUT_OF_SYNC, - unknown_sqlstate); + unknown_sqlstate, NULL); goto error; } if ((*mysql->methods->unbuffered_fetch)(mysql, (char**) row)) { - set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, - mysql->net.sqlstate); + set_stmt_errmsg(stmt, &mysql->net); /* If there was an error, there are no more pending rows: reset statement status to not hang up in following @@ -2657,8 +2690,8 @@ stmt_read_row_from_cursor(MYSQL_STMT *stmt, unsigned char **row) MYSQL *mysql= stmt->mysql; NET *net= &mysql->net; MYSQL_DATA *result= &stmt->result; - char buff[4 /* statement id */ + - 4 /* number of rows to fetch */]; + uchar buff[4 /* statement id */ + + 4 /* number of rows to fetch */]; free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); result->data= NULL; @@ -2667,10 +2700,10 @@ stmt_read_row_from_cursor(MYSQL_STMT *stmt, unsigned char **row) int4store(buff, stmt->stmt_id); int4store(buff + 4, stmt->prefetch_rows); /* number of rows to fetch */ if ((*mysql->methods->advanced_command)(mysql, COM_STMT_FETCH, - buff, sizeof(buff), NullS, 0, + buff, sizeof(buff), (uchar*) 0, 0, 1, stmt)) { - set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate); + set_stmt_errmsg(stmt, net); return 1; } if ((*mysql->methods->read_rows_from_cursor)(stmt)) @@ -2701,7 +2734,7 @@ static int stmt_read_row_no_result_set(MYSQL_STMT *stmt __attribute__((unused)), unsigned char **row __attribute__((unused))) { - set_stmt_error(stmt, CR_NO_RESULT_SET, unknown_sqlstate); + set_stmt_error(stmt, CR_NO_RESULT_SET, unknown_sqlstate, NULL); return 1; } @@ -2751,7 +2784,7 @@ my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, } return FALSE; err_not_implemented: - set_stmt_error(stmt, CR_NOT_IMPLEMENTED, unknown_sqlstate); + set_stmt_error(stmt, CR_NOT_IMPLEMENTED, unknown_sqlstate, NULL); return TRUE; } @@ -2762,11 +2795,11 @@ my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, { switch (attr_type) { case STMT_ATTR_UPDATE_MAX_LENGTH: - *(unsigned long *) value= stmt->update_max_length; + *(my_bool*) value= stmt->update_max_length; break; case STMT_ATTR_CURSOR_TYPE: *(ulong*) value= stmt->flags; - break; + break; case STMT_ATTR_PREFETCH_ROWS: *(ulong*) value= stmt->prefetch_rows; break; @@ -2777,6 +2810,50 @@ my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, } +/** + Update statement result set metadata from with the new field + information sent during statement execute. + + @pre mysql->field_count is not zero + + @retval TRUE if error: out of memory or the new + result set has a different number of columns + @retval FALSE success +*/ + +static void reinit_result_set_metadata(MYSQL_STMT *stmt) +{ + /* Server has sent result set metadata */ + if (stmt->field_count == 0) + { + /* + This is 'SHOW'/'EXPLAIN'-like query. Current implementation of + prepared statements can't send result set metadata for these queries + on prepare stage. Read it now. + */ + alloc_stmt_fields(stmt); + } + else + { + /* + Update result set metadata if it for some reason changed between + prepare and execute, i.e.: + - in case of 'SELECT ?' we don't know column type unless data was + supplied to mysql_stmt_execute, so updated column type is sent + now. + - if data dictionary changed between prepare and execute, for + example a table used in the query was altered. + Note, that now (4.1.3) we always send metadata in reply to + COM_STMT_EXECUTE (even if it is not necessary), so either this or + previous branch always works. + TODO: send metadata only when it's really necessary and add a warning + 'Metadata changed' when it's sent twice. + */ + update_stmt_fields(stmt); + } +} + + /* Send placeholders data to server (if there are placeholders) and execute prepared statement. @@ -2828,11 +2905,11 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) if (!mysql) { - set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate); + /* Error is already set in mysql_detatch_stmt_list */ DBUG_RETURN(1); } - if (reset_stmt_handle(stmt, RESET_STORE_RESULT)) + if (reset_stmt_handle(stmt, RESET_STORE_RESULT | RESET_CLEAR_ERROR)) DBUG_RETURN(1); /* No need to check for stmt->state: if the statement wasn't @@ -2840,40 +2917,10 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) */ if (mysql->methods->stmt_execute(stmt)) DBUG_RETURN(1); - if (mysql->field_count) - { - /* Server has sent result set metadata */ - if (stmt->field_count == 0) - { - /* - This is 'SHOW'/'EXPLAIN'-like query. Current implementation of - prepared statements can't send result set metadata for these queries - on prepare stage. Read it now. - */ - alloc_stmt_fields(stmt); - } - else - { - /* - Update result set metadata if it for some reason changed between - prepare and execute, i.e.: - - in case of 'SELECT ?' we don't know column type unless data was - supplied to mysql_stmt_execute, so updated column type is sent - now. - - if data dictionary changed between prepare and execute, for - example a table used in the query was altered. - Note, that now (4.1.3) we always send metadata in reply to - COM_STMT_EXECUTE (even if it is not necessary), so either this or - previous branch always works. - TODO: send metadata only when it's really necessary and add a warning - 'Metadata changed' when it's sent twice. - */ - update_stmt_fields(stmt); - } - } stmt->state= MYSQL_STMT_EXECUTE_DONE; - if (stmt->field_count) + if (mysql->field_count) { + reinit_result_set_metadata(stmt); if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS) { mysql->status= MYSQL_STATUS_READY; @@ -2888,7 +2935,7 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) network or b) is more efficient if all (few) result set rows are precached on client and server's resources are freed. */ - DBUG_RETURN(mysql_stmt_store_result(stmt)); + mysql_stmt_store_result(stmt); } else { @@ -2897,7 +2944,7 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) stmt->read_row_func= stmt_read_row_unbuffered; } } - DBUG_RETURN(0); + DBUG_RETURN(test(stmt->last_errno)); } @@ -3136,7 +3183,7 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *my_bind) { if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE) { - set_stmt_error(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate); + set_stmt_error(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate, NULL); DBUG_RETURN(1); } DBUG_RETURN(0); @@ -3301,7 +3348,7 @@ mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, */ if (param_number >= stmt->param_count) { - set_stmt_error(stmt, CR_INVALID_PARAMETER_NO, unknown_sqlstate); + set_stmt_error(stmt, CR_INVALID_PARAMETER_NO, unknown_sqlstate, NULL); DBUG_RETURN(1); } @@ -3323,7 +3370,7 @@ mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, { MYSQL *mysql= stmt->mysql; /* Packet header: stmt id (4 bytes), param no (2 bytes) */ - char buff[MYSQL_LONG_DATA_HEADER]; + uchar buff[MYSQL_LONG_DATA_HEADER]; int4store(buff, stmt->stmt_id); int2store(buff + 4, param_number); @@ -3334,11 +3381,10 @@ mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, This is intentional to save bandwidth. */ if ((*mysql->methods->advanced_command)(mysql, COM_STMT_SEND_LONG_DATA, - buff, sizeof(buff), data, + buff, sizeof(buff), (uchar*) data, length, 1, stmt)) { - set_stmt_errmsg(stmt, mysql->net.last_error, - mysql->net.last_errno, mysql->net.sqlstate); + set_stmt_errmsg(stmt, &mysql->net); DBUG_RETURN(1); } } @@ -3374,7 +3420,7 @@ static void read_binary_time(MYSQL_TIME *tm, uchar **pos) if (length) { uchar *to= *pos; - tm->neg= (bool) to[0]; + tm->neg= to[0]; tm->day= (ulong) sint4korr(to+1); tm->hour= (uint) to[5]; @@ -3551,7 +3597,7 @@ static void fetch_string_with_conversion(MYSQL_BIND *param, char *value, */ char *start= value + param->offset; char *end= value + length; - size_t copy_length; + ulong copy_length; if (start < end) { copy_length= end - start; @@ -3662,19 +3708,20 @@ static void fetch_long_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, } default: { - char buff[22]; /* Enough for longlong */ - char *end= longlong10_to_str(value, buff, is_unsigned ? 10: -10); + uchar buff[22]; /* Enough for longlong */ + uchar *end= (uchar*) longlong10_to_str(value, (char*) buff, + is_unsigned ? 10: -10); /* Resort to string conversion which supports all typecodes */ uint length= (uint) (end-buff); if (field->flags & ZEROFILL_FLAG && length < field->length && field->length < 21) { - bmove_upp((char*) buff+field->length,buff+length, length); - bfill((char*) buff, field->length - length,'0'); + bmove_upp(buff+field->length,buff+length, length); + bfill(buff, field->length - length,'0'); length= field->length; } - fetch_string_with_conversion(param, buff, length); + fetch_string_with_conversion(param, (char*) buff, length); break; } } @@ -3806,11 +3853,12 @@ static void fetch_float_with_conversion(MYSQL_BIND *param, MYSQL_FIELD *field, if (field->flags & ZEROFILL_FLAG && length < field->length && field->length < MAX_DOUBLE_STRING_REP_LENGTH - 1) { - bmove_upp((char*) buff + field->length, buff + length, (uint) length); + bmove_upp((uchar*) buff + field->length, (uchar*) buff + length, + length); bfill((char*) buff, field->length - length, '0'); length= field->length; } - fetch_string_with_conversion(param, buff, (uint) length); + fetch_string_with_conversion(param, buff, length); } break; @@ -4202,7 +4250,7 @@ static my_bool is_binary_compatible(enum enum_field_types type1, for (range= range_list; range != range_list_end; ++range) { /* check that both type1 and type2 are in the same range */ - bool type1_found= FALSE, type2_found= FALSE; + my_bool type1_found= FALSE, type2_found= FALSE; for (type= *range; *type != MYSQL_TYPE_NULL; type++) { type1_found|= type1 == *type; @@ -4405,7 +4453,7 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *my_bind) { int errorcode= (int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE ? CR_NO_PREPARE_STMT : CR_NO_STMT_METADATA; - set_stmt_error(stmt, errorcode, unknown_sqlstate); + set_stmt_error(stmt, errorcode, unknown_sqlstate, NULL); DBUG_RETURN(1); } @@ -4514,7 +4562,7 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) } if (!((bit<<=1) & 255)) { - bit= 1; /* To next byte */ + bit= 1; /* To next uchar */ null_ptr++; } } @@ -4585,12 +4633,12 @@ int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *my_bind, if ((int) stmt->state < (int) MYSQL_STMT_FETCH_DONE) { - set_stmt_error(stmt, CR_NO_DATA, unknown_sqlstate); + set_stmt_error(stmt, CR_NO_DATA, unknown_sqlstate, NULL); return 1; } if (column >= stmt->field_count) { - set_stmt_error(stmt, CR_INVALID_PARAMETER_NO, unknown_sqlstate); + set_stmt_error(stmt, CR_INVALID_PARAMETER_NO, unknown_sqlstate, NULL); DBUG_RETURN(1); } @@ -4636,7 +4684,7 @@ int cli_read_binary_rows(MYSQL_STMT *stmt) if (!mysql) { - set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate); + set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate, NULL); DBUG_RETURN(1); } @@ -4651,7 +4699,7 @@ int cli_read_binary_rows(MYSQL_STMT *stmt) if (!(cur= (MYSQL_ROWS*) alloc_root(&result->alloc, sizeof(MYSQL_ROWS) + pkt_len - 1))) { - set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate); + set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL); goto err; } cur->data= (MYSQL_ROW) (cur+1); @@ -4672,7 +4720,7 @@ int cli_read_binary_rows(MYSQL_STMT *stmt) DBUG_RETURN(0); } } - set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate); + set_stmt_errmsg(stmt, net); err: DBUG_RETURN(1); @@ -4715,7 +4763,7 @@ static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data) DBUG_ASSERT(row <= row_end); if (!((bit<<=1) & 255)) { - bit= 1; /* To next byte */ + bit= 1; /* To next uchar */ null_ptr++; } } @@ -4735,7 +4783,7 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) if (!mysql) { /* mysql can be reset in mysql_close called from mysql_reconnect */ - set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate); + set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate, NULL); DBUG_RETURN(1); } @@ -4746,7 +4794,13 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) if ((int) stmt->state < (int) MYSQL_STMT_EXECUTE_DONE) { - set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); + set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate, NULL); + DBUG_RETURN(1); + } + + if (stmt->last_errno) + { + /* An attempt to use an invalid statement handle. */ DBUG_RETURN(1); } @@ -4757,22 +4811,22 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) Server side cursor exist, tell server to start sending the rows */ NET *net= &mysql->net; - char buff[4 /* statement id */ + - 4 /* number of rows to fetch */]; + uchar buff[4 /* statement id */ + + 4 /* number of rows to fetch */]; /* Send row request to the server */ int4store(buff, stmt->stmt_id); int4store(buff + 4, (int)~0); /* number of rows to fetch */ if (cli_advanced_command(mysql, COM_STMT_FETCH, buff, sizeof(buff), - NullS, 0, 1, stmt)) + (uchar*) 0, 0, 1, stmt)) { - set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate); + set_stmt_errmsg(stmt, net); DBUG_RETURN(1); } } else if (mysql->status != MYSQL_STATUS_GET_RESULT) { - set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); + set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate, NULL); DBUG_RETURN(1); } @@ -4948,19 +5002,19 @@ static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags) Reset the server side statement and close the server side cursor if it exists. */ - char buff[MYSQL_STMT_HEADER]; /* packet header: 4 bytes for stmt id */ + uchar buff[MYSQL_STMT_HEADER]; /* packet header: 4 bytes for stmt id */ int4store(buff, stmt->stmt_id); if ((*mysql->methods->advanced_command)(mysql, COM_STMT_RESET, buff, sizeof(buff), 0, 0, 0, stmt)) { - set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, - mysql->net.sqlstate); + set_stmt_errmsg(stmt, &mysql->net); stmt->state= MYSQL_STMT_INIT_DONE; return 1; } - stmt_clear_error(stmt); } } + if (flags & RESET_CLEAR_ERROR) + stmt_clear_error(stmt); stmt->state= MYSQL_STMT_PREPARE_DONE; } return 0; @@ -4971,7 +5025,8 @@ my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt) DBUG_ENTER("mysql_stmt_free_result"); /* Free the client side and close the server side cursor if there is one */ - DBUG_RETURN(reset_stmt_handle(stmt, RESET_LONG_DATA | RESET_STORE_RESULT)); + DBUG_RETURN(reset_stmt_handle(stmt, RESET_LONG_DATA | RESET_STORE_RESULT | + RESET_CLEAR_ERROR)); } /******************************************************************** @@ -5009,7 +5064,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) net_clear_error(&mysql->net); if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE) { - char buff[MYSQL_STMT_HEADER]; /* 4 bytes - stmt id */ + uchar buff[MYSQL_STMT_HEADER]; /* 4 bytes - stmt id */ if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled) mysql->unbuffered_fetch_owner= 0; @@ -5027,13 +5082,12 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) int4store(buff, stmt->stmt_id); if ((rc= stmt_command(mysql, COM_STMT_CLOSE, buff, 4, stmt))) { - set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, - mysql->net.sqlstate); + set_stmt_errmsg(stmt, &mysql->net); } } } - my_free((gptr) stmt, MYF(MY_WME)); + my_free((uchar*) stmt, MYF(MY_WME)); DBUG_RETURN(test(rc)); } @@ -5049,11 +5103,13 @@ my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt) if (!stmt->mysql) { /* mysql can be reset in mysql_close called from mysql_reconnect */ - set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate); + set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate, NULL); DBUG_RETURN(1); } /* Reset the client and server sides of the prepared statement */ - DBUG_RETURN(reset_stmt_handle(stmt, RESET_SERVER_SIDE | RESET_LONG_DATA)); + DBUG_RETURN(reset_stmt_handle(stmt, + RESET_SERVER_SIDE | RESET_LONG_DATA | + RESET_CLEAR_ERROR)); } /* @@ -5153,15 +5209,11 @@ int STDCALL mysql_next_result(MYSQL *mysql) if (mysql->status != MYSQL_STATUS_READY) { - strmov(mysql->net.sqlstate, unknown_sqlstate); - strmov(mysql->net.last_error, - ER(mysql->net.last_errno=CR_COMMANDS_OUT_OF_SYNC)); + set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); DBUG_RETURN(1); } - mysql->net.last_error[0]= 0; - mysql->net.last_errno= 0; - strmov(mysql->net.sqlstate, not_error_sqlstate); + net_clear_error(&mysql->net); mysql->affected_rows= ~(my_ulonglong) 0; if (mysql->last_used_con->server_status & SERVER_MORE_RESULTS_EXISTS) |