diff options
Diffstat (limited to 'sql-common')
-rw-r--r-- | sql-common/client.c | 63 | ||||
-rw-r--r-- | sql-common/my_time.c | 233 |
2 files changed, 281 insertions, 15 deletions
diff --git a/sql-common/client.c b/sql-common/client.c index c25e0e3de8f..df7a3fe4974 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -57,7 +57,7 @@ my_bool net_flush(NET *net); #else /*EMBEDDED_LIBRARY*/ -#define CLI_MYSQL_REAL_CONNECT mysql_real_connect +#define CLI_MYSQL_REAL_CONNECT STDCALL mysql_real_connect #endif /*EMBEDDED_LIBRARY*/ #include <my_sys.h> #include <mysys_err.h> @@ -98,9 +98,6 @@ my_bool net_flush(NET *net); # include <sys/un.h> #endif -#ifndef INADDR_NONE -#define INADDR_NONE -1 -#endif #if defined(MSDOS) || defined(__WIN__) #define perror(A) #else @@ -664,6 +661,7 @@ cli_advanced_command(MYSQL *mysql, enum enum_server_command command, } if (mysql->status != MYSQL_STATUS_READY) { + DBUG_PRINT("error",("state: %d", mysql->status)); set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); return 1; } @@ -755,7 +753,7 @@ static void cli_flush_use_result(MYSQL *mysql) { if (protocol_41(mysql)) { - char *pos= (char*) mysql->net.read_pos; + char *pos= (char*) mysql->net.read_pos + 1; mysql->warning_count=uint2korr(pos); pos+=2; mysql->server_status=uint2korr(pos); pos+=2; } @@ -892,6 +890,7 @@ static const char *default_options[]= "replication-probe", "enable-reads-from-master", "repl-parse-query", "ssl-cipher", "max-allowed-packet", "protocol", "shared-memory-base-name", "multi-results", "multi-statements", "multi-queries", "secure-auth", + "report-data-truncation", NullS }; @@ -1103,6 +1102,9 @@ void mysql_read_default_options(struct st_mysql_options *options, case 33: /* secure-auth */ options->secure_auth= TRUE; break; + case 34: /* report-data-truncation */ + options->report_data_truncation= opt_arg ? test(atoi(opt_arg)) : 1; + break; default: DBUG_PRINT("warning",("unknown option: %s",option[0])); } @@ -1447,6 +1449,7 @@ mysql_init(MYSQL *mysql) #endif mysql->options.methods_to_use= MYSQL_OPT_GUESS_CONNECTION; + mysql->options.report_data_truncation= TRUE; /* default */ return mysql; } @@ -1532,7 +1535,8 @@ static MYSQL_METHODS client_methods= #endif }; -MYSQL * STDCALL + +MYSQL * CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, const char *passwd, const char *db, uint port, const char *unix_socket,ulong client_flag) @@ -1554,7 +1558,6 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, #ifdef HAVE_SYS_UN_H struct sockaddr_un UNIXaddr; #endif - init_sigpipe_variables DBUG_ENTER("mysql_real_connect"); LINT_INIT(host_info); @@ -1607,8 +1610,24 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, port=mysql->options.port; if (!unix_socket) unix_socket=mysql->options.unix_socket; + + /* + By default we don't reconnect because it could silently corrupt data (after + reconnection you potentially lose table locks, user variables, session + variables (transactions but they are specifically dealt with in + mysql_reconnect()). + This is a change: < 5.0.3 mysql->reconnect was set to 1 by default. + How this change impacts existing apps: + - existing apps which relyed on the default will see a behaviour change; + they will have to set reconnect=1 after mysql_real_connect(). + - existing apps which explicitely asked for reconnection (the only way they + could do it was by setting mysql.reconnect to 1 after mysql_real_connect()) + will not see a behaviour change. + - existing apps which explicitely asked for no reconnection + (mysql.reconnect=0) will not see a behaviour change. + */ + mysql->reconnect= 0; - mysql->reconnect=1; /* Reconnect as default */ mysql->server_status=SERVER_STATUS_AUTOCOMMIT; /* @@ -1663,7 +1682,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, ER(net->last_errno),socket_errno); goto error; } - net->vio = vio_new(sock, VIO_TYPE_SOCKET, TRUE); + net->vio= vio_new(sock, VIO_TYPE_SOCKET, + VIO_LOCALHOST | VIO_BUFFERED_READ); bzero((char*) &UNIXaddr,sizeof(UNIXaddr)); UNIXaddr.sun_family = AF_UNIX; strmake(UNIXaddr.sun_path, unix_socket, sizeof(UNIXaddr.sun_path)-1); @@ -1738,7 +1758,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, ER(net->last_errno),socket_errno); goto error; } - net->vio = vio_new(sock,VIO_TYPE_TCPIP,FALSE); + net->vio= vio_new(sock, VIO_TYPE_TCPIP, VIO_BUFFERED_READ); bzero((char*) &sock_addr,sizeof(sock_addr)); sock_addr.sin_family = AF_INET; @@ -2189,6 +2209,7 @@ my_bool mysql_reconnect(MYSQL *mysql) strmov(mysql->net.sqlstate, tmp_mysql.net.sqlstate); DBUG_RETURN(1); } + tmp_mysql.reconnect= 1; tmp_mysql.free_me= mysql->free_me; /* @@ -2636,6 +2657,25 @@ mysql_fetch_row(MYSQL_RES *res) } +/************************************************************************** + Get column lengths of the current row + If one uses mysql_use_result, res->lengths contains the length information, + else the lengths are calculated from the offset between pointers. +**************************************************************************/ + +ulong * STDCALL +mysql_fetch_lengths(MYSQL_RES *res) +{ + MYSQL_ROW column; + + if (!(column=res->current_row)) + return 0; /* Something is wrong */ + if (res->data) + (*res->methods->fetch_lengths)(res->lengths, column, res->field_count); + return res->lengths; +} + + int STDCALL mysql_options(MYSQL *mysql,enum mysql_option option, const char *arg) { @@ -2704,6 +2744,9 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const char *arg) case MYSQL_SECURE_AUTH: mysql->options.secure_auth= *(my_bool *) arg; break; + case MYSQL_REPORT_DATA_TRUNCATION: + mysql->options.report_data_truncation= test(*(my_bool *) arg); + break; default: DBUG_RETURN(1); } diff --git a/sql-common/my_time.c b/sql-common/my_time.c index 8dd4801b562..1078259f15d 100644 --- a/sql-common/my_time.c +++ b/sql-common/my_time.c @@ -47,6 +47,43 @@ uchar days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}; static long my_time_zone=0; +/* Calc days in one year. works with 0 <= year <= 99 */ + +uint calc_days_in_year(uint year) +{ + return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ? + 366 : 365); +} + +/* + check date. + + SYNOPOSIS + bool check_date() + time Date to check. + + NOTES + Here we assume that year and month is ok ! + If month is 0 we allow any date. (This only happens if we allow zero + date parts in str_to_datetime()) + + RETURN + 0 ok + 1 errro +*/ + +bool check_date(MYSQL_TIME *ltime) +{ + if (ltime->month && ltime->day > days_in_month[ltime->month-1]) + { + if (ltime->month != 2 || calc_days_in_year(ltime->year) != 366 || + ltime->day != 29) + return 1; + } + return 0; +} + + /* Convert a timestamp string to a MYSQL_TIME value. @@ -58,8 +95,12 @@ static long my_time_zone=0; flags Bitmap of following items TIME_FUZZY_DATE Set if we should allow partial dates TIME_DATETIME_ONLY Set if we only allow full datetimes. - was_cut Set to 1 if value was cut during conversion or to 0 - otherwise. + TIME_NO_ZERO_IN_DATE Don't allow partial dates + TIME_NO_ZERO_DATE Don't allow 0000-00-00 date + TIME_INVALID_DATES Allow 2000-02-31 + was_cut 0 Value ok + 1 If value was cut during conversion + 2 Date part was withing ranges but date was wrong DESCRIPTION At least the following formats are recogniced (based on number of digits) @@ -127,6 +168,8 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, *was_cut= 1; DBUG_RETURN(MYSQL_TIMESTAMP_NONE); } + if (flags & TIME_NO_ZERO_IN_DATE) + flags&= ~TIME_FUZZY_DATE; is_internal_format= 0; /* This has to be changed if want to activate different timestamp formats */ @@ -342,11 +385,22 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, if (year_length == 2 && not_zero_date) l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900); + if (!not_zero_date && (flags & TIME_NO_ZERO_DATE)) + { + /* + We don't set *was_cut here to signal that the problem was a zero date + and not an invalid date + */ + goto err; + } + if (number_of_fields < 3 || l_time->year > 9999 || l_time->month > 12 || l_time->day > 31 || l_time->hour > 23 || l_time->minute > 59 || l_time->second > 59 || - (!(flags & TIME_FUZZY_DATE) && (l_time->month == 0 || l_time->day == 0))) + (!(flags & TIME_FUZZY_DATE) && (l_time->month == 0 || + l_time->day == 0) && + not_zero_date)) { /* Only give warning for a zero date if there is some garbage after */ if (!not_zero_date) /* If zero date */ @@ -360,14 +414,19 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, } } } - if (not_zero_date) - *was_cut= 1; + *was_cut= test(not_zero_date); goto err; } l_time->time_type= (number_of_fields <= 3 ? MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME); + if (not_zero_date && !(flags & TIME_INVALID_DATES) && check_date(l_time)) + { + *was_cut= 2; /* Not correct date */ + goto err; + } + for (; str != end ; str++) { if (!my_isspace(&my_charset_latin1,*str)) @@ -812,3 +871,167 @@ int my_TIME_to_str(const MYSQL_TIME *l_time, char *to) return 0; } } + + +/* + Convert datetime value specified as number to broken-down TIME + representation and form value of DATETIME type as side-effect. + + SYNOPSIS + number_to_datetime() + nr - datetime value as number + time_res - pointer for structure for broken-down representation + fuzzy_date - indicates whenever we allow fuzzy dates + was_cut - set ot 1 if there was some kind of error during + conversion or to 0 if everything was OK. + + DESCRIPTION + Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS, + YYYYMMDDHHMMSS to broken-down TIME representation. Return value in + YYYYMMDDHHMMSS format as side-effect. + + This function also checks if datetime value fits in DATETIME range. + + RETURN VALUE + Datetime value in YYYYMMDDHHMMSS format. + If input value is not valid datetime value then 0 is returned. +*/ + +longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res, + my_bool fuzzy_date, int *was_cut) +{ + long part1,part2; + + *was_cut= 0; + + if (nr == LL(0) || nr >= LL(10000101000000)) + goto ok; + if (nr < 101) + goto err; + if (nr <= (YY_PART_YEAR-1)*10000L+1231L) + { + nr= (nr+20000000L)*1000000L; /* YYMMDD, year: 2000-2069 */ + goto ok; + } + if (nr < (YY_PART_YEAR)*10000L+101L) + goto err; + if (nr <= 991231L) + { + nr= (nr+19000000L)*1000000L; /* YYMMDD, year: 1970-1999 */ + goto ok; + } + if (nr < 10000101L) + goto err; + if (nr <= 99991231L) + { + nr= nr*1000000L; + goto ok; + } + if (nr < 101000000L) + goto err; + if (nr <= (YY_PART_YEAR-1)*LL(10000000000)+LL(1231235959)) + { + nr= nr+LL(20000000000000); /* YYMMDDHHMMSS, 2000-2069 */ + goto ok; + } + if (nr < YY_PART_YEAR*LL(10000000000)+ LL(101000000)) + goto err; + if (nr <= LL(991231235959)) + nr= nr+LL(19000000000000); /* YYMMDDHHMMSS, 1970-1999 */ + + ok: + part1=(long) (nr/LL(1000000)); + part2=(long) (nr - (longlong) part1*LL(1000000)); + time_res->year= (int) (part1/10000L); part1%=10000L; + time_res->month= (int) part1 / 100; + time_res->day= (int) part1 % 100; + time_res->hour= (int) (part2/10000L); part2%=10000L; + time_res->minute=(int) part2 / 100; + time_res->second=(int) part2 % 100; + + if (time_res->year <= 9999 && time_res->month <= 12 && + time_res->day <= 31 && time_res->hour <= 23 && + time_res->minute <= 59 && time_res->second <= 59 && + (fuzzy_date || (time_res->month != 0 && time_res->day != 0) || nr==0)) + return nr; + + err: + + *was_cut= 1; + return LL(0); +} + + +/* Convert time value to integer in YYYYMMDDHHMMSS format */ + +ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *time) +{ + return ((ulonglong) (time->year * 10000UL + + time->month * 100UL + + time->day) * ULL(1000000) + + (ulonglong) (time->hour * 10000UL + + time->minute * 100UL + + time->second)); +} + + +/* Convert TIME value to integer in YYYYMMDD format */ + +ulonglong TIME_to_ulonglong_date(const MYSQL_TIME *time) +{ + return (ulonglong) (time->year * 10000UL + time->month * 100UL + time->day); +} + + +/* + Convert TIME value to integer in HHMMSS format. + This function doesn't take into account time->day member: + it's assumed that days have been converted to hours already. +*/ + +ulonglong TIME_to_ulonglong_time(const MYSQL_TIME *time) +{ + return (ulonglong) (time->hour * 10000UL + + time->minute * 100UL + + time->second); +} + + +/* + Convert struct TIME (date and time split into year/month/day/hour/... + to a number in format YYYYMMDDHHMMSS (DATETIME), + YYYYMMDD (DATE) or HHMMSS (TIME). + + SYNOPSIS + TIME_to_ulonglong() + + DESCRIPTION + The function is used when we need to convert value of time item + to a number if it's used in numeric context, i. e.: + SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0; + SELECT ?+1; + + NOTE + This function doesn't check that given TIME structure members are + in valid range. If they are not, return value won't reflect any + valid date either. +*/ + +ulonglong TIME_to_ulonglong(const MYSQL_TIME *time) +{ + switch (time->time_type) { + case MYSQL_TIMESTAMP_DATETIME: + return TIME_to_ulonglong_datetime(time); + case MYSQL_TIMESTAMP_DATE: + return TIME_to_ulonglong_date(time); + case MYSQL_TIMESTAMP_TIME: + return TIME_to_ulonglong_time(time); + case MYSQL_TIMESTAMP_NONE: + case MYSQL_TIMESTAMP_ERROR: + return ULL(0); + default: + DBUG_ASSERT(0); + } + return 0; +} + |