diff --git a/libmariadb/ma_stmt_codec.c b/libmariadb/ma_stmt_codec.c index 1260e494..10684408 100644 --- a/libmariadb/ma_stmt_codec.c +++ b/libmariadb/ma_stmt_codec.c @@ -198,42 +198,90 @@ double my_atod(const char *number, const char *end, int *error) my_bool str_to_TIME(const char *str, size_t length, MYSQL_TIME *tm) { - my_bool is_time=0, is_date=0, has_time_frac=0; - char *p= (char *)str; + char *start= alloca(length + 1); + my_bool is_date= 0, is_time= 0; - if ((p= strchr(str, '-')) && p <= str + length) - is_date= 1; - if ((p= strchr(str, ':')) && p <= str + length) - is_time= 1; - if ((p= strchr(str, '.')) && p <= str + length) - has_time_frac= 1; - - p= (char *)str; - memset(tm, 0, sizeof(MYSQL_TIME)); + if (!start) + goto error; + tm->time_type= MYSQL_TIMESTAMP_NONE; + + memcpy(start, str, length); + start[length]= '\0'; + + while (length && isspace(*start)) start++, length--; + + if (!length) + goto error; + + /* negativ value? */ + if (*start == '-') + { + tm->neg= 1; + start++; + length--; + } + + if (!length) + return 1; + + /* Determine time type: + MYSQL_TIMESTAMP_DATE: [-]YY[YY].MM.DD + MYSQL_TIMESTAMP_DATETIME: [-]YY[YY].MM.DD hh:mm:ss.mmmmmm + MYSQL_TIMESTAMP_TIME: [-]hh:mm:ss.mmmmmm + */ + if (strchr(start, '-')) + { + if (tm->neg) + goto error; + tm->time_type= MYSQL_TIMESTAMP_DATE; + if (sscanf(start, "%d-%d-%d", &tm->year, &tm->month, &tm->day) < 3) + goto error; + is_date= 1; + if (!(start= strchr(start, ' '))) + goto check; + } + if (!strchr(start, ':')) + goto check; + + is_time= 1; + if (tm->time_type== MYSQL_TIMESTAMP_DATE) + tm->time_type= MYSQL_TIMESTAMP_DATETIME; + else + tm->time_type= MYSQL_TIMESTAMP_TIME; + + if (strchr(start, '.')) /* fractional seconds */ + { + if (sscanf(start, "%d:%d:%d.%ld", &tm->hour, &tm->minute, + &tm->second,&tm->second_part) < 4) + goto error; + } else { + if (sscanf(start, "%d:%d:%d", &tm->hour, &tm->minute, + &tm->second) < 3) + goto error; + } + +check: + if (tm->time_type == MYSQL_TIMESTAMP_NONE) + goto error; if (is_date) { - sscanf(str, "%d-%d-%d", &tm->year, &tm->month, &tm->day); - p= strchr(str, ' '); - if (!p) - { - tm->time_type= MYSQL_TIMESTAMP_DATE; - return 0; - } - } - if (has_time_frac) - { - sscanf(p, "%d:%d:%d.%ld", &tm->hour, &tm->minute, &tm->second, &tm->second_part); - tm->time_type= (is_date) ? MYSQL_TIMESTAMP_DATETIME : MYSQL_TIMESTAMP_TIME; - return 0; + if (tm->year < 69) + tm->year+= 2000; + else if (tm->year < 100) + tm->year+= 1900; + if (tm->day > 31 || tm->month > 12) + goto error; } if (is_time) { - sscanf(p, "%d:%d:%d", &tm->hour, &tm->minute, &tm->second); - tm->time_type= (is_date) ? MYSQL_TIMESTAMP_DATETIME : MYSQL_TIMESTAMP_TIME; - return 0; + if (tm->minute > 59 || tm->second > 59) + goto error; } + return 0; +error: + tm->time_type= MYSQL_TIMESTAMP_ERROR; return 1; } diff --git a/unittest/libmariadb/ps_bugs.c b/unittest/libmariadb/ps_bugs.c index 5fa72b74..b9437bb8 100644 --- a/unittest/libmariadb/ps_bugs.c +++ b/unittest/libmariadb/ps_bugs.c @@ -4654,39 +4654,68 @@ static int test_compress(MYSQL *mysql) static int test_codbc138(MYSQL *mysql) { int rc; - MYSQL_STMT *stmt= mysql_stmt_init(mysql); + MYSQL_STMT *stmt; MYSQL_BIND bind[1]; MYSQL_TIME tm; + int i= 0; - rc= mysql_stmt_prepare(stmt, SL("SELECT DATE_ADD('2018-02-01', INTERVAL -188 DAY)")); - check_stmt_rc(rc, stmt); + struct st_time_test { + char *statement; + MYSQL_TIME tm; + } time_test[]= { + {"SELECT DATE_ADD('2018-02-01', INTERVAL -188 DAY)", + {2017,7,28,0,0,0,0L,0, MYSQL_TIMESTAMP_DATE} + }, + {"SELECT '2001-02-03 11:12:13.123456'", + {2001,2,3,11,12,13,123456L,0, MYSQL_TIMESTAMP_DATETIME} + }, + {"SELECT '-11:12:13'", + {0,0,0,11,12,13,0,1, MYSQL_TIMESTAMP_TIME} + }, + {"SELECT ' '", + {0,0,0,0,0,0,0,0, MYSQL_TIMESTAMP_ERROR} + }, + {"SELECT '1--'", + {1,0,0,0,0,0,0,0, MYSQL_TIMESTAMP_ERROR} + }, + {"SELECT '-2001-01-01'", + {1,0,0,0,0,0,0,0, MYSQL_TIMESTAMP_ERROR} + }, + {"SELECT '-11:00'", + {1,0,0,0,0,0,0,0, MYSQL_TIMESTAMP_ERROR} + }, + {NULL, {0}} + }; - rc= mysql_stmt_execute(stmt); - check_stmt_rc(rc, stmt); - - memset(bind, 0, sizeof(MYSQL_BIND)); - bind[0].buffer_type= MYSQL_TYPE_DATETIME; - bind[0].buffer= &tm; - bind[0].buffer_length= sizeof(MYSQL_TIME); - - rc= mysql_stmt_bind_result(stmt, bind); - check_stmt_rc(rc, stmt); - - rc= mysql_stmt_fetch(stmt); - check_stmt_rc(rc, stmt); - - if (tm.year != 2017 && tm.day != 28 && tm.month != 7) + while (time_test[i].statement) { - diag("Error: Expected 2017-07-02"); - return FAIL; - } - if (tm.minute | tm.second || tm.second_part) - { - diag("Error: minute, second or second_part is not zero"); - return FAIL; + stmt= mysql_stmt_init(mysql); + rc= mysql_stmt_prepare(stmt, SL(time_test[i].statement)); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_execute(stmt); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_store_result(stmt); + + memset(bind, 0, sizeof(MYSQL_BIND)); + bind[0].buffer_type= MYSQL_TYPE_DATETIME; + bind[0].buffer= &tm; + bind[0].buffer_length= sizeof(MYSQL_TIME); + + rc= mysql_stmt_bind_result(stmt, bind); + check_stmt_rc(rc, stmt); + rc= mysql_stmt_fetch(stmt); + check_stmt_rc(rc, stmt); + diag("test: %s %d %d", time_test[i].statement, tm.time_type, time_test[i].tm.time_type); + if (time_test[i].tm.time_type == MYSQL_TIMESTAMP_ERROR) + { + FAIL_UNLESS(tm.time_type == MYSQL_TIMESTAMP_ERROR, "MYSQL_TIMESTAMP_ERROR expected"); + } + else + FAIL_UNLESS(memcmp(&tm, &time_test[i].tm, sizeof(MYSQL_TIME)) == 0, "time_in != time_out"); + mysql_stmt_close(stmt); + i++; } - mysql_stmt_close(stmt); return OK; } @@ -4776,4 +4805,3 @@ int main(int argc, char **argv) return(exit_status()); } -