diff options
Diffstat (limited to 'libmysql/libmysql.c')
-rw-r--r-- | libmysql/libmysql.c | 511 |
1 files changed, 348 insertions, 163 deletions
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 3e31f254913..2b97f6b90e4 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -115,7 +115,6 @@ static sig_handler pipe_sig_handler(int sig); static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to, const char *from, ulong length); static my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list); -static unsigned int get_binary_length(uint type); static my_bool org_my_init_done=0; @@ -3952,6 +3951,27 @@ mysql_prepare_result(MYSQL_STMT *stmt) DBUG_RETURN(result); } +/* + Returns parameter columns meta information in the form of + resultset. +*/ + +MYSQL_RES * STDCALL +mysql_param_result(MYSQL_STMT *stmt) +{ + DBUG_ENTER("mysql_param_result"); + + if (!stmt->param_count) + DBUG_RETURN(0); + + /* + TODO: Fix this when server sends the information. + Till then keep a dummy prototype + */ + DBUG_RETURN(0); +} + + /******************************************************************** Prepare-execute, and param handling @@ -4056,9 +4076,74 @@ static void store_param_double(NET *net, MYSQL_BIND *param) net->write_pos+= 8; } +static void store_param_time(NET *net, MYSQL_BIND *param) +{ + MYSQL_TIME *tm= (MYSQL_TIME *) param->buffer; + char buff[15], *pos; + uint length; + + pos= buff+1; + pos[0]= tm->neg ? 1: 0; + int4store(pos+1, tm->day); + pos[5]= (uchar) tm->hour; + pos[6]= (uchar) tm->minute; + pos[7]= (uchar) tm->second; + int4store(pos+8, tm->second_part); + if (tm->second_part) + length= 11; + else if (tm->hour || tm->minute || tm->second || tm->day) + length= 8; + else + length= 0; + buff[0]= (char) length++; + memcpy((char *)net->write_pos, buff, length); + net->write_pos+= length; +} + +static void net_store_datetime(NET *net, MYSQL_TIME *tm) +{ + char buff[12], *pos; + uint length; + + pos= buff+1; + + int2store(pos, tm->year); + pos[2]= (uchar) tm->month; + pos[3]= (uchar) tm->day; + pos[4]= (uchar) tm->hour; + pos[5]= (uchar) tm->minute; + pos[6]= (uchar) tm->second; + int4store(pos+7, tm->second_part); + if (tm->second_part) + length= 11; + else if (tm->hour || tm->minute || tm->second) + length= 7; + else if (tm->year || tm->month || tm->day) + length= 4; + else + length= 0; + buff[0]= (char) length++; + memcpy((char *)net->write_pos, buff, length); + net->write_pos+= length; +} + +static void store_param_date(NET *net, MYSQL_BIND *param) +{ + MYSQL_TIME *tm= (MYSQL_TIME *) param->buffer; + tm->hour= tm->minute= tm->second= 0; + tm->second_part= 0; + net_store_datetime(net, tm); +} + +static void store_param_datetime(NET *net, MYSQL_BIND *param) +{ + MYSQL_TIME *tm= (MYSQL_TIME *) param->buffer; + net_store_datetime(net, tm); +} + static void store_param_str(NET *net, MYSQL_BIND *param) { - ulong length= *param->length; + ulong length= min(*param->length, param->buffer_length); char *to= (char *) net_store_length((char *) net->write_pos, length); memcpy(to, param->buffer, length); net->write_pos= (uchar*) to+length; @@ -4107,10 +4192,13 @@ static my_bool store_param(MYSQL_STMT *stmt, MYSQL_BIND *param) { /* Param->length should ALWAYS point to the correct length for the type - Either to the length pointer given by the user or param->bind_length + Either to the length pointer given by the user or param->buffer_length */ if ((my_realloc_str(net, 9 + *param->length))) + { + set_stmt_error(stmt, CR_OUT_OF_MEMORY); DBUG_RETURN(1); + } (*param->store_param_func)(net, param); } DBUG_RETURN(0); @@ -4277,12 +4365,14 @@ my_bool STDCALL mysql_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind) param++) { param->param_number= count++; + param->long_data_used= 0; + /* - If param->length is not given, change it to point to bind_length. + If param->length is not given, change it to point to buffer_length. This way we can always use *param->length to get the length of data */ if (!param->length) - param->length= ¶m->bind_length; + param->length= ¶m->buffer_length; /* If param->is_null is not set, then the value can never be NULL */ if (!param->is_null) @@ -4295,42 +4385,52 @@ my_bool STDCALL mysql_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind) break; case MYSQL_TYPE_TINY: /* Force param->length as this is fixed for this type */ - param->length= ¶m->bind_length; - param->bind_length= param->buffer_length= 1; + param->length= ¶m->buffer_length; + param->buffer_length= 1; param->store_param_func= store_param_tinyint; break; case MYSQL_TYPE_SHORT: - param->length= ¶m->bind_length; - param->bind_length= param->buffer_length= 2; + param->length= ¶m->buffer_length; + param->buffer_length= 2; param->store_param_func= store_param_short; break; case MYSQL_TYPE_LONG: - param->length= ¶m->bind_length; - param->bind_length= param->buffer_length= 4; + param->length= ¶m->buffer_length; + param->buffer_length= 4; param->store_param_func= store_param_int32; break; case MYSQL_TYPE_LONGLONG: - param->length= ¶m->bind_length; - param->bind_length= param->buffer_length= 8; + param->length= ¶m->buffer_length; + param->buffer_length= 8; param->store_param_func= store_param_int64; break; case MYSQL_TYPE_FLOAT: - param->length= ¶m->bind_length; - param->bind_length= param->buffer_length= 4; + param->length= ¶m->buffer_length; + param->buffer_length= 4; param->store_param_func= store_param_float; break; case MYSQL_TYPE_DOUBLE: - param->length= ¶m->bind_length; - param->bind_length= param->buffer_length= 8; + param->length= ¶m->buffer_length; + param->buffer_length= 8; param->store_param_func= store_param_double; break; + case MYSQL_TYPE_TIME: + /* Buffer length ignored for DATE, TIME and DATETIME */ + param->store_param_func= store_param_time; + break; + case MYSQL_TYPE_DATE: + param->store_param_func= store_param_date; + break; + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + param->store_param_func= store_param_datetime; + break; case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: - param->bind_length= param->buffer_length; param->store_param_func= store_param_str; break; default: @@ -4440,24 +4540,91 @@ mysql_send_long_data(MYSQL_STMT *stmt, uint param_number, 1 Error (Can't alloc net->buffer) ****************************************************************************/ -/* Return the default binary data length for the common types */ -static unsigned int get_binary_length(uint type) +static void set_zero_time(MYSQL_TIME *tm) { - switch(type) { - case MYSQL_TYPE_TINY: - return 1; - case MYSQL_TYPE_SHORT: - case MYSQL_TYPE_YEAR: - return 2; - case MYSQL_TYPE_LONG: - case MYSQL_TYPE_FLOAT: - return 4; - case MYSQL_TYPE_LONGLONG: - case MYSQL_TYPE_DOUBLE: - return 8; - default: + tm->year= tm->month= tm->day= 0; + tm->hour= tm->minute= tm->second= 0; + tm->second_part= 0; + tm->neg= (bool)0; +} + +/* Read TIME from binary packet and return it to MYSQL_TIME */ +static uint read_binary_time(MYSQL_TIME *tm, uchar **pos) +{ + uchar *to; + uint length; + + if (!(length= net_field_length(pos))) + { + set_zero_time(tm); return 0; } + + to= *pos; + tm->second_part= (length > 8 ) ? (ulong) sint4korr(to+7): 0; + + tm->day= (ulong) sint4korr(to+1); + tm->hour= (uint) to[5]; + tm->minute= (uint) to[6]; + tm->second= (uint) to[7]; + + tm->year= tm->month= 0; + tm->neg= (bool)to[0]; + return length; +} + +/* Read DATETIME from binary packet and return it to MYSQL_TIME */ +static uint read_binary_datetime(MYSQL_TIME *tm, uchar **pos) +{ + uchar *to; + uint length; + + if (!(length= net_field_length(pos))) + { + set_zero_time(tm); + return 0; + } + + to= *pos; + tm->second_part= (length > 7 ) ? (ulong) sint4korr(to+7): 0; + + if (length > 4) + { + tm->hour= (uint) to[4]; + tm->minute= (uint) to[5]; + tm->second= (uint) to[6]; + } + else + tm->hour= tm->minute= tm->second= 0; + + tm->year= (uint) sint2korr(to); + tm->month= (uint) to[2]; + tm->day= (uint) to[3]; + tm->neg= 0; + return length; +} + +/* Read DATE from binary packet and return it to MYSQL_TIME */ +static uint read_binary_date(MYSQL_TIME *tm, uchar **pos) +{ + uchar *to; + uint length; + + if (!(length= net_field_length(pos))) + { + set_zero_time(tm); + return 0; + } + + to= *pos; + tm->year = (uint) sint2korr(to); + tm->month= (uint) to[2]; + tm->day= (uint) to[3]; + + tm->hour= tm->minute= tm->second= 0; + tm->second_part= 0; + tm->neg= 0; + return length; } /* Convert Numeric to buffer types */ @@ -4492,13 +4659,14 @@ static void send_data_long(MYSQL_BIND *param, longlong value) } default: { - uint length= sprintf(buffer,"%lld",value); + uint length= (uint)(longlong10_to_str(value,buffer,10)-buffer); *param->length= length; buffer[length]='\0'; } } } + /* Convert Double to buffer types */ static void send_data_double(MYSQL_BIND *param, double value) { @@ -4531,7 +4699,7 @@ static void send_data_double(MYSQL_BIND *param, double value) } default: { - uint length= sprintf(buffer,"%g",value); + uint length= my_sprintf(buffer,(buffer,"%g",value)); *param->length= length; buffer[length]='\0'; } @@ -4586,25 +4754,79 @@ static void send_data_str(MYSQL_BIND *param, char *value, uint length) } default: *param->length= length; + length= min(length, param->buffer_length); memcpy(buffer, value, length); - buffer[length]='\0'; + if (length != param->buffer_length) + buffer[length]='\0'; } } +static void send_data_time(MYSQL_BIND *param, MYSQL_TIME ltime, + uint length) +{ + switch (param->buffer_type) { + + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + { + MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer; + + tm->year= ltime.year; + tm->month= ltime.month; + tm->day= ltime.day; + + tm->hour= ltime.hour; + tm->minute= ltime.minute; + tm->second= ltime.second; + + tm->second_part= ltime.second_part; + tm->neg= ltime.neg; + break; + } + default: + { + char buff[25]; + + if (!length) + ltime.time_type= MYSQL_TIMESTAMP_NONE; + switch (ltime.time_type) { + case MYSQL_TIMESTAMP_DATE: + length= my_sprintf(buff,(buff, "%04d-%02d-%02d", ltime.year, + ltime.month,ltime.day)); + break; + case MYSQL_TIMESTAMP_FULL: + length= my_sprintf(buff,(buff, "%04d-%02d-%02d %02d:%02d:%02d", + ltime.year,ltime.month,ltime.day, + ltime.hour,ltime.minute,ltime.second)); + break; + case MYSQL_TIMESTAMP_TIME: + length= my_sprintf(buff, (buff, "%02d:%02d:%02d", + ltime.hour,ltime.minute,ltime.second)); + break; + default: + length= 0; + buff[0]='\0'; + } + send_data_str(param, (char *)buff, length); + } + } +} + + /* Fetch data to buffers */ -static my_bool fetch_results(MYSQL_STMT *stmt, MYSQL_BIND *param, - uint field_type, uchar **row) +static void fetch_results(MYSQL_BIND *param, uint field_type, uchar **row) { ulong length; - length= (ulong)get_binary_length(field_type); - switch (field_type) { case MYSQL_TYPE_TINY: { uchar value= (uchar) **row; send_data_long(param,(longlong)value); + length= 1; break; } case MYSQL_TYPE_SHORT: @@ -4612,18 +4834,21 @@ static my_bool fetch_results(MYSQL_STMT *stmt, MYSQL_BIND *param, { short value= (short)sint2korr(*row); send_data_long(param,(longlong)value); + length= 2; break; } case MYSQL_TYPE_LONG: { int32 value= (int32)sint4korr(*row); send_data_long(param,(int32)value); + length= 4; break; } case MYSQL_TYPE_LONGLONG: { longlong value= (longlong)sint8korr(*row); send_data_long(param,value); + length= 8; break; } case MYSQL_TYPE_FLOAT: @@ -4631,6 +4856,7 @@ static my_bool fetch_results(MYSQL_STMT *stmt, MYSQL_BIND *param, float value; float4get(value,*row); send_data_double(param,(double)value); + length= 4; break; } case MYSQL_TYPE_DOUBLE: @@ -4638,115 +4864,35 @@ static my_bool fetch_results(MYSQL_STMT *stmt, MYSQL_BIND *param, double value; float8get(value,*row); send_data_double(param,(double)value); + length= 8; break; } case MYSQL_TYPE_DATE: - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: { - uchar month,day; - short year; - int arg_length; - char ts[50],frac[10],time[20],date[20]; - - if (!(length= net_field_length(row))) - { - *param->length= 0; - break; - } - if (param->buffer_type < MYSQL_TYPE_VAR_STRING || - param->buffer_type > MYSQL_TYPE_STRING) - { - /* - Don't allow fetching of date/time/ts to non-string types - TODO: Allow fetching of date or time to long types. - */ - sprintf(stmt->last_error, - ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE), - param->buffer_type, param->param_number); - return 1; - } - - arg_length= 0; - if (length > 7) - { - int sec_part= sint4korr(*row+7); - sprintf(frac,".%04d", sec_part); - arg_length+= 5; - } - if (length == 7) - { - uchar hour, minute, sec; - hour= *(*row+4); - minute= *(*row+5); - sec= *(*row+6); - sprintf((char *)time," %02d:%02d:%02d",hour,minute,sec); - arg_length+= 9; - } - - year= sint2korr(*row); - month= *(*row+2); - day= *(*row+3); - sprintf((char*) date,"%04d-%02d-%02d",year,month,day); - arg_length+= 10; - - if (arg_length != 19) - time[0]='\0'; - if (arg_length != 24) - frac[0]='\0'; - - strxmov(ts,date,time,frac,NullS); - send_data_str(param,ts,arg_length); + MYSQL_TIME tm; + + length= read_binary_date(&tm,row); + tm.time_type= MYSQL_TIMESTAMP_DATE; + send_data_time(param, tm, length); break; } case MYSQL_TYPE_TIME: { - int day, arg_length; - uchar hour, minute, sec; - char ts[255], frac[20], time[20]; - const char *sign= ""; - - if (!(length= net_field_length(row))) - { - *param->length= 0; - break; - } - if (param->buffer_type < MYSQL_TYPE_VAR_STRING || - param->buffer_type > MYSQL_TYPE_STRING) - { - /* - Don't allow fetching of date/time/ts to non-string types - - TODO: Allow fetching of time to long types without - any conversion. - */ - sprintf(stmt->last_error, - ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE), - param->buffer_type, param->param_number); - return 1; - } - arg_length= 0; - if (length > 8) - { - int sec_part= sint4korr(*row+8); - sprintf(frac,".%04d", sec_part); - arg_length+= 5; - } - - if (**row) - sign="-"; - - day= sint4korr(*row); /* TODO: how to handle this case */ - hour= *(*row+5); - minute= *(*row+6); - sec= *(*row+7); - arg_length+= sprintf((char *)time,"%s%02d:%02d:%02d",sign,hour,minute,sec); - - if (arg_length <= 9) - frac[0]='\0'; - - strxmov(ts,time,frac,NullS); - send_data_str(param,ts,arg_length); + MYSQL_TIME tm; + + length= read_binary_time(&tm, row); + tm.time_type= MYSQL_TIMESTAMP_TIME; + send_data_time(param, tm, length); + break; + } + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + { + MYSQL_TIME tm; + + length= read_binary_datetime(&tm, row); + tm.time_type= MYSQL_TIMESTAMP_FULL; + send_data_time(param, tm, length); break; } default: @@ -4755,7 +4901,6 @@ static my_bool fetch_results(MYSQL_STMT *stmt, MYSQL_BIND *param, break; } *row+= length; - return 0; } static void fetch_result_tinyint(MYSQL_BIND *param, uchar **row) @@ -4801,12 +4946,33 @@ static void fetch_result_double(MYSQL_BIND *param, uchar **row) *row+= 8; } +static void fetch_result_time(MYSQL_BIND *param, uchar **row) +{ + MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer; + *row+= read_binary_time(tm, row); +} + +static void fetch_result_date(MYSQL_BIND *param, uchar **row) +{ + MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer; + *row+= read_binary_date(tm, row); +} + +static void fetch_result_datetime(MYSQL_BIND *param, uchar **row) +{ + MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer; + *row+= read_binary_datetime(tm, row); +} + static void fetch_result_str(MYSQL_BIND *param, uchar **row) { ulong length= net_field_length(row); - memcpy(param->buffer, (char *)*row, length); - *(param->buffer+length)= '\0'; - *param->length= length; + ulong copy_length= min(length, param->buffer_length); + memcpy(param->buffer, (char *)*row, copy_length); + /* Add an end null if there is room in the buffer */ + if (copy_length != param->buffer_length) + *(param->buffer+copy_length)= '\0'; + *param->length= length; // return total length *row+= length; } @@ -4846,25 +5012,47 @@ my_bool STDCALL mysql_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) if (!param->is_null) param->is_null= &int_is_null_dummy; + if (!param->length) + param->length= ¶m->buffer_length; + /* Setup data copy functions for the different supported types */ switch (param->buffer_type) { case MYSQL_TYPE_TINY: param->fetch_result= fetch_result_tinyint; + *param->length= 1; break; case MYSQL_TYPE_SHORT: param->fetch_result= fetch_result_short; + *param->length= 2; break; case MYSQL_TYPE_LONG: param->fetch_result= fetch_result_int32; + *param->length= 4; break; case MYSQL_TYPE_LONGLONG: param->fetch_result= fetch_result_int64; + *param->length= 8; break; case MYSQL_TYPE_FLOAT: param->fetch_result= fetch_result_float; + *param->length= 4; break; case MYSQL_TYPE_DOUBLE: param->fetch_result= fetch_result_double; + *param->length= 8; + break; + case MYSQL_TYPE_TIME: + param->fetch_result= fetch_result_time; + *param->length= sizeof(MYSQL_TIME); + break; + case MYSQL_TYPE_DATE: + param->fetch_result= fetch_result_date; + *param->length= sizeof(MYSQL_TIME); + break; + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + param->fetch_result= fetch_result_datetime; + *param->length= sizeof(MYSQL_TIME); break; case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: @@ -4881,9 +5069,6 @@ my_bool STDCALL mysql_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) param->buffer_type, param->param_number); DBUG_RETURN(1); } - if (!param->length) - param->length= ¶m->bind_length; - *param->length= (long)get_binary_length(param->buffer_type); } stmt->res_buffers= 1; DBUG_RETURN(0); @@ -4920,8 +5105,8 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) *bind->is_null= 0; if (field->type == bind->buffer_type) (*bind->fetch_result)(bind, &row); - else if (fetch_results(stmt, bind, field->type, &row)) - return 1; + else + fetch_results(bind, field->type, &row); } if (! ((bit<<=1) & 255)) { @@ -5052,7 +5237,7 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) { MYSQL *mysql= stmt->mysql; MYSQL_RES *result; - DBUG_ENTER("mysql_stmt_tore_result"); + DBUG_ENTER("mysql_stmt_store_result"); mysql= mysql->last_used_con; @@ -5060,9 +5245,8 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) DBUG_RETURN(0); if (mysql->status != MYSQL_STATUS_GET_RESULT) { - strmov(mysql->net.last_error, - ER(mysql->net.last_errno= CR_COMMANDS_OUT_OF_SYNC)); - DBUG_RETURN(0); + set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC); + DBUG_RETURN(1); } mysql->status= MYSQL_STATUS_READY; /* server is ready */ if (!(result= (MYSQL_RES*) my_malloc((uint) (sizeof(MYSQL_RES)+ @@ -5070,8 +5254,7 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) stmt->field_count), MYF(MY_WME | MY_ZEROFILL)))) { - mysql->net.last_errno= CR_OUT_OF_MEMORY; - strmov(mysql->net.last_error, ER(mysql->net.last_errno)); + set_stmt_error(stmt, CR_OUT_OF_MEMORY); DBUG_RETURN(1); } stmt->result_buffered= 1; @@ -5106,7 +5289,7 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) */ static my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list) { - my_bool error=0; + my_bool error= 0; DBUG_ENTER("mysql_stmt_close"); DBUG_ASSERT(stmt != 0); @@ -5116,12 +5299,14 @@ static my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_list) int4store(buff, stmt->stmt_id); error= simple_command(stmt->mysql, COM_CLOSE_STMT, buff, 4, 1); } - mysql_free_result(stmt->result); - free_root(&stmt->mem_root, MYF(0)); - my_free((gptr) stmt->query, MYF(MY_WME | MY_ALLOW_ZERO_PTR)); - if (!skip_list) - stmt->mysql->stmts= list_delete(stmt->mysql->stmts, &stmt->list); - my_free((gptr) stmt, MYF(MY_WME)); + if (!error) + { + mysql_free_result(stmt->result); + free_root(&stmt->mem_root, MYF(0)); + if (!skip_list) + stmt->mysql->stmts= list_delete(stmt->mysql->stmts, &stmt->list); + my_free((gptr) stmt, MYF(MY_WME)); + } DBUG_RETURN(error); } |