From 9a3fbf0d96426dd5273bb8992c4b131965e6d1e1 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 15 Mar 2004 16:32:53 +0400 Subject: [PATCH 1/9] Fix for valgrind's warning sql/gstream.cc: checking for ending \0 changed with m_limit sql/gstream.h: checking for ending \0 changed with m_limit sql/item_geofunc.cc: we should check for null value before we use val_str result sql/item_strfunc.cc: get rid of annoying warnings sql/spatial.h: error about size_t type fixed --- sql/gstream.cc | 17 +++++++++++------ sql/gstream.h | 9 +++++---- sql/item_geofunc.cc | 10 ++++++---- sql/item_strfunc.cc | 4 ++-- sql/spatial.h | 2 +- 5 files changed, 25 insertions(+), 17 deletions(-) diff --git a/sql/gstream.cc b/sql/gstream.cc index 6b1e12ec733..f7d11d76b0c 100644 --- a/sql/gstream.cc +++ b/sql/gstream.cc @@ -24,7 +24,7 @@ enum Gis_read_stream::enum_tok_types Gis_read_stream::get_next_toc_type() { skip_space(); - if (!*m_cur) + if (m_cur >= m_limit) return eostream; if (my_isvar_start(&my_charset_bin, *m_cur)) return word; @@ -53,7 +53,7 @@ bool Gis_read_stream::get_next_word(LEX_STRING *res) my_isvar() is a macro that would cause side effects */ m_cur++; - while (my_isvar(&my_charset_bin, *m_cur)) + while ((m_cur < m_limit) && my_isvar(&my_charset_bin, *m_cur)) m_cur++; res->length= (uint32) (m_cur - res->str); @@ -71,16 +71,21 @@ bool Gis_read_stream::get_next_word(LEX_STRING *res) bool Gis_read_stream::get_next_number(double *d) { char *endptr; + int err; skip_space(); - /* The following will also test for end \0 */ - if ((*m_cur < '0' || *m_cur > '9') && *m_cur != '-' && *m_cur != '+') + + if ((m_cur >= m_limit) || + (*m_cur < '0' || *m_cur > '9') && *m_cur != '-' && *m_cur != '+') { set_error_msg("Numeric constant expected"); return 1; } - *d = my_strtod(m_cur, &endptr); + *d = my_strntod(m_charset, (char *)m_cur, + m_limit-m_cur, &endptr, &err); + if (err) + return 1; if (endptr) m_cur = endptr; return 0; @@ -90,7 +95,7 @@ bool Gis_read_stream::get_next_number(double *d) bool Gis_read_stream::check_next_symbol(char symbol) { skip_space(); - if (*m_cur != symbol) + if ((m_cur >= m_limit) || (*m_cur != symbol)) { char buff[32]; strmov(buff, "'?' expected"); diff --git a/sql/gstream.h b/sql/gstream.h index 2e9513d2639..bfbf28851ce 100644 --- a/sql/gstream.h +++ b/sql/gstream.h @@ -29,8 +29,8 @@ public: comma }; - Gis_read_stream(const char *buffer, int size) - :m_cur(buffer), m_limit(buffer + size), m_err_msg(NULL) + Gis_read_stream(CHARSET_INFO *charset, const char *buffer, int size) + :m_cur(buffer), m_limit(buffer + size), m_err_msg(NULL), m_charset(charset) {} Gis_read_stream(): m_cur(NullS), m_limit(NullS), m_err_msg(NullS) {} @@ -46,14 +46,14 @@ public: inline void skip_space() { - while (my_isspace(&my_charset_latin1, *m_cur)) + while ((m_cur < m_limit) && my_isspace(&my_charset_latin1, *m_cur)) m_cur++; } /* Skip next character, if match. Return 1 if no match */ inline bool skip_char(char skip) { skip_space(); - if (*m_cur != skip) + if ((m_cur >= m_limit) || *m_cur != skip) return 1; /* Didn't find char */ m_cur++; return 0; @@ -72,4 +72,5 @@ protected: const char *m_cur; const char *m_limit; char *m_err_msg; + CHARSET_INFO *m_charset; }; diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 02c0f5829fd..1c9338f5f35 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -31,10 +31,13 @@ String *Item_func_geometry_from_text::val_str(String *str) { Geometry_buffer buffer; - Geometry *geom; String arg_val; String *wkt= args[0]->val_str(&arg_val); - Gis_read_stream trs(wkt->c_ptr(), wkt->length()); + + if ((null_value= args[0]->null_value)) + return 0; + + Gis_read_stream trs(wkt->charset(), wkt->ptr(), wkt->length()); uint32 srid= 0; if ((arg_count == 2) && !args[1]->null_value) @@ -44,8 +47,7 @@ String *Item_func_geometry_from_text::val_str(String *str) return 0; str->length(0); str->q_append(srid); - if ((null_value=(args[0]->null_value || - !(geom= Geometry::create_from_wkt(&buffer, &trs, str, 0))))) + if ((null_value= !Geometry::create_from_wkt(&buffer, &trs, str, 0))) return 0; return str; } diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index bcf534c9e84..3c8689e1588 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2241,7 +2241,7 @@ String *Item_func_hex::val_str(String *str) return &tmp_value; } -int inline hexchar_to_int(char c) +inline int hexchar_to_int(char c) { if (c <= '9' && c >= '0') return c-'0'; @@ -2721,7 +2721,7 @@ String *Item_func_uuid::val_str(String *str) { ulong tmp=sql_rnd_with_mutex(); uchar mac[6]; - int i; + unsigned int i; if (my_gethwaddr(mac)) { /* diff --git a/sql/spatial.h b/sql/spatial.h index 5d425725437..26396dd0f90 100644 --- a/sql/spatial.h +++ b/sql/spatial.h @@ -157,7 +157,7 @@ struct Geometry_buffer; class Geometry { public: - static void *operator new(unsigned size_t, void *buffer) + static void *operator new(size_t size, void *buffer) { return buffer; } From afedd9f8f777cd3ccbe6901f70b467bb71147afa Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 15 Mar 2004 18:28:21 +0400 Subject: [PATCH 2/9] Task #835: additional changes fot str_to_date include/mysqld_error.h: Task #835: additional changes fot str_to_date New error message mysql-test/r/date_formats.result: Task #835: additional changes fot str_to_date tests mysql-test/r/func_sapdb.result: Task #835: additional changes fot str_to_date tests mysql-test/r/func_time.result: Task #835: additional changes fot str_to_date tests mysql-test/r/type_time.result: Task #835: additional changes fot str_to_date tests mysql-test/t/date_formats.test: Task #835: additional changes fot str_to_date tests mysql-test/t/func_sapdb.test: Task #835: additional changes fot str_to_date tests mysql-test/t/func_time.test: Task #835: additional changes fot str_to_date tests sql/share/czech/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/danish/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/dutch/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/english/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/estonian/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/french/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/german/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/greek/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/hungarian/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/italian/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/japanese/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/korean/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/norwegian-ny/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/norwegian/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/polish/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/portuguese/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/romanian/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/russian/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/serbian/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/slovak/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/spanish/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/swedish/errmsg.txt: Task #835: additional changes fot str_to_date New error message sql/share/ukrainian/errmsg.txt: Task #835: additional changes fot str_to_date New error message --- include/mysqld_error.h | 3 +- mysql-test/r/date_formats.result | 113 +++++++++--- mysql-test/r/func_sapdb.result | 15 ++ mysql-test/r/func_time.result | 27 ++- mysql-test/r/type_time.result | 2 + mysql-test/t/date_formats.test | 31 +++- mysql-test/t/func_sapdb.test | 6 + mysql-test/t/func_time.test | 10 +- sql/item_timefunc.cc | 287 +++++++++++++++++++++++------- sql/item_timefunc.h | 43 +++-- sql/mysql_priv.h | 3 + sql/mysqld.cc | 8 + sql/share/czech/errmsg.txt | 1 + sql/share/danish/errmsg.txt | 1 + sql/share/dutch/errmsg.txt | 1 + sql/share/english/errmsg.txt | 1 + sql/share/estonian/errmsg.txt | 1 + sql/share/french/errmsg.txt | 1 + sql/share/german/errmsg.txt | 1 + sql/share/greek/errmsg.txt | 1 + sql/share/hungarian/errmsg.txt | 1 + sql/share/italian/errmsg.txt | 1 + sql/share/japanese/errmsg.txt | 1 + sql/share/korean/errmsg.txt | 1 + sql/share/norwegian-ny/errmsg.txt | 1 + sql/share/norwegian/errmsg.txt | 1 + sql/share/polish/errmsg.txt | 1 + sql/share/portuguese/errmsg.txt | 1 + sql/share/romanian/errmsg.txt | 1 + sql/share/russian/errmsg.txt | 1 + sql/share/serbian/errmsg.txt | 1 + sql/share/slovak/errmsg.txt | 1 + sql/share/spanish/errmsg.txt | 1 + sql/share/swedish/errmsg.txt | 1 + sql/share/ukrainian/errmsg.txt | 1 + sql/time.cc | 80 +++++++-- 36 files changed, 508 insertions(+), 143 deletions(-) diff --git a/include/mysqld_error.h b/include/mysqld_error.h index 2a7623b6947..77164e637cb 100644 --- a/include/mysqld_error.h +++ b/include/mysqld_error.h @@ -308,4 +308,5 @@ #define ER_FEATURE_DISABLED 1289 #define ER_OPTION_PREVENTS_STATEMENT 1290 #define ER_DUPLICATED_VALUE_IN_TYPE 1291 -#define ER_ERROR_MESSAGES 292 +#define ER_TRUNCATED_WRONG_VALUE 1292 +#define ER_ERROR_MESSAGES 293 diff --git a/mysql-test/r/date_formats.result b/mysql-test/r/date_formats.result index 165a8d7011c..f1582707cf5 100644 --- a/mysql-test/r/date_formats.result +++ b/mysql-test/r/date_formats.result @@ -78,11 +78,11 @@ select str_to_date(concat('15-01-2001',' 2:59:58.999'), concat('%d-%m-%Y',' ','%H:%i:%s.%f')); str_to_date(concat('15-01-2001',' 2:59:58.999'), concat('%d-%m-%Y',' ','%H:%i:%s.%f')) -2001-01-15 02:59:58.000999 +2001-01-15 02:59:58.999000 create table t1 (date char(30), format char(30) not null); insert into t1 values ('2003-01-02 10:11:12', '%Y-%m-%d %H:%i:%S'), -('03-01-02 8:11:2.123456', '%y-%m-%d %H:%i:%S'), +('03-01-02 8:11:2.123456', '%y-%m-%d %H:%i:%S.%#'), ('2003-01-02 10:11:12 PM', '%Y-%m-%d %h:%i:%S %p'), ('2003-01-02 01:11:12.12345AM', '%Y-%m-%d %h:%i:%S.%f%p'), ('2003-01-02 02:11:12.12345AM', '%Y-%m-%d %h:%i:%S.%f %p'), @@ -106,16 +106,16 @@ insert into t1 values select date,format,str_to_date(date, format) as str_to_date from t1; date format str_to_date 2003-01-02 10:11:12 %Y-%m-%d %H:%i:%S 2003-01-02 10:11:12 -03-01-02 8:11:2.123456 %y-%m-%d %H:%i:%S 2003-01-02 08:11:02 +03-01-02 8:11:2.123456 %y-%m-%d %H:%i:%S.%# 2003-01-02 08:11:02 2003-01-02 10:11:12 PM %Y-%m-%d %h:%i:%S %p 2003-01-02 22:11:12 -2003-01-02 01:11:12.12345AM %Y-%m-%d %h:%i:%S.%f%p 2003-01-02 01:11:12.012345 -2003-01-02 02:11:12.12345AM %Y-%m-%d %h:%i:%S.%f %p 2003-01-02 02:11:12.012345 -2003-01-02 12:11:12.12345 am %Y-%m-%d %h:%i:%S.%f%p 2003-01-02 00:11:12.012345 +2003-01-02 01:11:12.12345AM %Y-%m-%d %h:%i:%S.%f%p 2003-01-02 01:11:12.123450 +2003-01-02 02:11:12.12345AM %Y-%m-%d %h:%i:%S.%f %p 2003-01-02 02:11:12.123450 +2003-01-02 12:11:12.12345 am %Y-%m-%d %h:%i:%S.%f%p 2003-01-02 00:11:12.123450 2003-01-02 11:11:12Pm %Y-%m-%d %h:%i:%S%p 2003-01-02 23:11:12 10:20:10 %H:%i:%s 0000-00-00 10:20:10 10:20:10 %h:%i:%s.%f 0000-00-00 10:20:10 10:20:10AM %h:%i:%s%p 0000-00-00 10:20:10 -10:20:10.44AM %h:%i:%s.%f%p 0000-00-00 10:20:10.000044 +10:20:10.44AM %h:%i:%s.%f%p 0000-00-00 10:20:10.440000 15-01-2001 12:59:58 %d-%m-%Y %H:%i:%S 2001-01-15 12:59:58 15 September 2001 %d %M %Y 2001-09-15 00:00:00 15 SEPTEMB 2001 %d %M %Y 2001-09-15 00:00:00 @@ -130,16 +130,16 @@ Thursday 53 1998 %W %u %Y 1998-12-31 00:00:00 select date,format,concat('',str_to_date(date, format)) as con from t1; date format con 2003-01-02 10:11:12 %Y-%m-%d %H:%i:%S 2003-01-02 10:11:12 -03-01-02 8:11:2.123456 %y-%m-%d %H:%i:%S 2003-01-02 08:11:02 +03-01-02 8:11:2.123456 %y-%m-%d %H:%i:%S.%# 2003-01-02 08:11:02 2003-01-02 10:11:12 PM %Y-%m-%d %h:%i:%S %p 2003-01-02 22:11:12 -2003-01-02 01:11:12.12345AM %Y-%m-%d %h:%i:%S.%f%p 2003-01-02 01:11:12.012345 -2003-01-02 02:11:12.12345AM %Y-%m-%d %h:%i:%S.%f %p 2003-01-02 02:11:12.012345 -2003-01-02 12:11:12.12345 am %Y-%m-%d %h:%i:%S.%f%p 2003-01-02 00:11:12.012345 +2003-01-02 01:11:12.12345AM %Y-%m-%d %h:%i:%S.%f%p 2003-01-02 01:11:12.123450 +2003-01-02 02:11:12.12345AM %Y-%m-%d %h:%i:%S.%f %p 2003-01-02 02:11:12.123450 +2003-01-02 12:11:12.12345 am %Y-%m-%d %h:%i:%S.%f%p 2003-01-02 00:11:12.123450 2003-01-02 11:11:12Pm %Y-%m-%d %h:%i:%S%p 2003-01-02 23:11:12 10:20:10 %H:%i:%s 0000-00-00 10:20:10 10:20:10 %h:%i:%s.%f 0000-00-00 10:20:10 10:20:10AM %h:%i:%s%p 0000-00-00 10:20:10 -10:20:10.44AM %h:%i:%s.%f%p 0000-00-00 10:20:10.000044 +10:20:10.44AM %h:%i:%s.%f%p 0000-00-00 10:20:10.440000 15-01-2001 12:59:58 %d-%m-%Y %H:%i:%S 2001-01-15 12:59:58 15 September 2001 %d %M %Y 2001-09-15 00:00:00 15 SEPTEMB 2001 %d %M %Y 2001-09-15 00:00:00 @@ -154,16 +154,16 @@ Thursday 53 1998 %W %u %Y 1998-12-31 00:00:00 select date,format,cast(str_to_date(date, format) as datetime) as datetime from t1; date format datetime 2003-01-02 10:11:12 %Y-%m-%d %H:%i:%S 2003-01-02 10:11:12 -03-01-02 8:11:2.123456 %y-%m-%d %H:%i:%S 2003-01-02 08:11:02 +03-01-02 8:11:2.123456 %y-%m-%d %H:%i:%S.%# 2003-01-02 08:11:02 2003-01-02 10:11:12 PM %Y-%m-%d %h:%i:%S %p 2003-01-02 22:11:12 -2003-01-02 01:11:12.12345AM %Y-%m-%d %h:%i:%S.%f%p 2003-01-02 01:11:12.012345 -2003-01-02 02:11:12.12345AM %Y-%m-%d %h:%i:%S.%f %p 2003-01-02 02:11:12.012345 -2003-01-02 12:11:12.12345 am %Y-%m-%d %h:%i:%S.%f%p 2003-01-02 00:11:12.012345 +2003-01-02 01:11:12.12345AM %Y-%m-%d %h:%i:%S.%f%p 2003-01-02 01:11:12.123450 +2003-01-02 02:11:12.12345AM %Y-%m-%d %h:%i:%S.%f %p 2003-01-02 02:11:12.123450 +2003-01-02 12:11:12.12345 am %Y-%m-%d %h:%i:%S.%f%p 2003-01-02 00:11:12.123450 2003-01-02 11:11:12Pm %Y-%m-%d %h:%i:%S%p 2003-01-02 23:11:12 10:20:10 %H:%i:%s 0000-00-00 10:20:10 10:20:10 %h:%i:%s.%f 0000-00-00 10:20:10 10:20:10AM %h:%i:%s%p 0000-00-00 10:20:10 -10:20:10.44AM %h:%i:%s.%f%p 0000-00-00 10:20:10.000044 +10:20:10.44AM %h:%i:%s.%f%p 0000-00-00 10:20:10.440000 15-01-2001 12:59:58 %d-%m-%Y %H:%i:%S 2001-01-15 12:59:58 15 September 2001 %d %M %Y 2001-09-15 00:00:00 15 SEPTEMB 2001 %d %M %Y 2001-09-15 00:00:00 @@ -178,7 +178,7 @@ Thursday 53 1998 %W %u %Y 1998-12-31 00:00:00 select date,format,DATE(str_to_date(date, format)) as date2 from t1; date format date2 2003-01-02 10:11:12 %Y-%m-%d %H:%i:%S 2003-01-02 -03-01-02 8:11:2.123456 %y-%m-%d %H:%i:%S 2003-01-02 +03-01-02 8:11:2.123456 %y-%m-%d %H:%i:%S.%# 2003-01-02 2003-01-02 10:11:12 PM %Y-%m-%d %h:%i:%S %p 2003-01-02 2003-01-02 01:11:12.12345AM %Y-%m-%d %h:%i:%S.%f%p 2003-01-02 2003-01-02 02:11:12.12345AM %Y-%m-%d %h:%i:%S.%f %p 2003-01-02 @@ -202,16 +202,16 @@ Thursday 53 1998 %W %u %Y 1998-12-31 select date,format,TIME(str_to_date(date, format)) as time from t1; date format time 2003-01-02 10:11:12 %Y-%m-%d %H:%i:%S 10:11:12 -03-01-02 8:11:2.123456 %y-%m-%d %H:%i:%S 08:11:02 +03-01-02 8:11:2.123456 %y-%m-%d %H:%i:%S.%# 08:11:02 2003-01-02 10:11:12 PM %Y-%m-%d %h:%i:%S %p 22:11:12 -2003-01-02 01:11:12.12345AM %Y-%m-%d %h:%i:%S.%f%p 01:11:12.012345 -2003-01-02 02:11:12.12345AM %Y-%m-%d %h:%i:%S.%f %p 02:11:12.012345 -2003-01-02 12:11:12.12345 am %Y-%m-%d %h:%i:%S.%f%p 00:11:12.012345 +2003-01-02 01:11:12.12345AM %Y-%m-%d %h:%i:%S.%f%p 01:11:12.123450 +2003-01-02 02:11:12.12345AM %Y-%m-%d %h:%i:%S.%f %p 02:11:12.123450 +2003-01-02 12:11:12.12345 am %Y-%m-%d %h:%i:%S.%f%p 00:11:12.123450 2003-01-02 11:11:12Pm %Y-%m-%d %h:%i:%S%p 23:11:12 10:20:10 %H:%i:%s 10:20:10 10:20:10 %h:%i:%s.%f 10:20:10 10:20:10AM %h:%i:%s%p 10:20:10 -10:20:10.44AM %h:%i:%s.%f%p 10:20:10.000044 +10:20:10.44AM %h:%i:%s.%f%p 10:20:10.440000 15-01-2001 12:59:58 %d-%m-%Y %H:%i:%S 12:59:58 15 September 2001 %d %M %Y 00:00:00 15 SEPTEMB 2001 %d %M %Y 00:00:00 @@ -226,16 +226,16 @@ Thursday 53 1998 %W %u %Y 00:00:00 select date,format,concat(TIME(str_to_date(date, format))) as time2 from t1; date format time2 2003-01-02 10:11:12 %Y-%m-%d %H:%i:%S 10:11:12 -03-01-02 8:11:2.123456 %y-%m-%d %H:%i:%S 08:11:02 +03-01-02 8:11:2.123456 %y-%m-%d %H:%i:%S.%# 08:11:02 2003-01-02 10:11:12 PM %Y-%m-%d %h:%i:%S %p 22:11:12 -2003-01-02 01:11:12.12345AM %Y-%m-%d %h:%i:%S.%f%p 01:11:12.012345 -2003-01-02 02:11:12.12345AM %Y-%m-%d %h:%i:%S.%f %p 02:11:12.012345 -2003-01-02 12:11:12.12345 am %Y-%m-%d %h:%i:%S.%f%p 00:11:12.012345 +2003-01-02 01:11:12.12345AM %Y-%m-%d %h:%i:%S.%f%p 01:11:12.123450 +2003-01-02 02:11:12.12345AM %Y-%m-%d %h:%i:%S.%f %p 02:11:12.123450 +2003-01-02 12:11:12.12345 am %Y-%m-%d %h:%i:%S.%f%p 00:11:12.123450 2003-01-02 11:11:12Pm %Y-%m-%d %h:%i:%S%p 23:11:12 10:20:10 %H:%i:%s 10:20:10 10:20:10 %h:%i:%s.%f 10:20:10 10:20:10AM %h:%i:%s%p 10:20:10 -10:20:10.44AM %h:%i:%s.%f%p 10:20:10.000044 +10:20:10.44AM %h:%i:%s.%f%p 10:20:10.440000 15-01-2001 12:59:58 %d-%m-%Y %H:%i:%S 12:59:58 15 September 2001 %d %M %Y 00:00:00 15 SEPTEMB 2001 %d %M %Y 00:00:00 @@ -302,11 +302,15 @@ date format str_to_date 10:20:10AM %h:%i:%s 0000-00-00 10:20:10 2003-01-02 10:11:12 %Y-%m-%d %h:%i:%S 2003-01-02 10:11:12 03-01-02 10:11:12 PM %Y-%m-%d %h:%i:%S %p 0003-01-02 22:11:12 +Warnings: +Note 1292 Truncated wrong string value: '10:20:10AM' select date,format,concat(str_to_date(date, format),'') as con from t1; date format con 10:20:10AM %h:%i:%s 0000-00-00 10:20:10 2003-01-02 10:11:12 %Y-%m-%d %h:%i:%S 2003-01-02 10:11:12 03-01-02 10:11:12 PM %Y-%m-%d %h:%i:%S %p 0003-01-02 22:11:12 +Warnings: +Note 1292 Truncated wrong string value: '10:20:10AM' drop table t1; select get_format(DATE, 'USA') as a; a @@ -335,3 +339,56 @@ date_format(d,"%d") 14 14 drop table t1; +select str_to_date("2003-....01ABCD-02 10:11:12.0012", "%Y-%.%m%@-%d %H:%i:%S.%f") as a; +a +2003-01-02 10:11:12.001200 +create table t1 select str_to_date("2003-01-02 10:11:12.0012", "%Y-%m-%d %H:%i:%S.%f") as f1, +str_to_date("10:11:12.0012", "%H:%i:%S.%f") as f2, +str_to_date("2003-01-02", "%Y-%m-%d") as f3, +str_to_date("02", "%d") as f4, str_to_date("02 10", "%d %H") as f5; +describe t1; +Field Type Null Key Default Extra +f1 datetime YES NULL +f2 time YES NULL +f3 date YES NULL +f4 date YES NULL +f5 time YES NULL +select * from t1; +f1 f2 f3 f4 f5 +2003-01-02 10:11:12 10:11:12 2003-01-02 0000-00-02 58:00:00 +drop table t1; +create table t1 select "02 10" as a, "%d %H" as b; +select str_to_date(a,b) from t1; +str_to_date(a,b) +0000-00-02 10:00:00 +create table t2 select str_to_date(a,b) from t1; +describe t2; +Field Type Null Key Default Extra +str_to_date(a,b) char(29) YES NULL +select str_to_date("2003-01-02 10:11:12.0012", "%Y-%m-%d %H:%i:%S.%f") as f1, +str_to_date("2003-01-02 10:11:12.0012", "%Y-%m-%d %H:%i:%S") as f2, +str_to_date("2003-01-02", "%Y-%m-%d") as f3, +str_to_date("02 10:11:12", "%d %H:%i:%S.%f") as f4, +str_to_date("02 10:11:12", "%d %H:%i:%S") as f5, +str_to_date("02 10", "%d %f") as f6; +f1 f2 f3 f4 f5 f6 +2003-01-02 10:11:12.001200 2003-01-02 10:11:12 2003-01-02 58:11:12 58:11:12 48:00:00.100000 +Warnings: +Note 1292 Truncated wrong datetime value: '2003-01-02 10:11:12.0012' +drop table t1, t2; +select str_to_date("2003-01-02 10:11:12.0012ABCD", "%Y-%m-%d %H:%i:%S.%f") as f1, +addtime("-01:01:01.01 GGG", "-23:59:59.1") as f2, +microsecond("1997-12-31 23:59:59.01XXXX") as f3; +f1 f2 f3 +2003-01-02 10:11:12.001200 -25:01:00.110000 10000 +Warnings: +Note 1292 Truncated wrong datetime value: '2003-01-02 10:11:12.0012ABCD' +Note 1292 Truncated wrong time value: '-01:01:01.01 GG' +Note 1292 Truncated wrong datetime value: '1997-12-31 23:59:59.01XXXX' +select str_to_date("2003-04-05 g", "%Y-%m-%d") as f1, +str_to_date("2003-04-05 10:11:12.101010234567", "%Y-%m-%d %H:%i:%S.%f") as f2; +f1 f2 +2003-04-05 2003-04-05 10:11:12.101010 +Warnings: +Note 1292 Truncated wrong date value: '2003-04-05 g' +Note 1292 Truncated wrong datetime value: '2003-04-05 10:11:12.101010234567' diff --git a/mysql-test/r/func_sapdb.result b/mysql-test/r/func_sapdb.result index 38bd73bca0f..31868261157 100644 --- a/mysql-test/r/func_sapdb.result +++ b/mysql-test/r/func_sapdb.result @@ -198,3 +198,18 @@ NULL NULL NULL NULL 00:00:00 -24:00:00 drop table t1, test; +select addtime("-01:01:01.01", "-23:59:59.1") as a; +a +-25:01:00.110000 +select microsecond("1997-12-31 23:59:59.01") as a; +a +10000 +select microsecond(19971231235959.01) as a; +a +10000 +select date_add("1997-12-31",INTERVAL "10.09" SECOND_MICROSECOND) as a; +a +1997-12-31 00:00:10.090000 +select str_to_date("2003-01-02 10:11:12.0012", "%Y-%m-%d %H:%i:%S.%f"); +str_to_date("2003-01-02 10:11:12.0012", "%Y-%m-%d %H:%i:%S.%f") +2003-01-02 10:11:12.001200 diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index 0998f7b8bcf..060a5d0f1bd 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -506,17 +506,32 @@ last_day('2001-01-01 01:01:01') as f5, last_day(NULL), last_day('2001-02-12'); f1 f2 f3 f4 f5 last_day(NULL) last_day('2001-02-12') 2000-02-29 2002-12-31 NULL 2003-04-30 2001-01-31 NULL 2001-02-28 -create table t1 select last_day('2000-02-05') as a; +create table t1 select last_day('2000-02-05') as a, +from_days(to_days("960101")) as b; describe t1; Field Type Null Key Default Extra a date 0000-00-00 +b date YES NULL select * from t1; -a -2000-02-29 +a b +2000-02-29 1996-01-01 drop table t1; -select last_day('2000-02-05'); -last_day('2000-02-05') -2000-02-29 +select last_day('2000-02-05') as a, +from_days(to_days("960101")) as b; +a b +2000-02-29 1996-01-01 +select date_add(last_day("1997-12-1"), INTERVAL 1 DAY); +date_add(last_day("1997-12-1"), INTERVAL 1 DAY) +1998-01-01 +select length(last_day("1997-12-1")); +length(last_day("1997-12-1")) +10 +select last_day("1997-12-1")+0; +last_day("1997-12-1")+0 +19971231 +select last_day("1997-12-1")+0.0; +last_day("1997-12-1")+0.0 +19971231.0 select strcmp(date_sub(localtimestamp(), interval 3 hour), utc_timestamp())=0; strcmp(date_sub(localtimestamp(), interval 3 hour), utc_timestamp())=0 1 diff --git a/mysql-test/r/type_time.result b/mysql-test/r/type_time.result index 0830179902d..68b56802120 100644 --- a/mysql-test/r/type_time.result +++ b/mysql-test/r/type_time.result @@ -25,9 +25,11 @@ t 36:30:31 insert into t1 values("10.22.22"),(1234567),(123456789),(123456789.10),("10 22:22"),("12.45a"); Warnings: +Note 1292 Truncated wrong time value: '10.22.22' Warning 1264 Data truncated, out of range for column 't' at row 2 Warning 1264 Data truncated, out of range for column 't' at row 3 Warning 1264 Data truncated, out of range for column 't' at row 4 +Note 1292 Truncated wrong time value: '12.45a' select * from t1; t 10:22:33 diff --git a/mysql-test/t/date_formats.test b/mysql-test/t/date_formats.test index 18af3dfb3db..1fc04cb907b 100644 --- a/mysql-test/t/date_formats.test +++ b/mysql-test/t/date_formats.test @@ -124,7 +124,7 @@ select str_to_date(concat('15-01-2001',' 2:59:58.999'), create table t1 (date char(30), format char(30) not null); insert into t1 values ('2003-01-02 10:11:12', '%Y-%m-%d %H:%i:%S'), -('03-01-02 8:11:2.123456', '%y-%m-%d %H:%i:%S'), +('03-01-02 8:11:2.123456', '%y-%m-%d %H:%i:%S.%#'), ('2003-01-02 10:11:12 PM', '%Y-%m-%d %h:%i:%S %p'), ('2003-01-02 01:11:12.12345AM', '%Y-%m-%d %h:%i:%S.%f%p'), ('2003-01-02 02:11:12.12345AM', '%Y-%m-%d %h:%i:%S.%f %p'), @@ -209,3 +209,32 @@ create table t1 (d date); insert into t1 values ('2004-07-14'),('2005-07-14'); select date_format(d,"%d") from t1 order by 1; drop table t1; + +select str_to_date("2003-....01ABCD-02 10:11:12.0012", "%Y-%.%m%@-%d %H:%i:%S.%f") as a; + + +create table t1 select str_to_date("2003-01-02 10:11:12.0012", "%Y-%m-%d %H:%i:%S.%f") as f1, + str_to_date("10:11:12.0012", "%H:%i:%S.%f") as f2, + str_to_date("2003-01-02", "%Y-%m-%d") as f3, + str_to_date("02", "%d") as f4, str_to_date("02 10", "%d %H") as f5; +describe t1; +select * from t1; +drop table t1; + +create table t1 select "02 10" as a, "%d %H" as b; +select str_to_date(a,b) from t1; +create table t2 select str_to_date(a,b) from t1; +describe t2; +select str_to_date("2003-01-02 10:11:12.0012", "%Y-%m-%d %H:%i:%S.%f") as f1, + str_to_date("2003-01-02 10:11:12.0012", "%Y-%m-%d %H:%i:%S") as f2, + str_to_date("2003-01-02", "%Y-%m-%d") as f3, + str_to_date("02 10:11:12", "%d %H:%i:%S.%f") as f4, + str_to_date("02 10:11:12", "%d %H:%i:%S") as f5, + str_to_date("02 10", "%d %f") as f6; +drop table t1, t2; +select str_to_date("2003-01-02 10:11:12.0012ABCD", "%Y-%m-%d %H:%i:%S.%f") as f1, + addtime("-01:01:01.01 GGG", "-23:59:59.1") as f2, + microsecond("1997-12-31 23:59:59.01XXXX") as f3; + +select str_to_date("2003-04-05 g", "%Y-%m-%d") as f1, + str_to_date("2003-04-05 10:11:12.101010234567", "%Y-%m-%d %H:%i:%S.%f") as f2; diff --git a/mysql-test/t/func_sapdb.test b/mysql-test/t/func_sapdb.test index afd84fe9630..24028437fde 100644 --- a/mysql-test/t/func_sapdb.test +++ b/mysql-test/t/func_sapdb.test @@ -97,3 +97,9 @@ SELECT ADDTIME(t1,t2) As ttt, ADDTIME(t2, t3) As qqq from test; SELECT TIMEDIFF(t1,t4) As ttt, TIMEDIFF(t2, t3) As qqq from test; drop table t1, test; + +select addtime("-01:01:01.01", "-23:59:59.1") as a; +select microsecond("1997-12-31 23:59:59.01") as a; +select microsecond(19971231235959.01) as a; +select date_add("1997-12-31",INTERVAL "10.09" SECOND_MICROSECOND) as a; +select str_to_date("2003-01-02 10:11:12.0012", "%Y-%m-%d %H:%i:%S.%f"); diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index 06d0ff3fa1d..6417d5448e5 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -249,12 +249,18 @@ select last_day('2000-02-05') as f1, last_day('2002-12-31') as f2, last_day('2001-01-01 01:01:01') as f5, last_day(NULL), last_day('2001-02-12'); -create table t1 select last_day('2000-02-05') as a; +create table t1 select last_day('2000-02-05') as a, + from_days(to_days("960101")) as b; describe t1; select * from t1; drop table t1; -select last_day('2000-02-05'); +select last_day('2000-02-05') as a, + from_days(to_days("960101")) as b; +select date_add(last_day("1997-12-1"), INTERVAL 1 DAY); +select length(last_day("1997-12-1")); +select last_day("1997-12-1")+0; +select last_day("1997-12-1")+0.0; # Test SAPDB UTC_% functions. This part is TZ dependant (It is supposed that # TZ variable set to GMT-3 diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 32fbe192d8f..d0674411fcf 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -48,11 +48,6 @@ TYPELIB day_names_typelib= { array_elements(day_names)-1,"", day_names}; -enum date_time_format_types -{ - TIME_ONLY= 0, TIME_MICROSECOND, DATE_ONLY, DATE_TIME, DATE_TIME_MICROSECOND -}; - /* OPTIMIZATION TODO: - Replace the switch with a function that should be called for each @@ -128,6 +123,9 @@ static bool make_datetime(date_time_format_types format, TIME *ltime, val String to decode length Length of string l_time Store result here + cached_timestamp_type + It uses to get an appropriate warning + in the case when the value is truncated. RETURN 0 ok @@ -135,7 +133,8 @@ static bool make_datetime(date_time_format_types format, TIME *ltime, */ static bool extract_date_time(DATE_TIME_FORMAT *format, - const char *val, uint length, TIME *l_time) + const char *val, uint length, TIME *l_time, + timestamp_type cached_timestamp_type) { int weekday= 0, yearday= 0, daypart= 0; int week_number= -1; @@ -143,9 +142,11 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, int error= 0; bool usa_time= 0; bool sunday_first= 0; + int frac_part; + const char *val_begin= val; const char *val_end= val + length; const char *ptr= format->format.str; - const char *end= ptr+ format->format.length; + const char *end= ptr + format->format.length; DBUG_ENTER("extract_date_time"); bzero((char*) l_time, sizeof(*l_time)); @@ -235,7 +236,12 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, /* Second part */ case 'f': tmp= (char*) val_end; + if (tmp - val > 6) + tmp= (char*) val + 6; l_time->second_part= (int) my_strtoll10(val, &tmp, &error); + frac_part= 6 - (tmp - val); + if (frac_part > 0) + l_time->second_part*= (ulong) log_10_int[frac_part]; val= tmp; break; @@ -251,6 +257,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, (const uchar *) val, 2, (const uchar *) "AM", 2)) goto err; + val+= 2; break; /* Exotic things */ @@ -281,6 +288,18 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, val= tmp; break; + case '.': + while (my_ispunct(cs, *val) && val != val_end) + val++; + break; + case '@': + while (my_isalpha(cs, *val) && val != val_end) + val++; + break; + case '#': + while (my_isdigit(cs, *val) && val != val_end) + val++; + break; default: goto err; } @@ -348,6 +367,18 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, l_time->minute > 59 || l_time->second > 59) goto err; + if (val != val_end) + { + do + { + if (!my_isspace(&my_charset_latin1,*val)) + { + make_truncated_value_warning(current_thd, val_begin, length, + cached_timestamp_type); + break; + } + } while (++val != val_end); + } DBUG_RETURN(0); err: @@ -584,16 +615,27 @@ bool make_date_time(DATE_TIME_FORMAT *format, TIME *l_time, /* -** Get a array of positive numbers from a string object. -** Each number is separated by 1 non digit character -** Return error if there is too many numbers. -** If there is too few numbers, assume that the numbers are left out -** from the high end. This allows one to give: -** DAY_TO_SECOND as "D MM:HH:SS", "MM:HH:SS" "HH:SS" or as seconds. + Get a array of positive numbers from a string object. + Each number is separated by 1 non digit character + Return error if there is too many numbers. + If there is too few numbers, assume that the numbers are left out + from the high end. This allows one to give: + DAY_TO_SECOND as "D MM:HH:SS", "MM:HH:SS" "HH:SS" or as seconds. + + SYNOPSIS + str: string value + length: length of str + cs: charset of str + values: array of results + count: count of elements in result array + transform_msec: if value is true we suppose + that the last part of string value is microseconds + and we should transform value to six digit value. + For example, '1.1' -> '1.100000' */ bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs, - uint count, long *values) + uint count, long *values, bool transform_msec) { const char *end=str+length; uint i; @@ -603,8 +645,15 @@ bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs, for (i=0 ; i < count ; i++) { long value; + const char *start= str; for (value=0; str != end && my_isdigit(cs,*str) ; str++) value=value*10L + (long) (*str - '0'); + if (transform_msec && i == count - 1) // microseconds always last + { + long msec_length= 6 - (str - start); + if (msec_length > 0) + value*= (long) log_10_int[msec_length]; + } values[i]= value; while (str != end && !my_isdigit(cs,*str)) str++; @@ -925,19 +974,19 @@ static bool get_interval_value(Item *args,interval_type int_type, interval->second=value; break; case INTERVAL_YEAR_MONTH: // Allow YEAR-MONTH YYYYYMM - if (get_interval_info(str,length,cs,2,array)) + if (get_interval_info(str,length,cs,2,array,0)) return (1); interval->year=array[0]; interval->month=array[1]; break; case INTERVAL_DAY_HOUR: - if (get_interval_info(str,length,cs,2,array)) + if (get_interval_info(str,length,cs,2,array,0)) return (1); interval->day=array[0]; interval->hour=array[1]; break; case INTERVAL_DAY_MICROSECOND: - if (get_interval_info(str,length,cs,5,array)) + if (get_interval_info(str,length,cs,5,array,1)) return (1); interval->day=array[0]; interval->hour=array[1]; @@ -946,14 +995,14 @@ static bool get_interval_value(Item *args,interval_type int_type, interval->second_part=array[4]; break; case INTERVAL_DAY_MINUTE: - if (get_interval_info(str,length,cs,3,array)) + if (get_interval_info(str,length,cs,3,array,0)) return (1); interval->day=array[0]; interval->hour=array[1]; interval->minute=array[2]; break; case INTERVAL_DAY_SECOND: - if (get_interval_info(str,length,cs,4,array)) + if (get_interval_info(str,length,cs,4,array,0)) return (1); interval->day=array[0]; interval->hour=array[1]; @@ -961,7 +1010,7 @@ static bool get_interval_value(Item *args,interval_type int_type, interval->second=array[3]; break; case INTERVAL_HOUR_MICROSECOND: - if (get_interval_info(str,length,cs,4,array)) + if (get_interval_info(str,length,cs,4,array,1)) return (1); interval->hour=array[0]; interval->minute=array[1]; @@ -969,33 +1018,33 @@ static bool get_interval_value(Item *args,interval_type int_type, interval->second_part=array[3]; break; case INTERVAL_HOUR_MINUTE: - if (get_interval_info(str,length,cs,2,array)) + if (get_interval_info(str,length,cs,2,array,0)) return (1); interval->hour=array[0]; interval->minute=array[1]; break; case INTERVAL_HOUR_SECOND: - if (get_interval_info(str,length,cs,3,array)) + if (get_interval_info(str,length,cs,3,array,0)) return (1); interval->hour=array[0]; interval->minute=array[1]; interval->second=array[2]; break; case INTERVAL_MINUTE_MICROSECOND: - if (get_interval_info(str,length,cs,3,array)) + if (get_interval_info(str,length,cs,3,array,1)) return (1); interval->minute=array[0]; interval->second=array[1]; interval->second_part=array[2]; break; case INTERVAL_MINUTE_SECOND: - if (get_interval_info(str,length,cs,2,array)) + if (get_interval_info(str,length,cs,2,array,0)) return (1); interval->minute=array[0]; interval->second=array[1]; break; case INTERVAL_SECOND_MICROSECOND: - if (get_interval_info(str,length,cs,2,array)) + if (get_interval_info(str,length,cs,2,array,1)) return (1); interval->second=array[0]; interval->second_part=array[1]; @@ -1008,22 +1057,13 @@ static bool get_interval_value(Item *args,interval_type int_type, String *Item_date::val_str(String *str) { TIME ltime; - ulong value=(ulong) val_int(); - if (null_value) - return (String*) 0; - + if (get_date(<ime, TIME_FUZZY_DATE)) + return (String *) 0; if (str->alloc(11)) { null_value= 1; return (String *) 0; } - - ltime.year= (value/10000L) % 10000; - ltime.month= (value/100)%100; - ltime.day= (value%100); - ltime.neg= 0; - ltime.time_type=TIMESTAMP_DATE; - make_date((DATE_TIME_FORMAT *) 0, <ime, str); return str; } @@ -1032,28 +1072,31 @@ String *Item_date::val_str(String *str) int Item_date::save_in_field(Field *field, bool no_conversions) { TIME ltime; - timestamp_type t_type=TIMESTAMP_DATETIME; if (get_date(<ime, TIME_FUZZY_DATE)) - { - if (null_value) - return set_field_to_null(field); - t_type=TIMESTAMP_NONE; // Error - } + return set_field_to_null(field); field->set_notnull(); - field->store_time(<ime,t_type); + field->store_time(<ime, TIMESTAMP_DATE); return 0; } -longlong Item_func_from_days::val_int() +longlong Item_date::val_int() +{ + TIME ltime; + if (get_date(<ime, TIME_FUZZY_DATE)) + return 0; + return (longlong) (ltime.year*10000L+ltime.month*100+ltime.day); +} + + +bool Item_func_from_days::get_date(TIME *ltime, uint fuzzy_date) { longlong value=args[0]->val_int(); if ((null_value=args[0]->null_value)) - return 0; /* purecov: inspected */ - - uint year,month,day; - get_date_from_daynr((long) value,&year,&month,&day); - return (longlong) (year*10000L+month*100+day); + return 1; + get_date_from_daynr((long) value, <ime->year, <ime->month, <ime->day); + ltime->time_type= TIMESTAMP_DATE; + return 0; } @@ -1082,6 +1125,16 @@ void Item_func_curdate::fix_length_and_dec() ltime.time_type=TIMESTAMP_DATE; } +String *Item_func_curdate::val_str(String *str) +{ + if (str->alloc(11)) + { + null_value= 1; + return (String *) 0; + } + make_date((DATE_TIME_FORMAT *) 0, <ime, str); + return str; +} bool Item_func_curdate::get_date(TIME *res, uint fuzzy_date __attribute__((unused))) @@ -2311,6 +2364,103 @@ void Item_func_get_format::print(String *str) } +/* + check_result_type(s, l) returns DATE/TIME type + according to format string + + s: DATE/TIME format string + l: length of s + Result: date_time_format_types value: + DATE_TIME_MICROSECOND, DATE_TIME, + TIME_MICROSECOND, TIME_ONLY + + We don't process day format's characters('D', 'd', 'e') + because day may be a member of all date/time types. + If only day format's character and no time part present + the result type is MYSQL_TYPE_DATE +*/ + +date_time_format_types check_result_type(const char *format, uint length) +{ + const char *time_part_frms= "HISThiklrs"; + const char *date_part_frms= "MUYWabcjmuyw"; + bool date_part_used= 0, time_part_used= 0, frac_second_used= 0; + + const char *val= format; + const char *end= format + length; + + for (; val != end && val != end; val++) + { + if (*val == '%' && val+1 != end) + { + val++; + if ((frac_second_used= (*val == 'f')) || + (!time_part_used && strchr(time_part_frms, *val))) + time_part_used= 1; + else if (!date_part_used && strchr(date_part_frms, *val)) + date_part_used= 1; + if (time_part_used && date_part_used && frac_second_used) + return DATE_TIME_MICROSECOND; + } + } + + if (time_part_used) + { + if (date_part_used) + return DATE_TIME; + if (frac_second_used) + return TIME_MICROSECOND; + return TIME_ONLY; + } + return DATE_ONLY; +} + + +Field *Item_func_str_to_date::tmp_table_field(TABLE *t_arg) +{ + if (cached_field_type == MYSQL_TYPE_TIME) + return (new Field_time(maybe_null, name, t_arg, &my_charset_bin)); + if (cached_field_type == MYSQL_TYPE_DATE) + return (new Field_date(maybe_null, name, t_arg, &my_charset_bin)); + if (cached_field_type == MYSQL_TYPE_DATETIME) + return (new Field_datetime(maybe_null, name, t_arg, &my_charset_bin)); + return (new Field_string(max_length, maybe_null, name, t_arg, &my_charset_bin)); +} + + +void Item_func_str_to_date::fix_length_and_dec() +{ + char format_buff[64]; + String format_str(format_buff, sizeof(format_buff), &my_charset_bin), *format; + maybe_null= 1; + decimals=0; + cached_field_type= MYSQL_TYPE_STRING; + max_length= MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + cached_timestamp_type= TIMESTAMP_NONE; + if ((const_item= args[1]->const_item())) + { + format= args[1]->val_str(&format_str); + cached_format_type= check_result_type(format->ptr(), format->length()); + switch (cached_format_type) { + case DATE_ONLY: + cached_timestamp_type= TIMESTAMP_DATE; + cached_field_type= MYSQL_TYPE_DATE; + max_length= MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + break; + case TIME_ONLY: + case TIME_MICROSECOND: + cached_timestamp_type= TIMESTAMP_TIME; + cached_field_type= MYSQL_TYPE_TIME; + max_length= MAX_TIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + break; + default: + cached_timestamp_type= TIMESTAMP_DATETIME; + cached_field_type= MYSQL_TYPE_DATETIME; + break; + } + } +} + bool Item_func_str_to_date::get_date(TIME *ltime, uint fuzzy_date) { DATE_TIME_FORMAT date_time_format; @@ -2328,8 +2478,18 @@ bool Item_func_str_to_date::get_date(TIME *ltime, uint fuzzy_date) date_time_format.format.str= (char*) format->ptr(); date_time_format.format.length= format->length(); if (extract_date_time(&date_time_format, val->ptr(), val->length(), - ltime)) + ltime, cached_timestamp_type)) goto null_date; + if (cached_timestamp_type == TIMESTAMP_TIME && ltime->day) + { + /* + Day part for time type can be nonzero value and so + we should add hours from day part to hour part to + keep valid time value. + */ + ltime->hour+= ltime->day*24; + ltime->day= 0; + } return 0; null_date: @@ -2344,29 +2504,22 @@ String *Item_func_str_to_date::val_str(String *str) if (Item_func_str_to_date::get_date(<ime, TIME_FUZZY_DATE)) return 0; - /* - The following DATE_TIME should be done dynamicly based on the - format string (wen it's a constant). For example, we should only return - microseconds if there was an %f in the format - */ - if (!make_datetime(ltime.second_part ? DATE_TIME_MICROSECOND : DATE_TIME, + if (!make_datetime((const_item ? cached_format_type : + (ltime.second_part ? DATE_TIME_MICROSECOND : DATE_TIME)), <ime, str)) return str; return 0; } -String *Item_func_last_day::val_str(String *str) +bool Item_func_last_day::get_date(TIME *ltime, uint fuzzy_date) { - TIME ltime; - if (!get_arg0_date(<ime,0)) - { - uint month_idx= ltime.month-1; - ltime.day= days_in_month[month_idx]; - if ( month_idx == 1 && calc_days_in_year(ltime.year) == 366) - ltime.day+= 1; - if (!make_datetime(DATE_ONLY, <ime, str)) - return str; - } + if (get_arg0_date(ltime,fuzzy_date)) + return 1; + uint month_idx= ltime->month-1; + ltime->day= days_in_month[month_idx]; + if ( month_idx == 1 && calc_days_in_year(ltime->year) == 366) + ltime->day= 29; + ltime->time_type= TIMESTAMP_DATE; return 0; } diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 854a54bbe80..1b044b49fb1 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -21,6 +21,11 @@ #pragma interface /* gcc class implementation */ #endif +enum date_time_format_types +{ + TIME_ONLY= 0, TIME_MICROSECOND, DATE_ONLY, DATE_TIME, DATE_TIME_MICROSECOND +}; + class Item_func_period_add :public Item_int_func { public: @@ -318,6 +323,7 @@ public: enum Item_result result_type () const { return STRING_RESULT; } enum_field_types field_type() const { return MYSQL_TYPE_DATE; } String *val_str(String *str); + longlong val_int(); double val() { return (double) val_int(); } const char *func_name() const { return "date"; } void fix_length_and_dec() @@ -407,6 +413,7 @@ public: Item_func_curdate() :Item_date() {} void set_result_from_tm(struct tm *now); longlong val_int() { return (value) ; } + String *val_str(String *str); void fix_length_and_dec(); bool get_date(TIME *res, uint fuzzy_date); virtual void store_now_in_tm(time_t now, struct tm *now_tm)=0; @@ -477,8 +484,8 @@ class Item_func_from_days :public Item_date { public: Item_func_from_days(Item *a) :Item_date(a) {} - longlong val_int(); const char *func_name() const { return "from_days"; } + bool get_date(TIME *res, uint fuzzy_date); }; @@ -806,37 +813,29 @@ public: }; -class Item_func_str_to_date :public Item_date_func +class Item_func_str_to_date :public Item_str_func { + enum_field_types cached_field_type; + date_time_format_types cached_format_type; + timestamp_type cached_timestamp_type; + bool const_item; public: Item_func_str_to_date(Item *a, Item *b) - :Item_date_func(a, b) + :Item_str_func(a, b) {} String *val_str(String *str); bool get_date(TIME *ltime, uint fuzzy_date); const char *func_name() const { return "str_to_date"; } - void fix_length_and_dec() - { - maybe_null= 1; - decimals=0; - max_length=MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - } + enum_field_types field_type() const { return cached_field_type; } + void fix_length_and_dec(); + Field *tmp_table_field(TABLE *t_arg); }; -class Item_func_last_day :public Item_str_func + +class Item_func_last_day :public Item_date { public: - Item_func_last_day(Item *a) :Item_str_func(a) {} - String *val_str(String *str); + Item_func_last_day(Item *a) :Item_date(a) {} const char *func_name() const { return "last_day"; } - enum_field_types field_type() const { return MYSQL_TYPE_DATE; } - void fix_length_and_dec() - { - decimals=0; - max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - } - Field *tmp_table_field(TABLE *t_arg) - { - return (new Field_date(maybe_null, name, t_arg, &my_charset_bin)); - } + bool get_date(TIME *res, uint fuzzy_date); }; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index eb879a1fd59..9fd7e6f5b93 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -799,6 +799,7 @@ extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN]; extern char pidfile_name[FN_REFLEN], time_zone[30], *opt_init_file; extern char log_error_file[FN_REFLEN]; extern double log_10[32]; +extern ulonglong log_10_int[20]; extern ulonglong keybuff_size; extern ulong refresh_version,flush_version, thread_id,query_id,opened_tables; extern ulong created_tmp_tables, created_tmp_disk_tables, bytes_sent; @@ -958,6 +959,8 @@ timestamp_type str_to_TIME(const char *str, uint length, TIME *l_time, void localtime_to_TIME(TIME *to, struct tm *from); void calc_time_from_sec(TIME *to, long seconds, long microseconds); +void make_truncated_value_warning(THD *thd, const char *str_val, + uint str_length, timestamp_type time_type); extern DATE_TIME_FORMAT *date_time_format_make(timestamp_type format_type, const char *format_str, uint format_length); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 5b6c592cd51..9a469e529cf 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -305,6 +305,14 @@ ulong my_bind_addr; /* the address we bind to */ volatile ulong cached_thread_count= 0; double log_10[32]; /* 10 potences */ +ulonglong log_10_int[20]= +{ + 1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL, + 100000000UL, 1000000000UL, 10000000000UL, 100000000000UL, + 1000000000000UL, 10000000000000UL, 100000000000000UL, + 1000000000000000UL, 10000000000000000UL, 100000000000000000UL, + 1000000000000000000UL, 10000000000000000000UL +}; time_t start_time; diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 3a2d7a44c44..2d2a4472d9d 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -304,3 +304,4 @@ character-set=latin2 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index 917ea3bf407..ca7c67552c3 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -298,3 +298,4 @@ character-set=latin1 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 16cf2fd15de..2cb9e7e511b 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -306,3 +306,4 @@ character-set=latin1 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 8c4e0530ed4..4e0da64408e 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -295,3 +295,4 @@ character-set=latin1 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index 8c7cc53ef06..8b912d10e97 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -300,3 +300,4 @@ character-set=latin7 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index 78d729fc9f8..ef7385422fb 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -295,3 +295,4 @@ character-set=latin1 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 142076a1f14..60454d0d011 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -307,3 +307,4 @@ character-set=latin1 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index b9f47a54b35..4f695465bf5 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -295,3 +295,4 @@ character-set=greek "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index 9deb3da88a2..fdd48f38ef1 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -297,3 +297,4 @@ character-set=latin2 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index 9b5a081ec9d..b88117c0972 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -295,3 +295,4 @@ character-set=latin1 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index 1a17277cb90..c1b38e9ecbb 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -297,3 +297,4 @@ character-set=ujis "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index 9b07afd16c9..932243eca38 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -295,3 +295,4 @@ character-set=euckr "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index aaba0f1afbe..05b55a582bd 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -297,3 +297,4 @@ character-set=latin1 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index f205e07b3bb..c11e529efd5 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -297,3 +297,4 @@ character-set=latin1 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 2c942d40f80..319deedc321 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -299,3 +299,4 @@ character-set=latin2 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index f3a8a484696..5151e612813 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -296,3 +296,4 @@ character-set=latin1 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index 72df2447f99..2c6ec81cc1d 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -299,3 +299,4 @@ character-set=latin2 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index 3d37b2d60ce..00e931eb65a 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -297,3 +297,4 @@ character-set=koi8r "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index c68f9538dd2..dc15bbf4977 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -289,3 +289,4 @@ character-set=cp1250 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working" "The MySQL server is running with the %s option so it cannot execute this statement" "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index 35263024bb8..00b0dd9a0ae 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -303,3 +303,4 @@ character-set=latin2 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index 3e0f67b453a..cb5d5b6df28 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -297,3 +297,4 @@ character-set=latin1 "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index dc6759f91e0..5c60519e217 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -295,3 +295,4 @@ character-set=latin1 "MySQL är started i --skip-grant-tables mod. Pga av detta kan du inte använda detta program", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index fbf31744dca..7dd694e75ac 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -300,3 +300,4 @@ character-set=koi8u "The '%s' feature was disabled; you need MySQL built with '%s' to have it working", "The MySQL server is running with the %s option so it cannot execute this statement", "Column '%-.100s' has duplicated value '%-.64s' in %s" +"Truncated wrong %-.32s value: '%-.128s'" diff --git a/sql/time.cc b/sql/time.cc index 1dff0c62edf..376ad6926b8 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -391,9 +391,11 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) ulong not_zero_date, allow_space; bool is_internal_format; const char *pos, *last_field_pos; + const char *str_begin= str; const char *end=str+length; const uchar *format_position; bool found_delimitier= 0, found_space= 0; + uint frac_pos, frac_len; DBUG_ENTER("str_to_TIME"); DBUG_PRINT("ENTER",("str: %.*s",length,str)); @@ -482,7 +484,7 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) tmp_value=tmp_value*10 + (ulong) (uchar) (*str - '0'); str++; } - date_len[i]+= (uint) (str - start); + date_len[i]= (uint) (str - start); if (tmp_value > 999999) // Impossible date part DBUG_RETURN(TIMESTAMP_NONE); date[i]=tmp_value; @@ -535,9 +537,9 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) { if (str+2 <= end && (str[1] == 'M' || str[1] == 'm')) { - if (str[1] == 'p' || str[1] == 'P') + if (str[0] == 'p' || str[0] == 'P') add_hours= 12; - else if (str[1] != 'a' || str[1] != 'A') + else if (str[0] != 'a' || str[0] != 'A') continue; // Not AM/PM str+= 2; // Skip AM/PM /* Skip space after AM/PM */ @@ -569,7 +571,13 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) l_time->hour= date[(uint) format_position[3]]; l_time->minute= date[(uint) format_position[4]]; l_time->second= date[(uint) format_position[5]]; - l_time->second_part= date[(uint) format_position[6]]; + + frac_pos= (uint) format_position[6]; + frac_len= date_len[frac_pos]; + if (frac_len < 6) + date[frac_pos]*= (uint) log_10_int[6 - frac_len]; + l_time->second_part= date[frac_pos]; + if (format_position[7] != (uchar) 255) { if (l_time->hour > 12) @@ -585,6 +593,8 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) l_time->hour= date[3]; l_time->minute= date[4]; l_time->second= date[5]; + if (date_len[6] < 6) + date[6]*= (uint) log_10_int[6 - date_len[6]]; l_time->second_part=date[6]; } l_time->neg= 0; @@ -614,15 +624,17 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) current_thd->cuted_fields++; goto err; } - if (str != end && current_thd->count_cuted_fields) + + l_time->time_type= (number_of_fields <= 3 ? + TIMESTAMP_DATE : TIMESTAMP_DATETIME); + + for (; str != end ; str++) { - for (; str != end ; str++) + if (!my_isspace(&my_charset_latin1,*str)) { - if (!my_isspace(&my_charset_latin1,*str)) - { - current_thd->cuted_fields++; - break; - } + make_truncated_value_warning(current_thd, str_begin, length, + l_time->time_type); + break; } } @@ -686,6 +698,7 @@ bool str_to_time(const char *str,uint length,TIME *l_time) { long date[5],value; const char *end=str+length, *end_of_days; + const char *str_begin= str; bool found_days,found_hours; uint state; @@ -706,7 +719,7 @@ bool str_to_time(const char *str,uint length,TIME *l_time) { // Probably full timestamp enum timestamp_type res= str_to_TIME(str,length,l_time, (TIME_FUZZY_DATE | - TIME_DATETIME_ONLY)); + TIME_DATETIME_ONLY)); if ((int) res >= (int) TIMESTAMP_DATETIME_ERROR) return res == TIMESTAMP_DATETIME_ERROR; } @@ -784,6 +797,8 @@ fractional: my_isdigit(&my_charset_latin1,str[0]) && field_length--) value=value*10 + (uint) (uchar) (*str - '0'); + if (field_length) + value*= (long) log_10_int[field_length]; date[4]=value; } else @@ -796,12 +811,12 @@ fractional: str++; if (str+2 <= end && (str[1] == 'M' || str[1] == 'm')) { - if (str[1] == 'p' || str[1] == 'P') + if (str[0] == 'p' || str[0] == 'P') { str+= 2; date[1]= date[1]%12 + 12; } - else if (str[1] == 'a' || str[1] == 'A') + else if (str[0] == 'a' || str[0] == 'A') str+=2; } } @@ -822,13 +837,14 @@ fractional: l_time->time_type= TIMESTAMP_TIME; /* Check if there is garbage at end of the TIME specification */ - if (str != end && current_thd->count_cuted_fields) + if (str != end) { do { if (!my_isspace(&my_charset_latin1,*str)) { - current_thd->cuted_fields++; + make_truncated_value_warning(current_thd, str_begin, length, + TIMESTAMP_TIME); break; } } while (++str != end); @@ -1265,3 +1281,35 @@ void make_datetime(DATE_TIME_FORMAT *format, TIME *l_time, String *str) str->length(length); str->set_charset(&my_charset_bin); } + +void make_truncated_value_warning(THD *thd, const char *str_val, + uint str_length, timestamp_type time_type) +{ + char warn_buff[MYSQL_ERRMSG_SIZE]; + const char *type_str; + + char buff[128]; + String str(buff,(uint32) sizeof(buff), system_charset_info); + str.length(0); + str.append(str_val, str_length); + str.append('\0'); + + switch (time_type) { + case TIMESTAMP_DATE: + type_str= "date"; + break; + case TIMESTAMP_DATETIME: + type_str= "datetime"; + break; + case TIMESTAMP_TIME: + type_str= "time"; + break; + default: + type_str= "string"; + break; + } + sprintf(warn_buff, ER(ER_TRUNCATED_WRONG_VALUE), + type_str, str.ptr()); + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_TRUNCATED_WRONG_VALUE, warn_buff); +} From 60354533c1156472d5433e15dd295ee76a9b8ee5 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 15 Mar 2004 19:18:30 +0400 Subject: [PATCH 3/9] Fix for bug#2972 Problem the the CONV() function --- mysql-test/r/func_str.result | 3 +++ mysql-test/t/func_str.test | 7 +++++++ strings/ctype-simple.c | 16 ++++++++-------- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index eb09a746a1c..86c4b8cf0dc 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -624,6 +624,9 @@ Note 1003 select high_priority md5(_latin1'hello') AS `md5('hello')`,sha(_latin1 SELECT lpad(12345, 5, "#"); lpad(12345, 5, "#") 12345 +SELECT conv(71, 10, 36), conv('1Z', 36, 10); +conv(71, 10, 36) conv('1Z', 36, 10) +1Z 71 create table t1 (id int(1), str varchar(10)) DEFAULT CHARSET=utf8; insert into t1 values (1,'aaaaaaaaaa'), (2,'bbbbbbbbbb'); create table t2 (id int(1), str varchar(10)) DEFAULT CHARSET=utf8; diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index c9ead51c0a6..1e6d279f721 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -361,6 +361,13 @@ explain extended select md5('hello'), sha('abc'), sha1('abc'), soundex(''), 'moo SELECT lpad(12345, 5, "#"); +# +# Bug #2972 +# + +SELECT conv(71, 10, 36), conv('1Z', 36, 10); + + # # Bug #3089 diff --git a/strings/ctype-simple.c b/strings/ctype-simple.c index 719ae14f653..6e7d0b849e2 100644 --- a/strings/ctype-simple.c +++ b/strings/ctype-simple.c @@ -262,9 +262,9 @@ long my_strntol_8bit(CHARSET_INFO *cs, { if (c>='0' && c<='9') c -= '0'; - else if (c>='A' && c<='F') + else if (c>='A' && c<='Z') c = c - 'A' + 10; - else if (c>='a' && c<='f') + else if (c>='a' && c<='z') c = c - 'a' + 10; else break; @@ -384,9 +384,9 @@ ulong my_strntoul_8bit(CHARSET_INFO *cs, { if (c>='0' && c<='9') c -= '0'; - else if (c>='A' && c<='F') + else if (c>='A' && c<='Z') c = c - 'A' + 10; - else if (c>='a' && c<='f') + else if (c>='a' && c<='z') c = c - 'a' + 10; else break; @@ -499,9 +499,9 @@ longlong my_strntoll_8bit(CHARSET_INFO *cs __attribute__((unused)), { if (c>='0' && c<='9') c -= '0'; - else if (c>='A' && c<='F') + else if (c>='A' && c<='Z') c = c - 'A' + 10; - else if (c>='a' && c<='f') + else if (c>='a' && c<='z') c = c - 'a' + 10; else break; @@ -622,9 +622,9 @@ ulonglong my_strntoull_8bit(CHARSET_INFO *cs, { if (c>='0' && c<='9') c -= '0'; - else if (c>='A' && c<='F') + else if (c>='A' && c<='Z') c = c - 'A' + 10; - else if (c>='a' && c<='f') + else if (c>='a' && c<='z') c = c - 'a' + 10; else break; From 5148b005111200b45672f1de30a16f7d902cc8ca Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 15 Mar 2004 19:47:51 +0400 Subject: [PATCH 4/9] Fix for bug#3041 --safe-updates option declaration anomaly --- client/mysql.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/mysql.cc b/client/mysql.cc index 5d69d3838ee..82925400c44 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -619,10 +619,10 @@ static struct my_option my_long_options[] = (gptr*) ¤t_user, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #endif {"safe-updates", 'U', "Only allow UPDATE and DELETE that uses keys.", - (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, OPT_ARG, 0, 0, + (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"i-am-a-dummy", 'U', "Synonym for option --safe-updates, -U.", - (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, OPT_ARG, 0, 0, + (gptr*) &safe_updates, (gptr*) &safe_updates, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"verbose", 'v', "Write more. (-v -v -v gives the table output format).", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, From ef44fb9af3ee729de5897f290c13437bbbc09da5 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 15 Mar 2004 20:20:47 +0300 Subject: [PATCH 5/9] Fixes for bugs #2274 "mysqld gets SIGSEGV during processing of malformed COM_EXECUTE packet" and #2795 "prepare + execute without bind_param crashes server" and #2473 "seg fault running tests/client_test.c": - length checking added to packet parser - default impelemntation of Item_param::set_param_func will work in case of malformed packet. No test cases are possible in our test suite, as there are no tests operating on protocol layer. sql/item.cc: Default set_param function implemented: this is to not sigsegv in case of malformed packet with no parameters data. sql/item.h: - Item_param constructor moved to .cc to be able to assign set_param_func. - now embedded and ordinary versions of set_param have the same signature. sql/mysql_priv.h: mysql_stmt_execute now requires packet_length sql/sql_parse.cc: mysql_stmt_execute now requires packet length. sql/sql_prepare.cc: - length checking added to all functions working with network packet. - set_param_func's in embedded and ordinary version now have the same signature --- sql/item.cc | 28 ++++++++++- sql/item.h | 24 ++++------ sql/mysql_priv.h | 2 +- sql/sql_parse.cc | 2 +- sql/sql_prepare.cc | 115 ++++++++++++++++++++++++++++++--------------- 5 files changed, 114 insertions(+), 57 deletions(-) diff --git a/sql/item.cc b/sql/item.cc index 5aaeffff5d2..bd3b89b396b 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -517,7 +517,33 @@ String *Item_null::val_str(String *str) { null_value=1; return 0;} -/* Item_param related */ +/*********************** Item_param related ******************************/ + +/* + Default function of Item_param::set_param_func, so in case + of malformed packet the server won't SIGSEGV +*/ + +static void +default_set_param_func(Item_param *param, + uchar **pos __attribute__((unused)), + ulong len __attribute__((unused))) +{ + param->set_null(); +} + +Item_param::Item_param(unsigned position) : + value_is_set(FALSE), + item_result_type(STRING_RESULT), + item_type(STRING_ITEM), + item_is_time(FALSE), + long_data_supplied(FALSE), + pos_in_query(position), + set_param_func(default_set_param_func) +{ + name= (char*) "?"; +} + void Item_param::set_null() { DBUG_ENTER("Item_param::set_null"); diff --git a/sql/item.h b/sql/item.h index 727132916f0..832fc775016 100644 --- a/sql/item.h +++ b/sql/item.h @@ -348,16 +348,7 @@ public: bool long_data_supplied; uint pos_in_query; - Item_param(uint position) - { - name= (char*) "?"; - pos_in_query= position; - item_type= STRING_ITEM; - item_result_type = STRING_RESULT; - item_is_time= false; - long_data_supplied= false; - value_is_set= 0; - } + Item_param(uint position); enum Type type() const { return item_type; } double val(); longlong val_int(); @@ -374,11 +365,14 @@ public: void set_time(TIME *tm, timestamp_type type); bool get_time(TIME *tm); void reset() {} -#ifndef EMBEDDED_LIBRARY - void (*set_param_func)(Item_param *param, uchar **pos); -#else - void (*set_param_func)(Item_param *param, uchar **pos, ulong data_len); -#endif + /* + Assign placeholder value from bind data. + Note, that 'len' has different semantics in embedded library (as we + don't need to check that packet is not broken there). See + sql_prepare.cc for details. + */ + void (*set_param_func)(Item_param *param, uchar **pos, ulong len); + enum Item_result result_type () const { return item_result_type; } String *query_val_str(String *str); diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 9fd7e6f5b93..8696a09962c 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -621,7 +621,7 @@ int mysqld_help (THD *thd, const char *text); /* sql_prepare.cc */ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length); -void mysql_stmt_execute(THD *thd, char *packet); +void mysql_stmt_execute(THD *thd, char *packet, uint packet_length); void mysql_stmt_free(THD *thd, char *packet); void mysql_stmt_reset(THD *thd, char *packet); void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 97bae472757..3100737aaa5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1405,7 +1405,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } case COM_EXECUTE: { - mysql_stmt_execute(thd, packet); + mysql_stmt_execute(thd, packet, packet_length); break; } case COM_LONG_DATA: diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index ac5b7847647..ab136668cfb 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -94,7 +94,8 @@ public: bool long_data_used; bool log_full_query; #ifndef EMBEDDED_LIBRARY - bool (*set_params)(Prepared_statement *st, uchar *pos, uchar *read_pos); + bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end, + uchar *read_pos); #else bool (*set_params_data)(Prepared_statement *st); #endif @@ -117,14 +118,6 @@ inline bool is_param_null(const uchar *pos, ulong param_no) enum { STMT_QUERY_LOG_LENGTH= 8192 }; -#ifdef EMBEDDED_LIBRARY -#define SET_PARAM_FUNCTION(fn_name) \ -static void fn_name(Item_param *param, uchar **pos, ulong data_len) -#else -#define SET_PARAM_FUNCTION(fn_name) \ -static void fn_name(Item_param *param, uchar **pos) -#endif - enum enum_send_error { DONT_SEND_ERROR= 0, SEND_ERROR }; /* @@ -186,29 +179,38 @@ static bool send_prep_stmt(Prepared_statement *stmt, */ #ifndef EMBEDDED_LIBRARY -static ulong get_param_length(uchar **packet) +static ulong get_param_length(uchar **packet, ulong len) { reg1 uchar *pos= *packet; + if (len < 1) + return 0; if (*pos < 251) { (*packet)++; return (ulong) *pos; } + if (len < 3) + return 0; if (*pos == 252) { (*packet)+=3; return (ulong) uint2korr(pos+1); } + if (len < 4) + return 0; if (*pos == 253) { (*packet)+=4; return (ulong) uint3korr(pos+1); } + if (len < 5) + return 0; (*packet)+=9; // Must be 254 when here + /* TODO: why uint4korr here? (should be uint8korr) */ return (ulong) uint4korr(pos+1); } #else -#define get_param_length(A) data_len +#define get_param_length(packet, len) len #endif /*!EMBEDDED_LIBRARY*/ /* @@ -230,55 +232,80 @@ static ulong get_param_length(uchar **packet) none */ -SET_PARAM_FUNCTION(set_param_tiny) +void set_param_tiny(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 1) + return; +#endif param->set_int((longlong)(**pos)); *pos+= 1; } -SET_PARAM_FUNCTION(set_param_short) +void set_param_short(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 2) + return; +#endif param->set_int((longlong)sint2korr(*pos)); *pos+= 2; } -SET_PARAM_FUNCTION(set_param_int32) +void set_param_int32(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 4) + return; +#endif param->set_int((longlong)sint4korr(*pos)); *pos+= 4; } -SET_PARAM_FUNCTION(set_param_int64) +void set_param_int64(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 8) + return; +#endif param->set_int((longlong)sint8korr(*pos)); *pos+= 8; } -SET_PARAM_FUNCTION(set_param_float) +void set_param_float(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 4) + return; +#endif float data; float4get(data,*pos); param->set_double((double) data); *pos+= 4; } -SET_PARAM_FUNCTION(set_param_double) +void set_param_double(Item_param *param, uchar **pos, ulong len) { +#ifndef EMBEDDED_LIBRARY + if (len < 8) + return; +#endif double data; float8get(data,*pos); param->set_double((double) data); *pos+= 8; } -SET_PARAM_FUNCTION(set_param_time) +void set_param_time(Item_param *param, uchar **pos, ulong len) { ulong length; - if ((length= get_param_length(pos))) + if ((length= get_param_length(pos, len)) >= 8) { uchar *to= *pos; TIME tm; + /* TODO: why length is compared with 8 here? */ tm.second_part= (length > 8 ) ? (ulong) sint4korr(to+7): 0; tm.day= (ulong) sint4korr(to+1); @@ -294,11 +321,11 @@ SET_PARAM_FUNCTION(set_param_time) *pos+= length; } -SET_PARAM_FUNCTION(set_param_datetime) +void set_param_datetime(Item_param *param, uchar **pos, ulong len) { uint length; - if ((length= get_param_length(pos))) + if ((length= get_param_length(pos, len)) >= 4) { uchar *to= *pos; TIME tm; @@ -324,11 +351,11 @@ SET_PARAM_FUNCTION(set_param_datetime) *pos+= length; } -SET_PARAM_FUNCTION(set_param_date) +void set_param_date(Item_param *param, uchar **pos, ulong len) { ulong length; - if ((length= get_param_length(pos))) + if ((length= get_param_length(pos, len)) >= 4) { uchar *to= *pos; TIME tm; @@ -346,11 +373,11 @@ SET_PARAM_FUNCTION(set_param_date) *pos+= length; } -SET_PARAM_FUNCTION(set_param_str) +void set_param_str(Item_param *param, uchar **pos, ulong len) { - ulong len= get_param_length(pos); - param->set_value((const char *)*pos, len); - *pos+= len; + ulong length= get_param_length(pos, len); + param->set_value((const char *)*pos, length); + *pos+= length; } static void setup_one_conversion_function(Item_param *param, uchar param_type) @@ -405,8 +432,8 @@ static void setup_one_conversion_function(Item_param *param, uchar param_type) and if binary/update log is set, generate the valid query. */ -static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, - uchar *read_pos) +static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array, + uchar *read_pos, uchar *data_end) { THD *thd= stmt->thd; Item_param **begin= stmt->param_array; @@ -428,7 +455,7 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, res= param->query_val_str(&str); else { - if (is_param_null(pos, it - begin)) + if (is_param_null(null_array, it - begin)) { param->maybe_null= param->null_value= 1; res= &my_null_string; @@ -436,7 +463,9 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, else { param->maybe_null= param->null_value= 0; - param->set_param_func(param, &read_pos); + if (read_pos >= data_end) + DBUG_RETURN(1); + param->set_param_func(param, &read_pos, data_end - read_pos); res= param->query_val_str(&str); } } @@ -452,8 +481,8 @@ static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos, } -static bool insert_params(Prepared_statement *stmt, uchar *pos, - uchar *read_pos) +static bool insert_params(Prepared_statement *stmt, uchar *null_array, + uchar *read_pos, uchar *data_end) { Item_param **begin= stmt->param_array; Item_param **end= begin + stmt->param_count; @@ -465,20 +494,23 @@ static bool insert_params(Prepared_statement *stmt, uchar *pos, Item_param *param= *it; if (!param->long_data_supplied) { - if (is_param_null(pos, it - begin)) + if (is_param_null(null_array, it - begin)) param->maybe_null= param->null_value= 1; else { param->maybe_null= param->null_value= 0; - param->set_param_func(param, &read_pos); + if (read_pos >= data_end) + DBUG_RETURN(1); + param->set_param_func(param, &read_pos, data_end - read_pos); } } } DBUG_RETURN(0); } + static bool setup_conversion_functions(Prepared_statement *stmt, - uchar **data) + uchar **data, uchar *data_end) { /* skip null bits */ uchar *read_pos= *data + (stmt->param_count+7) / 8; @@ -495,6 +527,8 @@ static bool setup_conversion_functions(Prepared_statement *stmt, Item_param **end= it + stmt->param_count; for (; it < end; ++it) { + if (read_pos >= data_end) + DBUG_RETURN(1); setup_one_conversion_function(*it, *read_pos); read_pos+= 2; } @@ -1072,7 +1106,7 @@ static void reset_stmt_for_execute(Prepared_statement *stmt) */ -void mysql_stmt_execute(THD *thd, char *packet) +void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) { ulong stmt_id= uint4korr(packet); Prepared_statement *stmt; @@ -1097,10 +1131,11 @@ void mysql_stmt_execute(THD *thd, char *packet) #ifndef EMBEDDED_LIBRARY if (stmt->param_count) { + uchar *packet_end= (uchar *) packet + packet_length - 1; packet+= 4; uchar *null_array= (uchar *) packet; - if (setup_conversion_functions(stmt, (uchar **) &packet) || - stmt->set_params(stmt, null_array, (uchar *) packet)) + if (setup_conversion_functions(stmt, (uchar **) &packet, packet_end) || + stmt->set_params(stmt, null_array, (uchar *) packet, packet_end)) goto set_params_data_err; } #else @@ -1159,6 +1194,7 @@ set_params_data_err: void mysql_stmt_reset(THD *thd, char *packet) { + /* There is always space for 4 bytes in buffer */ ulong stmt_id= uint4korr(packet); Prepared_statement *stmt; @@ -1189,6 +1225,7 @@ void mysql_stmt_reset(THD *thd, char *packet) void mysql_stmt_free(THD *thd, char *packet) { + /* There is always space for 4 bytes in packet buffer */ ulong stmt_id= uint4korr(packet); Prepared_statement *stmt; From 2e0d5eace576b2470c4bdc2649227a47994c4129 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 15 Mar 2004 22:39:36 +0300 Subject: [PATCH 6/9] Fix of compilation failure of latest 4.1 tree: new constants in mysqld.cc exceed unsigned long limit. include/config-win.h: implementation of ULL macro for Windows include/my_global.h: ULL macro defined: we need this macro because ULL qualifier is not defined on systems withoug unsigned long long sql/item_func.cc: new ULL macro caused conflict with class for user level lock ULL. ULL renamed to User_level_lock sql/item_func.h: ULL -> User_level_lock sql/mysql_priv.h: merge error fixed: LL defined in my_global.h sql/mysqld.cc: ULL macro used for long long constants to fix compilation failure on gcc 3.* sql/sql_class.h: ULL renamed to User_level_lock --- include/config-win.h | 1 + include/my_global.h | 8 ++++++++ sql/item_func.cc | 43 +++++++++++++++++++++++-------------------- sql/item_func.h | 4 ++-- sql/mysql_priv.h | 8 -------- sql/mysqld.cc | 8 ++++---- sql/sql_class.h | 2 +- 7 files changed, 39 insertions(+), 35 deletions(-) diff --git a/include/config-win.h b/include/config-win.h index abc1aa7a6ee..518f445861a 100644 --- a/include/config-win.h +++ b/include/config-win.h @@ -95,6 +95,7 @@ functions */ #define LONGLONG_MAX ((__int64) 0x7FFFFFFFFFFFFFFF) #define ULONGLONG_MAX ((unsigned __int64) 0xFFFFFFFFFFFFFFFF) #define LL(A) ((__int64) A) +#define ULL(A) ((unsigned __int64) A) /* Type information */ diff --git a/include/my_global.h b/include/my_global.h index 2a82173979d..4a56741ddbc 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -842,6 +842,14 @@ typedef char bool; /* Ordinary boolean values 0 1 */ #endif #endif +#ifndef ULL +#ifdef HAVE_LONG_LONG +#define ULL(A) A ## ULL +#else +#define ULL(A) A ## UL +#endif +#endif + /* Defines to make it possible to prioritize register assignments. No longer that important with modern compilers. diff --git a/sql/item_func.cc b/sql/item_func.cc index 0327204dbfd..99a429e48fb 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -1728,7 +1728,7 @@ bool udf_handler::get_arguments() { return 0; } pthread_mutex_t LOCK_user_locks; static HASH hash_user_locks; -class ULL +class User_level_lock { char *key; uint key_length; @@ -1740,7 +1740,7 @@ public: pthread_t thread; ulong thread_id; - ULL(const char *key_arg,uint length, ulong id) + User_level_lock(const char *key_arg,uint length, ulong id) :key_length(length),count(1),locked(1), thread_id(id) { key=(char*) my_memdup((byte*) key_arg,length,MYF(0)); @@ -1754,7 +1754,7 @@ public: } } } - ~ULL() + ~User_level_lock() { if (key) { @@ -1764,11 +1764,12 @@ public: pthread_cond_destroy(&cond); } inline bool initialized() { return key != 0; } - friend void item_user_lock_release(ULL *ull); - friend char *ull_get_key(const ULL *ull,uint *length,my_bool not_used); + friend void item_user_lock_release(User_level_lock *ull); + friend char *ull_get_key(const User_level_lock *ull, uint *length, + my_bool not_used); }; -char *ull_get_key(const ULL *ull,uint *length, +char *ull_get_key(const User_level_lock *ull, uint *length, my_bool not_used __attribute__((unused))) { *length=(uint) ull->key_length; @@ -1796,7 +1797,7 @@ void item_user_lock_free(void) } } -void item_user_lock_release(ULL *ull) +void item_user_lock_release(User_level_lock *ull) { ull->locked=0; if (mysql_bin_log.is_open()) @@ -1852,7 +1853,7 @@ longlong Item_master_pos_wait::val_int() void debug_sync_point(const char* lock_name, uint lock_timeout) { THD* thd=current_thd; - ULL* ull; + User_level_lock* ull; struct timespec abstime; int lock_name_len,error=0; lock_name_len=strlen(lock_name); @@ -1870,7 +1871,7 @@ void debug_sync_point(const char* lock_name, uint lock_timeout) this case, we will not be waiting, but rather, just waste CPU and memory on the whole deal */ - if (!(ull= ((ULL*) hash_search(&hash_user_locks,lock_name, + if (!(ull= ((User_level_lock*) hash_search(&hash_user_locks, lock_name, lock_name_len)))) { pthread_mutex_unlock(&LOCK_user_locks); @@ -1931,7 +1932,7 @@ longlong Item_func_get_lock::val_int() longlong timeout=args[1]->val_int(); struct timespec abstime; THD *thd=current_thd; - ULL *ull; + User_level_lock *ull; int error=0; pthread_mutex_lock(&LOCK_user_locks); @@ -1950,10 +1951,11 @@ longlong Item_func_get_lock::val_int() thd->ull=0; } - if (!(ull= ((ULL*) hash_search(&hash_user_locks,(byte*) res->ptr(), - res->length())))) + if (!(ull= ((User_level_lock *) hash_search(&hash_user_locks, + (byte*) res->ptr(), + res->length())))) { - ull=new ULL(res->ptr(),res->length(), thd->thread_id); + ull=new User_level_lock(res->ptr(),res->length(), thd->thread_id); if (!ull || !ull->initialized()) { delete ull; @@ -2022,7 +2024,7 @@ longlong Item_func_get_lock::val_int() longlong Item_func_release_lock::val_int() { String *res=args[0]->val_str(&value); - ULL *ull; + User_level_lock *ull; longlong result; if (!res || !res->length()) { @@ -2033,8 +2035,9 @@ longlong Item_func_release_lock::val_int() result=0; pthread_mutex_lock(&LOCK_user_locks); - if (!(ull= ((ULL*) hash_search(&hash_user_locks,(const byte*) res->ptr(), - res->length())))) + if (!(ull= ((User_level_lock*) hash_search(&hash_user_locks, + (const byte*) res->ptr(), + res->length())))) { null_value=1; } @@ -3041,7 +3044,7 @@ longlong Item_func_is_free_lock::val_int() { String *res=args[0]->val_str(&value); THD *thd=current_thd; - ULL *ull; + User_level_lock *ull; null_value=0; if (!res || !res->length()) @@ -3051,7 +3054,7 @@ longlong Item_func_is_free_lock::val_int() } pthread_mutex_lock(&LOCK_user_locks); - ull= (ULL*) hash_search(&hash_user_locks,(byte*) res->ptr(), + ull= (User_level_lock *) hash_search(&hash_user_locks, (byte*) res->ptr(), res->length()); pthread_mutex_unlock(&LOCK_user_locks); if (!ull || !ull->locked) @@ -3063,14 +3066,14 @@ longlong Item_func_is_used_lock::val_int() { String *res=args[0]->val_str(&value); THD *thd=current_thd; - ULL *ull; + User_level_lock *ull; null_value=1; if (!res || !res->length()) return 0; pthread_mutex_lock(&LOCK_user_locks); - ull= (ULL*) hash_search(&hash_user_locks,(byte*) res->ptr(), + ull= (User_level_lock *) hash_search(&hash_user_locks, (byte*) res->ptr(), res->length()); pthread_mutex_unlock(&LOCK_user_locks); if (!ull || !ull->locked) diff --git a/sql/item_func.h b/sql/item_func.h index 3890e7c6de5..8fbe5a124da 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -864,9 +864,9 @@ public: ** User level locks */ -class ULL; +class User_level_lock; void item_user_lock_init(void); -void item_user_lock_release(ULL *ull); +void item_user_lock_release(User_level_lock *ull); void item_user_lock_free(void); class Item_func_get_lock :public Item_int_func diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 8696a09962c..8f86d9990fe 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -64,14 +64,6 @@ char* query_table_status(THD *thd,const char *db,const char *table_name); #define PREV_BITS(type,A) ((type) (((type) 1 << (A)) -1)) #define all_bits_set(A,B) ((A) & (B) != (B)) -#ifndef LL -#ifdef HAVE_LONG_LONG -#define LL(A) A ## LL -#else -#define LL(A) A ## L -#endif -#endif - extern CHARSET_INFO *system_charset_info, *files_charset_info ; extern CHARSET_INFO *national_charset_info, *table_alias_charset; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 9a469e529cf..786390efd6b 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -308,10 +308,10 @@ double log_10[32]; /* 10 potences */ ulonglong log_10_int[20]= { 1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL, - 100000000UL, 1000000000UL, 10000000000UL, 100000000000UL, - 1000000000000UL, 10000000000000UL, 100000000000000UL, - 1000000000000000UL, 10000000000000000UL, 100000000000000000UL, - 1000000000000000000UL, 10000000000000000000UL + ULL(100000000), ULL(1000000000), ULL(10000000000), ULL(100000000000), + ULL(1000000000000), ULL(10000000000000), ULL(100000000000000), + ULL(1000000000000000), ULL(10000000000000000), ULL(100000000000000000), + ULL(1000000000000000000), ULL(10000000000000000000) }; time_t start_time; diff --git a/sql/sql_class.h b/sql/sql_class.h index 567135b1378..33684f93d93 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -657,7 +657,7 @@ public: points to a lock object if the lock is present. See item_func.cc and chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK. */ - ULL *ull; + User_level_lock *ull; #ifndef DBUG_OFF uint dbug_sentry; // watch out for memory corruption #endif From ffad9aeff0fd70204a259c7a87c02c259e4c78e1 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 16 Mar 2004 01:50:35 +0400 Subject: [PATCH 7/9] Yet another fix for spatial parts sql/spatial.cc: should be one step further --- sql/spatial.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/spatial.cc b/sql/spatial.cc index f98799e26d1..ec6bfb307b9 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -25,7 +25,7 @@ Geometry::Class_info *Geometry::ci_collection[Geometry::wkb_end]= }; static Geometry::Class_info **ci_collection_end= - Geometry::ci_collection+Geometry::wkb_end; + Geometry::ci_collection+Geometry::wkb_end + 1; Geometry::Class_info::Class_info(const char *name, int type_id, void(*create_func)(void *)): From 4c0d915652466b8f6c7f1bb5b397f59cb5cea8d3 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 16 Mar 2004 02:04:03 +0400 Subject: [PATCH 8/9] Small fix sql/item_strfunc.cc: that's the right fix --- sql/item_strfunc.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 3c8689e1588..2833e1ca016 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2721,7 +2721,7 @@ String *Item_func_uuid::val_str(String *str) { ulong tmp=sql_rnd_with_mutex(); uchar mac[6]; - unsigned int i; + int i; if (my_gethwaddr(mac)) { /* @@ -2731,7 +2731,7 @@ String *Item_func_uuid::val_str(String *str) randominit() here */ randominit(&uuid_rand, tmp + (ulong)current_thd, tmp + query_id); - for (i=0; i < sizeof(mac); i++) + for (i=0; i < (int)sizeof(mac); i++) mac[i]=(uchar)(my_rnd(&uuid_rand)*255); } s=clock_seq_and_node_str+sizeof(clock_seq_and_node_str)-1; From 321422c86e29199d1d2fb0337640fa819550baa0 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 16 Mar 2004 01:04:04 +0300 Subject: [PATCH 9/9] Intermediate commit of client library (cleanups + fixes of 3 items from flaws list) TODO: * verify that no sequence of API calls produces SIGSEGV. That is, verify that mysql_stmt_init -> mysql_stmt_fetch is OK, or mysql_stmt_prepare -> mysql_stmt_fetch_column is OK and sets meaningful error. * remove alloc_stmt_fields call * revise stmt->state codes and statement states. * there are other items in prepared statements 'to fix' document. Done: - cleanups and comments - revision of prepared statement error codes. - mysql_stmt_prepare is now can always be called (that is, you can reprepare a statement) - new implementation of mysql_stmt_close and fetch cancellation include/errmsg.h: - CR_NOT_ALL_PARAMS_BOUND - this error code wasn't used until now. Apparently it was added in advance, but then interface of mysql_stmt_bind_param changed. Now it's not possible to bind only some parameters - either all or none of parameters are bound. This error code is renamed to CR_PARAMS_NOT_BOUND - CR_FETCH_CANCELLED - error code set on server side when fetch from MYSQL_RES or MYSQL_STMT (in blocking mode) was cancelled because of intercepting call to mysql_stmt_close - CR_NO_DATA - this is proposed error code to return from mysql_stmt_fetch_column if no row was fetched (by any type of fetch). We always can fall back to CR_COMMANDS_OUT_OF_SYNC though. Need reviewer's opinion on this one. include/mysql.h: - added unbuffered_fetch_owner member to MYSQL to point to MYSQL_RES or MYSQL_STMT which is used to fetch result at the moment. This is to be able to set CR_FETCH_CANCELLED error without fantoms. - added unbuffered_fetch_cancelled boolean variable to MYSQL_STMT and MYSQL_RES structures - rename PREP_STMT_STATE -> enum enum_mysql_stmt_state - members of MYSQL_STMT ordered by size. - removed members of MYSQL_STMT: current_row, result_buffered, last_fetched_column, last_fetched_buffer, query - renamed members of MYSQL_STMT: param_buffers -> bind_param_done, res_buffers -> bind_result_done - now mysql_stmt_fetch calls stmt->read_row_func to read row either from buffer or from network. include/sql_common.h: declaration for flush_use_result libmysql/client_settings.h: stmt_close declaration removed libmysql/errmsg.c: Error messages for changed and added error codes. libmysql/libmysql.c: Many changes: - some unused variables removed - cleanups - better error reporting - some function calls commented - alloc_stmt_fields is now called right after execute, to not read mysql->fields of some other statement - new implementation of mysql_stmt_fetch - this is also with cursor fetch in mind (to implement cursor fetch I'll just need to write special read_row function for it, so this change will be local) - implementation of fetch cancellation, including complete rewrite of mysql_stmt_close - now mysql_stmt_free_result doesn't free results of other statements. sql-common/client.c: - implementation of flush_use_result - implementation of fetch cancellation - changed behaviour of mysql_close in regard to mysql_stmt_close - now mysql_close just set stmt->mysql to 0 --- include/errmsg.h | 4 +- include/mysql.h | 52 ++-- include/sql_common.h | 1 + libmysql/client_settings.h | 1 - libmysql/errmsg.c | 16 +- libmysql/libmysql.c | 593 +++++++++++++++++++++++++------------ sql-common/client.c | 98 +++--- 7 files changed, 517 insertions(+), 248 deletions(-) diff --git a/include/errmsg.h b/include/errmsg.h index 8f3ddfa9796..16f220a7ee2 100644 --- a/include/errmsg.h +++ b/include/errmsg.h @@ -67,7 +67,7 @@ extern const char *client_errors[]; /* Error messages */ /* new 4.1 error codes */ #define CR_NULL_POINTER 2028 #define CR_NO_PREPARE_STMT 2029 -#define CR_NOT_ALL_PARAMS_BOUND 2030 +#define CR_PARAMS_NOT_BOUND 2030 #define CR_DATA_TRUNCATED 2031 #define CR_NO_PARAMETERS_EXISTS 2032 #define CR_INVALID_PARAMETER_NO 2033 @@ -87,3 +87,5 @@ extern const char *client_errors[]; /* Error messages */ #define CR_CONN_UNKNOW_PROTOCOL 2046 #define CR_INVALID_CONN_HANDLE 2047 #define CR_SECURE_AUTH 2048 +#define CR_FETCH_CANCELLED 2049 +#define CR_NO_DATA 2050 diff --git a/include/mysql.h b/include/mysql.h index 2b4153bb140..1bcc2a58534 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -256,6 +256,11 @@ typedef struct st_mysql LIST *stmts; /* list of all statements */ const struct st_mysql_methods *methods; void *thd; + /* + Points to boolean flag in MYSQL_RES or MYSQL_STMT. We set this flag + from mysql_stmt_close if close had to cancel result set of this object. + */ + my_bool *unbuffered_fetch_owner; } MYSQL; typedef struct st_mysql_res { @@ -270,6 +275,8 @@ typedef struct st_mysql_res { MYSQL_ROW row; /* If unbuffered read */ MYSQL_ROW current_row; /* buffer to current row */ my_bool eof; /* Used by mysql_fetch_row */ + /* mysql_stmt_close() had to cancel this result */ + my_bool unbuffered_fetch_cancelled; const struct st_mysql_methods *methods; } MYSQL_RES; @@ -479,7 +486,11 @@ my_bool STDCALL mysql_read_query_result(MYSQL *mysql); */ /* statement state */ -enum PREP_STMT_STATE { MY_ST_UNKNOWN, MY_ST_PREPARE, MY_ST_EXECUTE }; +enum enum_mysql_stmt_state +{ + MYSQL_STMT_INIT_DONE= 1, MYSQL_STMT_PREPARE_DONE, MYSQL_STMT_EXECUTE_DONE, + MYSQL_STMT_FETCH_DONE +}; /* client TIME structure to handle TIME, DATE and TIMESTAMP directly in @@ -525,31 +536,34 @@ typedef struct st_mysql_bind /* statement handler */ typedef struct st_mysql_stmt { - MYSQL *mysql; /* connection handle */ - MYSQL_BIND *params; /* input parameters */ - MYSQL_RES *result; /* resultset */ - MYSQL_BIND *bind; /* row binding */ - MYSQL_FIELD *fields; /* prepare meta info */ + MEM_ROOT mem_root; /* root allocations */ LIST list; /* list to keep track of all stmts */ - unsigned char *current_row; /* unbuffered row */ - unsigned char *last_fetched_buffer; /* last fetched column buffer */ - char *query; /* query buffer */ - MEM_ROOT mem_root; /* root allocations */ - my_ulonglong last_fetched_column; /* last fetched column */ + MYSQL *mysql; /* connection handle */ + MYSQL_BIND *params; /* input parameters */ + MYSQL_BIND *bind; /* output parameters */ + MYSQL_FIELD *fields; /* result set metadata */ + MYSQL_RES *result; /* cached result set */ /* copy of mysql->affected_rows after statement execution */ my_ulonglong affected_rows; + /* + mysql_stmt_fetch() calls this function to fetch one row (it's different + for buffered, unbuffered and cursor fetch). + */ + int (*read_row_func)(struct st_mysql_stmt *stmt, + unsigned char **row); unsigned long stmt_id; /* Id for prepared statement */ unsigned int last_errno; /* error code */ - unsigned int param_count; /* parameters count */ - unsigned int field_count; /* fields count */ - enum PREP_STMT_STATE state; /* statement state */ + unsigned int param_count; /* inpute parameters count */ + unsigned int field_count; /* number of columns in result set */ + enum enum_mysql_stmt_state state; /* statement state */ char last_error[MYSQL_ERRMSG_SIZE]; /* error message */ char sqlstate[SQLSTATE_LENGTH+1]; - my_bool long_alloced; /* flag to indicate long alloced */ - my_bool send_types_to_server; /* Types sent to server */ - my_bool param_buffers; /* param bound buffers */ - my_bool res_buffers; /* output bound buffers */ - my_bool result_buffered; /* Results buffered */ + /* Types of input parameters should be sent to server */ + my_bool send_types_to_server; + my_bool bind_param_done; /* input buffers were supplied */ + my_bool bind_result_done; /* output buffers were supplied */ + /* mysql_stmt_close() had to cancel this result */ + my_bool unbuffered_fetch_cancelled; } MYSQL_STMT; diff --git a/include/sql_common.h b/include/sql_common.h index cde53786f83..3f50008a922 100644 --- a/include/sql_common.h +++ b/include/sql_common.h @@ -25,6 +25,7 @@ extern "C" { MYSQL_FIELD *unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, my_bool default_value, uint server_capabilities); void free_rows(MYSQL_DATA *cur); +void flush_use_result(MYSQL *mysql); my_bool mysql_autenticate(MYSQL *mysql, const char *passwd); void free_old_query(MYSQL *mysql); void end_server(MYSQL *mysql); diff --git a/libmysql/client_settings.h b/libmysql/client_settings.h index e4475d76958..9fc8e67df27 100644 --- a/libmysql/client_settings.h +++ b/libmysql/client_settings.h @@ -22,7 +22,6 @@ extern my_string mysql_unix_port; CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION) sig_handler pipe_sig_handler(int sig __attribute__((unused))); -my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_free); void read_user_name(char *name); my_bool send_file_to_server(MYSQL *mysql, const char *filename); diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c index 569267ddb37..e651c13897f 100644 --- a/libmysql/errmsg.c +++ b/libmysql/errmsg.c @@ -54,7 +54,7 @@ const char *client_errors[]= "Malformed packet", "Invalid use of null pointer", "Statement not prepared", - "Not all parameters data supplied", + "Parameters data was not supplied", "Data truncated", "No parameters exists in the statement", "Invalid parameter number", @@ -72,7 +72,9 @@ const char *client_errors[]= "Can't open shared memory. Can't send the request event to server (%lu)", "Wrong or unknown protocol", "Invalid connection handle", - "Connection using old (pre 4.1.1) authentication protocol refused (client option 'secure_auth' enabled)" + "Connection using old (pre 4.1.1) authentication protocol refused (client option 'secure_auth' enabled)", + "Row retrieval was cancelled by mysql_stmt_close() call", + "Attempt to read column without prior row fetch" }; /* Start of code added by Roberto M. Serqueira - martinsc@uol.com.br - 05.24.2001 */ @@ -110,7 +112,7 @@ const char *client_errors[]= "Malformed packet", "Invalid use of null pointer", "Statement not prepared", - "Not all parameters data supplied", + "Parameters data was not supplied", "Data truncated", "No parameters exists in the statement", "Invalid parameter number", @@ -128,7 +130,9 @@ const char *client_errors[]= "Can't open shared memory. Can't send the request event to server (%lu)", "Wrong or unknown protocol", "Invalid connection handle", - "Connection using old (pre 4.1.1) authentication protocol refused (client option 'secure_auth' enabled)" + "Connection using old (pre 4.1.1) authentication protocol refused (client option 'secure_auth' enabled)", + "Row retrieval was cancelled by mysql_stmt_close() call", + "Attempt to read column without prior row fetch" }; #else /* ENGLISH */ @@ -182,7 +186,9 @@ const char *client_errors[]= "Can't open shared memory. Can't send the request event to server (%lu)", "Wrong or unknown protocol", "Invalid connection handle", - "Connection using old (pre 4.1.1) authentication protocol refused (client option 'secure_auth' enabled)" + "Connection using old (pre 4.1.1) authentication protocol refused (client option 'secure_auth' enabled)", + "Row retrieval was cancelled by mysql_stmt_close() call", + "Attempt to read column without prior row fetch" }; #endif diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 0f274021f20..91927e44e49 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -638,6 +638,7 @@ 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) { @@ -866,6 +867,7 @@ STDCALL mysql_set_master(MYSQL* mysql, const char* host, return 0; } + int STDCALL mysql_add_slave(MYSQL* mysql, const char* host, unsigned int port, @@ -987,6 +989,7 @@ mysql_list_tables(MYSQL *mysql, const char *wild) DBUG_RETURN (mysql_store_result(mysql)); } + MYSQL_FIELD *cli_list_fields(MYSQL *mysql) { MYSQL_DATA *query; @@ -1062,6 +1065,7 @@ mysql_list_processes(MYSQL *mysql) DBUG_RETURN(mysql_store_result(mysql)); } + #ifdef USE_OLD_FUNCTIONS int STDCALL mysql_create_db(MYSQL *mysql, const char *db) @@ -1099,6 +1103,7 @@ mysql_refresh(MYSQL *mysql,uint options) DBUG_RETURN(simple_command(mysql,COM_REFRESH,(char*) bits,1,0)); } + int STDCALL mysql_kill(MYSQL *mysql,ulong pid) { @@ -1126,6 +1131,7 @@ mysql_dump_debug_info(MYSQL *mysql) DBUG_RETURN(simple_command(mysql,COM_DEBUG,0,0,0)); } + const char *cli_read_statistics(MYSQL *mysql) { mysql->net.read_pos[mysql->packet_length]=0; /* End of stat string */ @@ -1139,6 +1145,7 @@ const char *cli_read_statistics(MYSQL *mysql) return (char*) mysql->net.read_pos; } + const char * STDCALL mysql_stat(MYSQL *mysql) { @@ -1576,6 +1583,10 @@ static my_bool my_realloc_str(NET *net, ulong length) Prepare related implementations ********************************************************************/ +static int stmt_read_row_unbuffered(MYSQL_STMT *stmt, unsigned char **row); +static int stmt_read_row_buffered(MYSQL_STMT *stmt, unsigned char **row); +static int stmt_read_row_no_data(MYSQL_STMT *stmt, unsigned char **row); + /* Read the prepared statement results .. @@ -1591,13 +1602,12 @@ static my_bool my_realloc_str(NET *net, ulong length) my_bool cli_read_prepare_result(MYSQL *mysql, MYSQL_STMT *stmt) { uchar *pos; - uint field_count; - ulong length, param_count; + uint field_count, param_count; MYSQL_DATA *fields_data; DBUG_ENTER("read_prepare_result"); mysql= mysql->last_used_con; - if ((length= net_safe_read(mysql)) == packet_error) + if (net_safe_read(mysql) == packet_error) DBUG_RETURN(1); pos= (uchar*) mysql->net.read_pos; @@ -1634,7 +1644,7 @@ MYSQL_STMT * STDCALL mysql_prepare(MYSQL *mysql, const char *query, stmt= mysql_stmt_init(mysql); if (stmt && mysql_stmt_prepare(stmt, query, query_length)) { - stmt_close(stmt, 0); + mysql_stmt_close(stmt); DBUG_RETURN(0); } DBUG_RETURN(stmt); @@ -1646,6 +1656,7 @@ MYSQL_STMT * STDCALL mysql_prepare(MYSQL *mysql, const char *query, SYNOPSIS mysql_stmt_init() mysql connection handle + RETURN VALUE statement structure upon success and NULL if out of memory @@ -1664,20 +1675,35 @@ mysql_stmt_init(MYSQL *mysql) DBUG_RETURN(0); } - init_alloc_root(&stmt->mem_root,8192,0); + init_alloc_root(&stmt->mem_root, 2048, 2048); mysql->stmts= list_add(mysql->stmts, &stmt->list); stmt->list.data= stmt; - stmt->state= MY_ST_UNKNOWN; + stmt->state= MYSQL_STMT_INIT_DONE; stmt->mysql= mysql; + stmt->read_row_func= stmt_read_row_no_data; + /* The rest of statement members was bzeroed inside malloc */ DBUG_RETURN(stmt); } /* - Prepare server side statement with query. + Prepare server side statement with query: + SYNOPSIS + mysql_stmt_prepare() + query statement to prepare + length statement length - Also update the total parameter count along with resultset - metadata information by reading from server + DESCRIPTION + - if this is a re-prepare of the statement, first close previous data + structure on the server and free old statement data + - send the query to server and get back number of placeholders, + number of columns in result set (if any), and result set metadata. + At the same time allocate memory for input and output parameters + to have less checks in mysql_stmt_bind_{param, result}. + + RETURN VALUES + 0 success + !0 error */ @@ -1687,17 +1713,44 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) MYSQL *mysql= stmt->mysql; DBUG_ENTER("mysql_stmt_prepare"); - DBUG_ASSERT(mysql != 0); - - /* In case we reprepare this handle with another statement */ - free_root(&stmt->mem_root, MYF(0)); - - /* stmt->query is never used yet */ - if (!(stmt->query= strmake_root(&stmt->mem_root, query, length))) + if (!mysql) { - set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate); + /* mysql can be reset in mysql_close called from mysql_reconnect */ + set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate); DBUG_RETURN(1); } + + if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE) + { + /* This is second prepare with another statement */ + char buff[4]; + + mysql_stmt_free_result(stmt); + /* + These members must be reset for API to + function in case of error or misuse. + */ + stmt->bind_param_done= stmt->bind_result_done= FALSE; + stmt->param_count= stmt->field_count= 0; + stmt->last_errno= 0; + stmt->last_error[0]= '\0'; + free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC)); + + int4store(buff, stmt->stmt_id); + /* + If there was a 'use' result from another statement, or from + mysql_use_result it won't be freed in mysql_stmt_free_result and + we should get 'Commands out of sync' here. + */ + if (simple_command(mysql, COM_CLOSE_STMT, buff, 4, 1)) + { + set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, + mysql->net.sqlstate); + DBUG_RETURN(1); + } + stmt->state= MYSQL_STMT_INIT_DONE; + } + if (simple_command(mysql, COM_PREPARE, query, length, 1)) { set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, @@ -1706,8 +1759,18 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) } if ((*mysql->methods->read_prepare_result)(mysql, stmt)) + { + set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, + mysql->net.sqlstate); DBUG_RETURN(1); + } + /* + alloc_root will return valid address even in case param_count + and field_count are zero. Thus we should never rely on stmt->bind + or stmt->params when checking for existence of placeholders or + result set. + */ if (!(stmt->params= (MYSQL_BIND *) alloc_root(&stmt->mem_root, sizeof(MYSQL_BIND)* (stmt->param_count + @@ -1717,26 +1780,22 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) DBUG_RETURN(1); } stmt->bind= stmt->params + stmt->param_count; - stmt->state= MY_ST_PREPARE; - mysql->status= MYSQL_STATUS_READY; + stmt->state= MYSQL_STMT_PREPARE_DONE; DBUG_PRINT("info", ("Parameter count: %ld", stmt->param_count)); DBUG_RETURN(0); } /* Get the execute query meta information for non-select - statements (on demand). + statements. */ -unsigned int alloc_stmt_fields(MYSQL_STMT *stmt) +static unsigned int alloc_stmt_fields(MYSQL_STMT *stmt) { MYSQL_FIELD *fields, *field, *end; MEM_ROOT *alloc= &stmt->mem_root; MYSQL *mysql= stmt->mysql->last_used_con; - if (stmt->state != MY_ST_EXECUTE || !mysql->field_count) - return 0; - stmt->field_count= mysql->field_count; /* @@ -1782,20 +1841,25 @@ mysql_stmt_result_metadata(MYSQL_STMT *stmt) MYSQL_RES *result; DBUG_ENTER("mysql_stmt_result_metadata"); - if (!stmt->field_count || !stmt->fields) - { - if (!alloc_stmt_fields(stmt)) - DBUG_RETURN(0); - } - if (!(result=(MYSQL_RES*) my_malloc(sizeof(*result)+ - sizeof(ulong)*stmt->field_count, - MYF(MY_WME | MY_ZEROFILL)))) - return 0; + /* + stmt->fields is only defined if stmt->field_count is not null; + stmt->field_count is initialized in prepare. + */ + if (!stmt->field_count) + DBUG_RETURN(0); - result->methods= stmt->mysql->methods; - result->eof=1; /* Marker for buffered */ + if (!(result=(MYSQL_RES*) my_malloc(sizeof(*result), + MYF(MY_WME | MY_ZEROFILL)))) + { + set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate); + DBUG_RETURN(0); + } + + result->methods= stmt->mysql->methods; + result->eof= 1; /* Marker for buffered */ result->fields= stmt->fields; result->field_count= stmt->field_count; + /* The rest of members of 'result' was bzeroed inside malloc */ DBUG_RETURN(result); } @@ -1989,11 +2053,9 @@ static void store_param_null(NET *net, MYSQL_BIND *param) client application */ - static my_bool store_param(MYSQL_STMT *stmt, MYSQL_BIND *param) { - MYSQL *mysql= stmt->mysql; - NET *net = &mysql->net; + NET *net= &stmt->mysql->net; DBUG_ENTER("store_param"); DBUG_PRINT("enter",("type: %d, buffer:%lx, length: %lu is_null: %d", param->buffer_type, @@ -2059,6 +2121,12 @@ int cli_stmt_execute(MYSQL_STMT *stmt) uint null_count; my_bool result; + if (!stmt->bind_param_done) + { + set_stmt_error(stmt, CR_PARAMS_NOT_BOUND, unknown_sqlstate); + DBUG_RETURN(1); + } + net_clear(net); /* Sets net->write_pos */ /* Reserve place for null-marker bytes */ null_count= (stmt->param_count+7) /8; @@ -2093,7 +2161,6 @@ int cli_stmt_execute(MYSQL_STMT *stmt) set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate); DBUG_RETURN(1); } - net->write_pos= net->buff; /* Reset for net_write() */ result= execute(stmt, param_data, length); stmt->send_types_to_server=0; my_free(param_data, MYF(MY_WME)); @@ -2102,22 +2169,146 @@ int cli_stmt_execute(MYSQL_STMT *stmt) DBUG_RETURN((int) execute(stmt,0,0)); } +/* + Read one row from buffered result set. Result set is created by prior + call to mysql_stmt_store_result(). + SYNOPSIS + stmt_read_row_buffered() + + RETURN VALUE + 0 - success; *row is set to valid row pointer (row data + is stored in result set buffer) + MYSQL_NO_DATA - end of result set. *row is set to NULL +*/ + +static int stmt_read_row_buffered(MYSQL_STMT *stmt, unsigned char **row) +{ + MYSQL_RES *result= stmt->result; + + if (result && result->data_cursor) + { + *row= (uchar *) result->data_cursor->data; + result->data_cursor= result->data_cursor->next; + return 0; + } + *row= 0; + return MYSQL_NO_DATA; +} + +/* + Read one row from network: unbuffered non-cursor fetch. + If last row was read, or error occured, erase this statement + from record pointing to object unbuffered fetch is performed from. + + SYNOPSIS + stmt_read_row_unbuffered() + stmt statement handle + row pointer to write pointer to row data; + + RETURN VALUE + 0 - success; *row contains valid address of a row; + row data is stored in network buffer + 1 - error; error code is written to + stmt->last_{errno,error}; *row is not changed + MYSQL_NO_DATA - end of file was read from network; + *row is to NULL +*/ + +static int stmt_read_row_unbuffered(MYSQL_STMT *stmt, unsigned char **row) +{ + int rc= 1; + MYSQL *mysql= stmt->mysql; + /* + This function won't be called if stmt->field_count is zero + or execution wasn't done: this is ensured by mysql_stmt_execute. + */ + if (!mysql) + { + set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate); + return 1; + } + if (mysql->status != MYSQL_STATUS_GET_RESULT) + { + set_stmt_error(stmt, stmt->unbuffered_fetch_cancelled ? + CR_FETCH_CANCELLED : CR_COMMANDS_OUT_OF_SYNC, + unknown_sqlstate); + 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); + goto error; + } + if (!*row) + { + mysql->status= MYSQL_STATUS_READY; + rc= MYSQL_NO_DATA; + goto error; + } + return 0; +error: + if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled) + mysql->unbuffered_fetch_owner= 0; + return rc; +} + +/* + Default read row function to not SIGSEGV in client in + case of wrong sequence of API calls. +*/ + +static int +stmt_read_row_no_data(MYSQL_STMT *stmt __attribute__((unused)), + unsigned char **row __attribute__((unused))) +{ + if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE) + { + set_stmt_error(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate); + return 1; + } + return MYSQL_NO_DATA; +} + /* Execute the prepared query */ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) { + MYSQL *mysql= stmt->mysql; DBUG_ENTER("mysql_stmt_execute"); - if ((*stmt->mysql->methods->stmt_execute)(stmt)) + if (!mysql) + { + set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate); DBUG_RETURN(1); + } + + mysql_stmt_free_result(stmt); + /* + No need to check for stmt->state: if the statement wasn't + prepared we'll get 'unknown statemenet handler' error from server. + */ + if (mysql->methods->stmt_execute(stmt)) + DBUG_RETURN(1); + if (!stmt->field_count && mysql->field_count) + { + /* + This is 'SHOW'/'EXPLAIN'-like query. Current implementation of + prepared statements can't send result set metadata for this queries + on prepare stage. Read it now. + */ + alloc_stmt_fields(stmt); + } - stmt->state= MY_ST_EXECUTE; - mysql_free_result(stmt->result); - stmt->result= (MYSQL_RES *)0; - stmt->result_buffered= 0; - stmt->current_row= 0; + stmt->state= MYSQL_STMT_EXECUTE_DONE; + if (stmt->field_count) + { + stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled; + stmt->unbuffered_fetch_cancelled= FALSE; + stmt->read_row_func= stmt_read_row_unbuffered; + } DBUG_RETURN(0); } @@ -2155,6 +2346,16 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind) MYSQL_BIND *param, *end; DBUG_ENTER("mysql_stmt_bind_param"); + if (!stmt->param_count) + { + if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE) + { + set_stmt_error(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate); + DBUG_RETURN(1); + } + DBUG_RETURN(0); + } + /* Allocated on prepare */ memcpy((char*) stmt->params, (char*) bind, sizeof(MYSQL_BIND) * stmt->param_count); @@ -2166,13 +2367,6 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind) param->param_number= count++; param->long_data_used= 0; - /* - 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->buffer_length; - /* If param->is_null is not set, then the value can never be NULL */ if (!param->is_null) param->is_null= &int_is_null_false; @@ -2239,10 +2433,16 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind) param->buffer_type, count); DBUG_RETURN(1); } + /* + 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->buffer_length; } /* We have to send/resendtype information to MySQL */ - stmt->send_types_to_server= 1; - stmt->param_buffers= 1; + stmt->send_types_to_server= TRUE; + stmt->bind_param_done= TRUE; DBUG_RETURN(0); } @@ -2276,6 +2476,16 @@ mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, DBUG_ASSERT(stmt != 0); DBUG_PRINT("enter",("param no : %d, data : %lx, length : %ld", param_number, data, length)); + + /* + We only need to check for stmt->param_count, if it's not null + prepare was done. + */ + if (param_number >= stmt->param_count) + { + set_stmt_error(stmt, CR_INVALID_PARAMETER_NO, unknown_sqlstate); + DBUG_RETURN(1); + } param= stmt->params+param_number; if (param->buffer_type < MYSQL_TYPE_TINY_BLOB || @@ -2846,9 +3056,20 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) DBUG_ENTER("mysql_stmt_bind_result"); DBUG_ASSERT(stmt != 0); - if (!(bind_count= stmt->field_count) && - !(bind_count= alloc_stmt_fields(stmt))) + if (!stmt->field_count) + { + if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE) + { + set_stmt_error(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate); + } DBUG_RETURN(0); + } + bind_count= stmt->field_count; + + /* + We only need to check that stmt->field_count - if it is not null + stmt->bind was initialized in mysql_stmt_prepare + */ memcpy((char*) stmt->bind, (char*) bind, sizeof(MYSQL_BIND)*bind_count); @@ -2929,7 +3150,7 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) DBUG_RETURN(1); } } - stmt->res_buffers= 1; + stmt->bind_result_done= TRUE; DBUG_RETURN(0); } @@ -2943,9 +3164,18 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) MYSQL_BIND *bind, *end; MYSQL_FIELD *field, *field_end; uchar *null_ptr, bit; + /* + Precondition: if stmt->field_count is zero or row is NULL, read_row_* + function must return no data. + */ + DBUG_ASSERT(stmt->field_count); + DBUG_ASSERT(row); - if (!row || !stmt->res_buffers) + if (!stmt->bind_result_done) + { + /* If output parameters were not bound we should just return success */ return 0; + } null_ptr= row; row+= (stmt->field_count+9)/8; /* skip null bits */ @@ -2994,57 +3224,22 @@ int cli_unbuffered_fetch(MYSQL *mysql, char **row) int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt) { - MYSQL *mysql= stmt->mysql; + int rc; uchar *row; DBUG_ENTER("mysql_stmt_fetch"); - stmt->last_fetched_column= 0; /* reset */ - if (stmt->result_buffered) /* buffered */ + if ((rc= (*stmt->read_row_func)(stmt, &row)) || + (rc= stmt_fetch_row(stmt, row))) { - MYSQL_RES *res; - - if (!(res= stmt->result)) - goto no_data; - - if (!res->data_cursor) - { - stmt->current_row= 0; - goto no_data; - } - row= (uchar *)res->data_cursor->data; - res->data_cursor= res->data_cursor->next; + stmt->state= MYSQL_STMT_PREPARE_DONE; /* XXX: this is buggy */ + stmt->read_row_func= stmt_read_row_no_data; } - else /* un-buffered */ + else { - if (mysql->status != MYSQL_STATUS_GET_RESULT) - { - if (!stmt->field_count) - goto no_data; - - set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); - DBUG_RETURN(1); - } - - if ((*mysql->methods->unbuffered_fetch)(mysql, (char**) &row)) - { - set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, - mysql->net.sqlstate); - DBUG_RETURN(1); - } - if (!row) - { - mysql->status= MYSQL_STATUS_READY; - stmt->current_row= 0; - goto no_data; - } + /* This is to know in mysql_stmt_fetch_column that data was fetched */ + stmt->state= MYSQL_STMT_FETCH_DONE; } - - stmt->current_row= row; - DBUG_RETURN(stmt_fetch_row(stmt, row)); - -no_data: - DBUG_PRINT("info", ("end of data")); - DBUG_RETURN(MYSQL_NO_DATA); /* no more data */ + DBUG_RETURN(rc); } @@ -3065,13 +3260,22 @@ no_data: */ int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, - uint column, ulong offset) + uint column, ulong offset) { MYSQL_BIND *param= stmt->bind+column; DBUG_ENTER("mysql_stmt_fetch_column"); - if (!stmt->current_row) - goto no_data; + if ((int) stmt->state < (int) MYSQL_STMT_FETCH_DONE) + { + set_stmt_error(stmt, CR_NO_DATA, unknown_sqlstate); + return 1; + } + if (column >= stmt->field_count) + { + set_stmt_error(stmt, CR_INVALID_PARAMETER_NO, unknown_sqlstate); + DBUG_RETURN(1); + } + if (param->null_field) { @@ -3092,10 +3296,6 @@ int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, fetch_results(bind, field, &row); } DBUG_RETURN(0); - -no_data: - DBUG_PRINT("info", ("end of data")); - DBUG_RETURN(MYSQL_NO_DATA); /* no more data */ } @@ -3111,7 +3311,7 @@ MYSQL_DATA *cli_read_binary_rows(MYSQL_STMT *stmt) MYSQL_DATA *result; MYSQL_ROWS *cur, **prev_ptr; NET *net = &mysql->net; - DBUG_ENTER("read_binary_rows"); + DBUG_ENTER("cli_read_binary_rows"); mysql= mysql->last_used_con; if ((pkt_len= net_safe_read(mysql)) == packet_error) @@ -3181,33 +3381,37 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) if (!stmt->field_count) DBUG_RETURN(0); - if (mysql->status != MYSQL_STATUS_GET_RESULT) + if ((int) stmt->state < (int) MYSQL_STMT_EXECUTE_DONE || + mysql->status != MYSQL_STATUS_GET_RESULT) { set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); DBUG_RETURN(1); } - mysql->status= MYSQL_STATUS_READY; /* server is ready */ - if (!(result= (MYSQL_RES*) my_malloc((uint) (sizeof(MYSQL_RES)+ - sizeof(ulong) * - stmt->field_count), + if (!(result= (MYSQL_RES*) my_malloc(sizeof(MYSQL_RES), MYF(MY_WME | MY_ZEROFILL)))) { set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate); DBUG_RETURN(1); } result->methods= mysql->methods; - stmt->result_buffered= 1; - if (!(result->data= (*stmt->mysql->methods->read_binary_rows)(stmt))) + if ((result->data= (*mysql->methods->read_binary_rows)(stmt))) + { + result->row_count= result->data->rows; + result->data_cursor= result->data->data; + } + else if (stmt->last_errno) { my_free((gptr) result,MYF(0)); - DBUG_RETURN(0); + DBUG_RETURN(1); } - mysql->affected_rows= result->row_count= result->data->rows; - stmt->affected_rows= result->row_count; - result->data_cursor= result->data->data; + mysql->affected_rows= stmt->affected_rows= result->row_count; result->fields= stmt->fields; result->field_count= stmt->field_count; + /* The rest of MYSQL_RES members were bzeroed inside my_malloc */ stmt->result= result; + stmt->read_row_func= stmt_read_row_buffered; + mysql->unbuffered_fetch_owner= 0; /* set in stmt_execute */ + mysql->status= MYSQL_STATUS_READY; /* server is ready */ DBUG_RETURN(0); /* Data buffered, must be fetched with mysql_stmt_fetch() */ } @@ -3292,6 +3496,40 @@ my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt) DBUG_RETURN(0); } +my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt) +{ + DBUG_ENTER("mysql_stmt_free_result"); + + DBUG_ASSERT(stmt != 0); + + if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE) + { + MYSQL *mysql= stmt->mysql; + + if (stmt->result) + { + /* Result buffered */ + mysql_free_result(stmt->result); + stmt->result= 0; + } + else if (mysql && stmt->field_count + && (int) stmt->state > (int) MYSQL_STMT_PREPARE_DONE) + { + if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled) + mysql->unbuffered_fetch_owner= 0; + if (mysql->status != MYSQL_STATUS_READY) + { + /* There is a result set and it belongs to this statement */ + flush_use_result(mysql); + mysql->status= MYSQL_STATUS_READY; + } + } + stmt->state= MYSQL_STMT_PREPARE_DONE; + stmt->read_row_func= stmt_read_row_no_data; + } + DBUG_RETURN(0); +} + /******************************************************************** statement error handling and close *********************************************************************/ @@ -3300,82 +3538,55 @@ my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt) Close the statement handle by freeing all alloced resources SYNOPSIS - mysql_stmt_free_result() + mysql_stmt_close() stmt Statement handle - skip_list Flag to indicate delete from list or not + RETURN VALUES 0 ok 1 error */ -my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt) -{ - MYSQL *mysql; - DBUG_ENTER("mysql_stmt_free_result"); - - DBUG_ASSERT(stmt != 0); - - mysql= stmt->mysql; - if (mysql->status != MYSQL_STATUS_READY) - { - /* Clear the current execution status */ - DBUG_PRINT("warning",("Not all packets read, clearing them")); - for (;;) - { - ulong pkt_len; - if ((pkt_len= net_safe_read(mysql)) == packet_error) - break; - if (pkt_len <= 8 && mysql->net.read_pos[0] == 254) - break; - } - mysql->status= MYSQL_STATUS_READY; - } - mysql_free_result(stmt->result); - stmt->result= 0; - stmt->result_buffered= 0; - stmt->current_row= 0; - DBUG_RETURN(0); -} - - -my_bool stmt_close(MYSQL_STMT *stmt, my_bool skip_free) -{ - MYSQL *mysql; - DBUG_ENTER("stmt_close"); - - DBUG_ASSERT(stmt != 0); - - if (!(mysql= stmt->mysql)) - { - if (!skip_free) - my_free((gptr) stmt, MYF(MY_WME)); - DBUG_RETURN(0); - } - mysql_stmt_free_result(stmt); - if (stmt->state == MY_ST_PREPARE || stmt->state == MY_ST_EXECUTE) - { - char buff[4]; - int4store(buff, stmt->stmt_id); - if (skip_free || simple_command(mysql, COM_CLOSE_STMT, buff, 4, 1)) - { - set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, - mysql->net.sqlstate); - stmt->mysql= NULL; /* connection isn't valid anymore */ - DBUG_RETURN(1); - } - } - stmt->field_count= 0; - free_root(&stmt->mem_root, MYF(0)); - mysql->stmts= list_delete(mysql->stmts, &stmt->list); - mysql->status= MYSQL_STATUS_READY; - my_free((gptr) stmt, MYF(MY_WME)); - DBUG_RETURN(0); -} - - my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) { - return stmt_close(stmt, 0); + MYSQL *mysql= stmt->mysql; + int rc= 0; + DBUG_ENTER("mysql_stmt_close"); + + mysql_free_result(stmt->result); /* if result is buffered */ + free_root(&stmt->mem_root, MYF(0)); + + if (mysql) + { + mysql->stmts= list_delete(mysql->stmts, &stmt->list); + if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE) + { + char buff[4]; + + if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled) + mysql->unbuffered_fetch_owner= 0; + if (mysql->status != MYSQL_STATUS_READY) + { + /* + Flush result set of the connection. If it does not belong + to this statement, set a warning. + */ + flush_use_result(mysql); + if (mysql->unbuffered_fetch_owner) + *mysql->unbuffered_fetch_owner= TRUE; + mysql->status= MYSQL_STATUS_READY; + } + int4store(buff, stmt->stmt_id); + if ((rc= simple_command(mysql, COM_CLOSE_STMT, buff, 4, 1))) + { + set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, + mysql->net.sqlstate); + } + } + } + + my_free((gptr) stmt, MYF(MY_WME)); + + DBUG_RETURN(test(rc)); } /* @@ -3388,6 +3599,10 @@ my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt) MYSQL *mysql; DBUG_ENTER("mysql_stmt_reset"); DBUG_ASSERT(stmt != 0); + + /* If statement hasnt been prepared there is nothing to reset */ + if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE) + DBUG_RETURN(0); mysql= stmt->mysql->last_used_con; int4store(buff, stmt->stmt_id); /* Send stmt id to server */ diff --git a/sql-common/client.c b/sql-common/client.c index 5d2df4a0ddf..6d0da338543 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -712,6 +712,34 @@ void set_mysql_error(MYSQL *mysql, int errcode, const char *sqlstate) net->last_errno= errcode; strmov(net->last_error, ER(errcode)); strmov(net->sqlstate, sqlstate); + + DBUG_VOID_RETURN; +} + +/* + Flush result set sent from server +*/ + +void flush_use_result(MYSQL *mysql) +{ + /* Clear the current execution status */ + DBUG_PRINT("warning",("Not all packets read, clearing them")); + for (;;) + { + ulong pkt_len; + if ((pkt_len=net_safe_read(mysql)) == packet_error) + break; + if (pkt_len <= 8 && mysql->net.read_pos[0] == 254) + { + if (protocol_41(mysql)) + { + char *pos= (char*) mysql->net.read_pos; + mysql->warning_count=uint2korr(pos); pos+=2; + mysql->server_status=uint2korr(pos); pos+=2; + } + break; /* End of data */ + } + } } @@ -752,26 +780,16 @@ mysql_free_result(MYSQL_RES *result) DBUG_PRINT("enter",("mysql_res: %lx",result)); if (result) { - if (result->handle && result->handle->status == MYSQL_STATUS_USE_RESULT) + MYSQL *mysql= result->handle; + if (mysql) { - DBUG_PRINT("warning",("Not all rows in set where read; Ignoring rows")); - for (;;) + if (mysql->unbuffered_fetch_owner == &result->unbuffered_fetch_cancelled) + mysql->unbuffered_fetch_owner= 0; + if (mysql->status == MYSQL_STATUS_USE_RESULT) { - ulong pkt_len; - if ((pkt_len=net_safe_read(result->handle)) == packet_error) - break; - if (pkt_len <= 8 && result->handle->net.read_pos[0] == 254) - { - if (protocol_41(result->handle)) - { - char *pos= (char*) result->handle->net.read_pos; - result->handle->warning_count=uint2korr(pos); pos+=2; - result->handle->server_status=uint2korr(pos); pos+=2; - } - break; /* End of data */ - } + flush_use_result(mysql); + mysql->status=MYSQL_STATUS_READY; } - result->handle->status=MYSQL_STATUS_READY; } free_rows(result->data); if (result->fields) @@ -2177,12 +2195,13 @@ void STDCALL mysql_close(MYSQL *mysql) #ifdef MYSQL_CLIENT if (mysql->stmts) { - /* Free any open prepared statements */ - LIST *element, *next_element; - for (element= mysql->stmts; element; element= next_element) + /* Reset connection handle in all prepared statements. */ + LIST *element; + for (element= mysql->stmts; element; element= element->next) { - next_element= element->next; - stmt_close((MYSQL_STMT *)element->data, 1); + MYSQL_STMT *stmt= (MYSQL_STMT *) element->data; + stmt->mysql= 0; + /* No need to call list_delete for statement here */ } mysql->stmts= 0; } @@ -2372,9 +2391,10 @@ MYSQL_RES * STDCALL mysql_store_result(MYSQL *mysql) result->fields= mysql->fields; result->field_alloc= mysql->field_alloc; result->field_count= mysql->field_count; - result->current_field=0; - result->current_row=0; /* Must do a fetch first */ + /* The rest of result members is bzeroed in malloc */ mysql->fields=0; /* fields is now in result */ + /* just in case this was mistakenly called after mysql_stmt_execute() */ + mysql->unbuffered_fetch_owner= 0; DBUG_RETURN(result); /* Data fetched */ } @@ -2423,6 +2443,7 @@ static MYSQL_RES * cli_use_result(MYSQL *mysql) result->current_row= 0; mysql->fields=0; /* fields is now in result */ mysql->status=MYSQL_STATUS_USE_RESULT; + mysql->unbuffered_fetch_owner= &result->unbuffered_fetch_cancelled; DBUG_RETURN(result); /* Data is read to be fetched */ } @@ -2439,19 +2460,30 @@ mysql_fetch_row(MYSQL_RES *res) { /* Unbufferred fetch */ if (!res->eof) { - if (!(read_one_row(res->handle,res->field_count,res->row, res->lengths))) + MYSQL *mysql= res->handle; + if (mysql->status != MYSQL_STATUS_USE_RESULT) + { + set_mysql_error(mysql, + res->unbuffered_fetch_cancelled ? + CR_FETCH_CANCELLED : CR_COMMANDS_OUT_OF_SYNC, + unknown_sqlstate); + } + else if (!(read_one_row(mysql, res->field_count, res->row, res->lengths))) { res->row_count++; DBUG_RETURN(res->current_row=res->row); } - else - { - DBUG_PRINT("info",("end of data")); - res->eof=1; - res->handle->status=MYSQL_STATUS_READY; - /* Don't clear handle in mysql_free_results */ - res->handle=0; - } + DBUG_PRINT("info",("end of data")); + res->eof=1; + mysql->status=MYSQL_STATUS_READY; + /* + Reset only if owner points to us: there is a chance that somebody + started new query after mysql_stmt_close(): + */ + if (mysql->unbuffered_fetch_owner == &res->unbuffered_fetch_cancelled) + mysql->unbuffered_fetch_owner= 0; + /* Don't clear handle in mysql_free_result */ + res->handle=0; } DBUG_RETURN((MYSQL_ROW) NULL); }