mariadb/sql/sql_time.h

191 lines
8.2 KiB
C
Raw Normal View History

/* Copyright (c) 2006, 2010, Oracle and/or its affiliates.
2020-07-31 12:51:28 +02:00
Copyright (c) 2011, 2020, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
#ifndef SQL_TIME_INCLUDED
#define SQL_TIME_INCLUDED
#include "sql_basic_types.h"
#include "my_time.h"
#include "mysql_time.h" /* timestamp_type */
#include "sql_error.h" /* Sql_condition */
#include "structs.h" /* INTERVAL */
typedef enum enum_mysql_timestamp_type timestamp_type;
typedef struct st_date_time_format DATE_TIME_FORMAT;
typedef struct st_known_date_time_format KNOWN_DATE_TIME_FORMAT;
/* Flags for calc_week() function. */
#define WEEK_MONDAY_FIRST 1
#define WEEK_YEAR 2
#define WEEK_FIRST_WEEKDAY 4
ulong convert_period_to_month(ulong period);
ulong convert_month_to_period(ulong month);
void set_current_date(THD *thd, MYSQL_TIME *to);
2014-04-18 11:41:15 +02:00
bool time_to_datetime(MYSQL_TIME *ltime);
2012-08-31 14:15:52 +02:00
bool get_date_from_daynr(long daynr,uint *year, uint *month, uint *day);
my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code);
bool str_to_datetime_with_warn(THD *thd,
CHARSET_INFO *cs, const char *str, size_t length,
MYSQL_TIME *l_time,
date_mode_t flags);
bool double_to_datetime_with_warn(THD *thd, double value, MYSQL_TIME *ltime,
date_mode_t fuzzydate,
const TABLE_SHARE *s, const char *name);
bool decimal_to_datetime_with_warn(THD *thd,
const my_decimal *value, MYSQL_TIME *ltime,
date_mode_t fuzzydate,
const TABLE_SHARE *s, const char *name);
MDEV-17216 Assertion `!dt->fraction_remainder(decimals())' failed in Field_temporal_with_date::store_TIME_with_warning The problem happened because {{Field_xxx::store(longlong nr, bool unsigned_val)}} erroneously passed {{unsigned_flag}} to the {{usec}} parameter of this constructor: {code:cpp} Datetime(int *warn, longlong sec, ulong usec, date_conv_mode_t flags) {code} 1. Changing Time and Datetime constructors to accept data as Sec6 rather than as longlong/double/my_decimal, so it's not possible to do such mistakes in the future. Additional good effect of these changes: - This reduced some amount of similar code (minus ~35 lines). - The code now does not rely on the fact that "unsigned_flag" is not important inside Datetime(). The constructor always gets all three parts: sign, integer part, fractional part. The simple the better. 2. Fixing Field_xxx::store() to use the new Datetime constructor format. This change actually fixes the problem. 3. Adding "explicit" keyword to all Sec6 constructors, to avoid automatic hidden conversion from double/my_decimal to Sec6, as well as from longlong/ulonglong through double to Sec6. 4. Change#1 caused (as a dependency) changes in a few places with code like this: bool neg= nr < 0 && !unsigned_val; ulonglong value= m_neg ? (ulonglong) -nr : (ulonglong) nr; These fragments relied on a non-standard behavior with the operator "minus" applied to the lowest possible negative signed long long value. This can lead to different results depending on the platform and compilation flags. We have fixed such bugs a few times already. So instead of modifying the old wrong code to a new wrong code, replacing all such fragments to use Longlong_hybrid, which correctly handles this special case with -LONGLONG_MIN in its method abs(). This also reduced the amount of similar code (1 or 2 new lines instead 3 old lines in all 6 such fragments). 5. Removing ErrConvInteger(longlong nr, bool unsigned_flag= false) and adding ErrConvInteger(Longlong_hybrid) instead, to encourage use of safe Longlong_hybrid instead of unsafe pairs nr+neg. 6. Removing unused ErrConvInteger from Item_cache_temporal::get_date()
2018-10-09 10:02:35 +02:00
bool int_to_datetime_with_warn(THD *thd, const Longlong_hybrid &nr,
MYSQL_TIME *ltime,
date_mode_t fuzzydate,
const TABLE_SHARE *s, const char *name);
bool time_to_datetime(THD *thd, const MYSQL_TIME *tm, MYSQL_TIME *dt);
bool time_to_datetime_with_warn(THD *thd,
const MYSQL_TIME *tm, MYSQL_TIME *dt,
date_conv_mode_t fuzzydate);
inline void datetime_to_date(MYSQL_TIME *ltime)
{
DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE ||
ltime->time_type == MYSQL_TIMESTAMP_DATETIME);
DBUG_ASSERT(ltime->neg == 0);
2017-09-23 13:48:47 +02:00
ltime->second_part= ltime->hour= ltime->minute= ltime->second= 0;
ltime->time_type= MYSQL_TIMESTAMP_DATE;
}
inline void date_to_datetime(MYSQL_TIME *ltime)
{
DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE ||
ltime->time_type == MYSQL_TIMESTAMP_DATETIME);
DBUG_ASSERT(ltime->neg == 0);
ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
}
void make_truncated_value_warning(THD *thd,
Sql_condition::enum_warning_level level,
const ErrConv *str_val,
timestamp_type time_type,
const char *db_name, const char *table_name,
const char *field_name);
extern DATE_TIME_FORMAT *date_time_format_make(timestamp_type format_type,
const char *format_str,
uint format_length);
extern DATE_TIME_FORMAT *date_time_format_copy(THD *thd,
DATE_TIME_FORMAT *format);
const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
timestamp_type type);
bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec);
/* MYSQL_TIME operations */
bool date_add_interval(THD *thd, MYSQL_TIME *ltime, interval_type int_type,
const INTERVAL &interval, bool push_warn= true);
bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
int l_sign, ulonglong *seconds_out, ulong *microseconds_out);
2018-02-19 15:53:31 +01:00
int append_interval(String *str, interval_type int_type,
const INTERVAL &interval);
/**
Calculate time difference between two MYSQL_TIME values and
store the result as an out MYSQL_TIME value in MYSQL_TIMESTAMP_TIME format.
The result can be outside of the supported TIME range.
For example, calc_time_diff('2002-01-01 00:00:00', '2001-01-01 00:00:00')
returns '8760:00:00'. So the caller might want to do check_time_range() or
adjust_time_range_with_warn() on the result of a calc_time_diff() call.
@param l_time1 - the minuend (TIME/DATE/DATETIME value)
@param l_time2 - the subtrahend TIME/DATE/DATETIME value
@param l_sign - +1 if absolute values are to be subtracted,
or -1 if absolute values are to be added.
@param[out] l_time3 - the result
@param fuzzydate - flags
@return true - if TIME_NO_ZERO_DATE was passed in flags and
the result appeared to be '00:00:00.000000'.
This is important when calc_time_diff() is called
when calculating DATE_ADD(TIMEDIFF(...),...)
@return false - otherwise
*/
bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
int lsign, MYSQL_TIME *l_time3, date_mode_t fuzzydate);
int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b);
void localtime_to_TIME(MYSQL_TIME *to, struct tm *from);
void calc_time_from_sec(MYSQL_TIME *to, ulong seconds, ulong microseconds);
uint calc_week(const MYSQL_TIME *l_time, uint week_behaviour, uint *year);
int calc_weekday(long daynr,bool sunday_first_day_of_week);
bool parse_date_time_format(timestamp_type format_type,
const char *format, uint format_length,
DATE_TIME_FORMAT *date_time_format);
/* convenience wrapper */
inline bool parse_date_time_format(timestamp_type format_type,
DATE_TIME_FORMAT *date_time_format)
{
return parse_date_time_format(format_type,
date_time_format->format.str,
(uint) date_time_format->format.length,
date_time_format);
}
extern DATE_TIME_FORMAT global_date_format;
extern DATE_TIME_FORMAT global_datetime_format;
extern DATE_TIME_FORMAT global_time_format;
extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
Changing field::field_name and Item::name to LEX_CSTRING Benefits of this patch: - Removed a lot of calls to strlen(), especially for field_string - Strings generated by parser are now const strings, less chance of accidently changing a string - Removed a lot of calls with LEX_STRING as parameter (changed to pointer) - More uniform code - Item::name_length was not kept up to date. Now fixed - Several bugs found and fixed (Access to null pointers, access of freed memory, wrong arguments to printf like functions) - Removed a lot of casts from (const char*) to (char*) Changes: - This caused some ABI changes - lex_string_set now uses LEX_CSTRING - Some fucntions are now taking const char* instead of char* - Create_field::change and after changed to LEX_CSTRING - handler::connect_string, comment and engine_name() changed to LEX_CSTRING - Checked printf() related calls to find bugs. Found and fixed several errors in old code. - A lot of changes from LEX_STRING to LEX_CSTRING, especially related to parsing and events. - Some changes from LEX_STRING and LEX_STRING & to LEX_CSTRING* - Some changes for char* to const char* - Added printf argument checking for my_snprintf() - Introduced null_clex_str, star_clex_string, temp_lex_str to simplify code - Added item_empty_name and item_used_name to be able to distingush between items that was given an empty name and items that was not given a name This is used in sql_yacc.yy to know when to give an item a name. - select table_name."*' is not anymore same as table_name.* - removed not used function Item::rename() - Added comparision of item->name_length before some calls to my_strcasecmp() to speed up comparison - Moved Item_sp_variable::make_field() from item.h to item.cc - Some minimal code changes to avoid copying to const char * - Fixed wrong error message in wsrep_mysql_parse() - Fixed wrong code in find_field_in_natural_join() where real_item() was set when it shouldn't - ER_ERROR_ON_RENAME was used with extra arguments. - Removed some (wrong) ER_OUTOFMEMORY, as alloc_root will already give the error. TODO: - Check possible unsafe casts in plugin/auth_examples/qa_auth_interface.c - Change code to not modify LEX_CSTRING for database name (as part of lower_case_table_names)
2017-04-23 18:39:57 +02:00
extern LEX_CSTRING interval_type_to_name[];
static inline bool
non_zero_hhmmssuu(const MYSQL_TIME *ltime)
{
return ltime->hour || ltime->minute || ltime->second || ltime->second_part;
}
static inline bool
non_zero_YYMMDD(const MYSQL_TIME *ltime)
{
return ltime->year || ltime->month || ltime->day;
}
static inline bool
non_zero_date(const MYSQL_TIME *ltime)
{
return non_zero_YYMMDD(ltime) ||
(ltime->time_type == MYSQL_TIMESTAMP_DATETIME &&
non_zero_hhmmssuu(ltime));
}
static inline bool
check_date(const MYSQL_TIME *ltime, date_conv_mode_t flags, int *was_cut)
{
return check_date(ltime, non_zero_date(ltime),
ulonglong(flags & TIME_MODE_FOR_XXX_TO_DATE), was_cut);
}
bool check_date_with_warn(THD *thd, const MYSQL_TIME *ltime,
date_conv_mode_t fuzzy_date, timestamp_type ts_type);
static inline bool
check_date_with_warn(THD *thd, const MYSQL_TIME *ltime,
date_mode_t fuzzydate, timestamp_type ts_type)
{
return check_date_with_warn(thd, ltime, date_conv_mode_t(fuzzydate), ts_type);
}
bool adjust_time_range_with_warn(THD *thd, MYSQL_TIME *ltime, uint dec);
MDEV-15310 Range optimizer does not work well for "WHERE temporal_column NOT IN (const_list)" There were two problems related to the bug report: 1. Item_datetime::get_date() was not implemented. So execution went through val_int() followed by int-to-datetime or int-to-time conversion. This was the reason why the optimizer did not work well on data with fractional seconds. 2. Item_datetime::set() did not have a TIME specific code to mix months and days to hours after unpack_time(). This is why the optimizer did not work well with negative TIME values, as well as huge time values. Changes: 1. Overriding Item_datetime::get_date(), to return ltime. This fixes the problem N1. 2. Cleanup: Moving pack_time() and unpack_time() from sql-common/my_time.c and include/my_time.h to sql/sql_time.cc and sql/sql_time.h, as they are not needed on the client side. 3. Adding a new "enum_mysql_timestamp_type ts_type" parameter to unpack_time() and moving the TIME specific code to mix months and days with hours inside unpack_time(). Adding a new "ts_type" parameter to Item_datetime::set(), to pass it from the caller down to unpack_time(). So now the TIME specific code is automatically called from Item_datetime::set(). This fixes the problem N2. This change also helped to get rid of duplicate TIME specific code from other three places, where mixing month/days to hours was done immediately after unpack_time(). Moving the DATE specific code to zero hhmmssff from Item_func_min_max::get_date_native to inside unpack_time(), for symmetry. 4. Removing the virtual method in_vector::result_type(), adding in_vector::type_handler() instead. This helps to get result_type(), field_type(), mysql_timestamp_type() of an in_vector easier. Passing type_handler()->mysql_timestamp_type() as a new parameter to Item_datetime::set() inside in_temporal::value_to_item(). 5. Cleaup: Removing separate implementations of in_datetime::get_value() and in_time::get_value(). Adding a single implementation in_temporal::get_value() instead. Passing type_handler()->field_type() to get_value_internal().
2018-02-14 19:58:34 +01:00
longlong pack_time(const MYSQL_TIME *my_time);
void unpack_time(longlong packed, MYSQL_TIME *my_time,
enum_mysql_timestamp_type ts_type);
#endif /* SQL_TIME_INCLUDED */